Skip to content
Browse files

Add JWT token auth

  • Loading branch information
BrnoPCmaniak committed Jul 26, 2017
1 parent a0764f9 commit a7fe25d9be48945e630abe66a45ce21126f8e910
@@ -0,0 +1,9 @@

.. automodule::

.. automodule::
@@ -4,6 +4,7 @@
.. toctree::

@@ -40,3 +40,29 @@ logging
handlers: ["myCustomHandler"]
level: DEBUG

The configuration section for `JSON Web Tokens <>`_ authentication.

You can turn off JWT token expiration time verification by setting
`JWT_VERIFY_EXPIRATION` to `False`. Without expiration verification, tokens will last forever
meaning a leaked token could be used by an attacker indefinitely (token can be
invalidated by changing user's secret, which will lead to invalidation of all user's tokens).

Default is `True`.

This is number of seconds for which is token valid if `JWT_VERIFY_EXPIRATION` enabled.

Default is `1209600`. (14 days)

.. warning::
Change of this value will affect only newly generated tokens.

Allow setting user's secret via REST API. This is needed for offline token generation.

Default is `False`.
@@ -0,0 +1,154 @@

All calls to REST API endpoints must be authenticated as a particular User.

.. tip::
The password for the "admin" user can be set using two methods.

``pulp-manager reset-admin-password``

The above command prompts the user to enter a new password for "admin" user.

``pulp-manager reset-admin-password --random``

The above command generates a random password for "admin" user and prints it to the screen.

.. tip::
If you are using django rest framework browsable API these browser addons may come handy:

* Chrome `ModHeader <>`_
* Firefox `Modify Headers <>`_

Basic Authentication

Any call to the REST API may use
`HTTP basic authentication <>`_ to provide
a username and password.

JWT Authentication

Alternatively you can use `JSON Web Tokens authentication <>`_.

Token Structure

The structure of the token consists of ``username`` and ``exp``. The tokens are signed by each
user's individual secret by HMAC SHA-256 ("HS256") algorithm.

"username": "admin",
"exp": 1501172472

Obtaining token from server

You can "login" to Pulp by supplying your credentials in POST request and obtain a JWT token
with expiration time set by ``JWT_AUTH.JWT_EXPIRATION_DELTA``

* **method:** ``post``
* **path:** ``api/v3/jwt/``
* response list:
* **code:** ``200`` - credentials were accepted
* **code:** ``400`` - credentials are wrong

**Sample POST request:**
"username": "admin",
"password": "admin_password"

**Sample 200 Response Body:**
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTAyMzgzMDExfQ.3ZpcclxV6hN8ui2HUbwXLJsHl2lhesiCPeDVV2GIbJg"

Using a token

For using JWT tokens you have to set ``Authorization`` header as follows:
Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTAyMzgzMDExfQ.3ZpcclxV6hN8ui2HUbwXLJsHl2lhesiCPeDVV2GIbJg

User secret

To achieve ability to invalidate all user's tokens each user have their own secret key which is
used to sign their tokens. The change of the secret will lead to invalidation of all user's
tokens. The change is independent on password.

You can reset user secret to random by setting ``reset_jwt_secret`` on user API endpoint to True.

If you have enabled ``JWT_AUTH.JWT_ALLOW_SETTING_USER_SECRET`` you can set the user's secret
via user API endpoint.

The secret is stored in User model in field ``jwt_secret``.

Offline token generation

If you have enabled ``JWT_AUTH.JWT_ALLOW_SETTING_USER_SECRET`` users can set their secrets and
therefore are able to generate tokens offline.

If you have pulpcore installed in your environment you can do the following:

.. code-block:: python
from datetime import timedelta
from import generate_token_offline
username = "admin"
jwt_secret = "admin_token_secret"
exp_delta = timedelta(days=7) # This value is optional, default 14 days
token = generate_token_offline(username, jwt_secret, exp_delta)
If not you can implement the above function like this:

.. code-block:: python
import jwt # pip install pyjwt
from datetime import datetime, timedelta
def generate_token_offline(username, jwt_secret, exp_delta=timedelta(days=14)):
Generate JWT token for pulp offline from username and secret.
This function can be used for JWT token generation on client without
the need of connection to pulp server. The only things you need to
know are `username` and `jwt_secret`.
username (str): username
jwt_secret (str): User's JWT token secret
exp_delta (datetime.timedelta, optional):
Token expiration time delta. This will be added to
`datetime.utcnow()` to set the expiration time.
If not set default 14 days is used.
str: JWT token
return jwt.encode(
'username': username,
'exp': datetime.utcnow() + exp_delta
.. warning::
When tokens are generated on client. The client can set **ANY** expiration time they want
no matter what is set in ``JWT_EXPIRATION_DELTA``.
@@ -1,13 +1,7 @@
REST API Reference

All REST API endpoints are protected with basic authentication. The password for the "admin"
user can be set using two methods.
.. toctree::
:maxdepth: 3

``pulp-manager reset-admin-password``

The above command prompts the user to enter a new password for "admin" user.

``pulp-manager reset-admin-password --random``

The above command generates a random password for "admin" user and prints it to the screen.
No changes.
@@ -0,0 +1,63 @@
import jwt
from django.contrib.auth import get_user_model
from django.utils.translation import ugettext as _
from rest_framework import exceptions
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework_jwt.settings import api_settings

class PulpJSONWebTokenAuthentication(JSONWebTokenAuthentication):
Authenticate user by JWT token.

def authenticate(self, request):
Returns a two-tuple of `User` and token if a valid signature has been
supplied using JWT-based authentication. Otherwise returns `None`.
User = get_user_model()
jwt_value = self.get_jwt_value(request)
if jwt_value is None:
return None

payload = api_settings.JWT_DECODE_HANDLER(jwt_value)
except User.DoesNotExist:
msg = _('User not found.')
raise exceptions.AuthenticationFailed(msg)
except jwt.ExpiredSignature:
msg = _('Token has expired.')
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = _('Invalid token.')
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed()

user = self.authenticate_credentials(payload)

return (user, jwt_value)

def authenticate_credentials(self, payload):
Returns an active user that matches the payload's user id and email.
User = get_user_model()
username = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER(payload)

if not username:
msg = _('Invalid token.')
raise exceptions.AuthenticationFailed(msg)

user = User.objects.get_by_natural_key(username)
except User.DoesNotExist:
msg = _('Invalid token. User not found.')
raise exceptions.AuthenticationFailed(msg)

if not user.is_active:
msg = _('Invalid token. User account is disabled.')
raise exceptions.AuthenticationFailed(msg)

return user

0 comments on commit a7fe25d

Please sign in to comment.
You can’t perform that action at this time.