This is an extension of django-oauth-toolkit that solves the lack of support for JWT.
JWT support for:
- Token request.
- Token refresh.
Unsupported:
- Token revoke.
Add to your pip requirements:
git+https://github.com/Humanitec/django-oauth-toolkit-jwt#egg=django-oauth-toolkit-jwt
In order to generate a RS[256, 384, 512] (RSA Signature with SHA-[256, 384, 512]) public and private keys, execute the following:
ssh-keygen -t rsa -b 4096 -f jwtRS256.key # don't add passphrase
openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub
cat jwtRS256.key
cat jwtRS256.key.pub
The bit-length in this JWT-algorithm setting specifies only the bit-length of the used hash-algorithm (SHA), thus the used bit-length of the RSA-keys is not relevant from the key-generation point of view. Recommended is the highest value your platform supports.
These keys are also used if you configure PS[256, 384, 512] as your algorithm.
Creating ECDSA-keys ('ES[256, 384, 512]') is similar to creating RSA-keys, but the bit-length has to be consider also on creation.
ssh-keygen -t ecdsa -b 256 -f jwtECDSA256.key # don't add passphrase
openssl ec -in jwtECDSA256.key -pubout -outform PEM -out jwtECDSA256.key.pub
cat jwtECDSA256.key
cat jwtECDSA256.key.pub
To use this library to issue a token, configure the project as it follows:
Add oauth2_provider and oauth2_provider_jwt to your INSTALLED_APPS:
# settings.py
INSTALLED_APPS = (
...
'oauth2_provider',
'oauth2_provider_jwt',
)
Include the new oauth URLs:
# urls.py
urlpatterns = [
...
url(r'^oauth/', include('oauth2_provider_jwt.urls', namespace='oauth2_provider_jwt')),
]
Add to your MIDDLEWARE the following:
# settings.py
MIDDLEWARE = [
...
'oauth2_provider.middleware.OAuth2TokenMiddleware',
]
And finally add a custom backend authentication:
# settings.py
AUTHENTICATION_BACKENDS = (
...
'oauth2_provider.backends.OAuth2Backend',
)
Now we need to set up a JWT_ISSUER
variable in our config, which will be the
name of the issuer. Take the private key that we generated before
and store it in a JWT_PRIVATE_KEY_<JWT_ISSUER>
variable. Finally, define the
ID attribute you wish to use in JWT_ID_ATTRIBUTE
. While this can be any
attribute on your user model it should be unique.
Also you have to set your JWT-encoding Algorithm if it's different than
RS256
.
For example:
# settings.py
JWT_ISSUER = 'OneIssuer'
JWT_ID_ATTRIBUTE = 'email'
JWT_PRIVATE_KEY_ONEISSUER = """
-----BEGIN RSA PRIVATE KEY-----
MIIBOAIBAAJAbCmbRUsLrsv0/Cq7DVDpUooPS1V2sr0EhTZAZmJhid2o/+ya/28m
...
6D0+csaGDlZ9GbrTpTJUObNENNHqfrHGfqzDxQ==
-----END RSA PRIVATE KEY-----
"""
* Note that storing hardcoded secrets in the settings is a bad practice and can lead to severe security breaches in your code. We recommend using environment variables for this purpose.
** Note that you can configure only one JWT-Encoding Algorithm in
JWT_ENC_ALGORITHM
. But you can set multiple allowed decoding(verifying)
Algorithms in JWT_JWS_ALGORITHMS
as an array of Strings. It is only useful
if the JWT is from a 3rd Party and you don't know which Algorithm is used.
The payload of messages will be by default something like:
{
"iss": "OneIssuer",
"exp": 1234567890,
"iat": 1234567789
}
But there is the possibility to add extra data to it. Just create a
function that will enrich the payload and set the location to it in the
JWT_PAYLOAD_ENRICHER
variable:
# settings.py
JWT_PAYLOAD_ENRICHER = 'myapp.jwt_utils.payload_enricher'
# myproject/myapp/jwt_utils.py
def payload_enricher(request):
return {
'sub': 'mysubject',
...
}
In order to let users authenticate using JWT header and token we need to add the following configuration:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'oauth2_provider_jwt.authentication.JWTAuthentication',
)
}
Also, you will need to add to the settings every public key of all the
possible token issuers, if configured, using a variable JWT_PUBLIC_KEY_<JWT_ISSUER>
:
# settings.py
JWT_PUBLIC_KEY_ONEISSUER = """
-----BEGIN PUBLIC KEY-----
MFswDQYJKoZIhvcNAQEBBQADSgAwRwJAbCmbRUsLrsv0/Cq7DVDpUooPS1V2sr0E
hTZAZmJhid2o/+ya/28muuoQgknEoJz32bKeWuYZrFkRKUrGFnlxHwIDAQAB
-----END PUBLIC KEY-----
"""
By default authentication will be enabled, use JWT_AUTH_DISABLED
setting
variable to disable that feature:
# settings.py
JWT_AUTH_DISABLED = True
Have Docker and docker-compose installed as a first step.
docker-compose build
To run the tests on latest Python-version:
docker-compose run dot_jwt
To run the tests only for Python 3.6:
docker-compose run dot_jwt_36
There are tests configured for all currently supported Python-Versions. Just exchange the suffix of the docker-compose service tag with your major-minor combination.