Skip to content

Commit

Permalink
Add jwks support (#437)
Browse files Browse the repository at this point in the history
* Pull the priv/pub keys into separate module

Needed to generate a second priv/pub pair and didn't want to add
significant extra scrolling.

* Add test for resolving the public key from jwks

* Add jwks verifying_key resolution through JWK_URL

Since pyjwt 2.0.0 they have added support for resolving JWT keys from a
some hosted key registry.

https://pyjwt.readthedocs.io/en/latest/usage.html#retrieve-rsa-signing-keys-from-a-jwks-endpoint

This functionality is required to be able to use Auth0 with simplejwt

* Add JWK_URL setting to the state and settings.py

* Update docs with JWK_URL option

* Add requested changes

* Sort imports

* Fix minor linting errors
  • Loading branch information
damelLP committed Aug 6, 2021
1 parent 2874142 commit f97ef69
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 53 deletions.
10 changes: 10 additions & 0 deletions docs/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Some of Simple JWT's behavior can be customized through settings variables in
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'JWK_URL': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
Expand Down Expand Up @@ -146,6 +147,15 @@ The issuer claim to be included in generated tokens and/or validated in decoded
tokens. When set to ``None``, this field is excluded from tokens and is not
validated.

``JWK_URL``
----------

The JWK_URL is used to dynamically resolve the public keys needed to verify the
signing of tokens. When using Auth0 for example you might set this to
'https://yourdomain.auth0.com/.well-known/jwks.json'. When set to ``None``,
this field is excluded from the token backend and is not used during
validation.

``AUTH_HEADER_TYPES``
---------------------

Expand Down
41 changes: 34 additions & 7 deletions rest_framework_simplejwt/backends.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import jwt
from django.utils.translation import gettext_lazy as _
from jwt import InvalidAlgorithmError, InvalidTokenError, algorithms
import jwt
from jwt import InvalidAlgorithmError, InvalidTokenError, PyJWKClient, algorithms

from .exceptions import TokenBackendError
from .utils import format_lazy
Expand All @@ -16,14 +16,25 @@


class TokenBackend:
def __init__(self, algorithm, signing_key=None, verifying_key=None, audience=None, issuer=None):
def __init__(
self,
algorithm,
signing_key=None,
verifying_key=None,
audience=None,
issuer=None,
jwk_url: str = None,
):
self._validate_algorithm(algorithm)

self.algorithm = algorithm
self.signing_key = signing_key
self.audience = audience
self.issuer = issuer
if algorithm.startswith('HS'):

self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None

if algorithm.startswith("HS"):
self.verifying_key = signing_key
else:
self.verifying_key = verifying_key
Expand All @@ -39,6 +50,15 @@ def _validate_algorithm(self, algorithm):
if algorithm in algorithms.requires_cryptography and not algorithms.has_crypto:
raise TokenBackendError(format_lazy(_("You must have cryptography installed to use {}."), algorithm))

def get_verifying_key(self, token):
if self.algorithm.startswith("HS"):
return self.signing_key

if self.jwks_client:
return self.jwks_client.get_signing_key_from_jwt(token).key

return self.verifying_key

def encode(self, payload):
"""
Returns an encoded token for the given payload dictionary.
Expand Down Expand Up @@ -66,9 +86,16 @@ def decode(self, token, verify=True):
"""
try:
return jwt.decode(
token, self.verifying_key, algorithms=[self.algorithm], verify=verify,
audience=self.audience, issuer=self.issuer,
options={'verify_aud': self.audience is not None, "verify_signature": verify}
token,
self.get_verifying_key(token),
algorithms=[self.algorithm],
verify=verify,
audience=self.audience,
issuer=self.issuer,
options={
'verify_aud': self.audience is not None,
'verify_signature': verify,
},
)
except InvalidAlgorithmError as ex:
raise TokenBackendError(_('Invalid algorithm specified')) from ex
Expand Down
1 change: 1 addition & 0 deletions rest_framework_simplejwt/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'JWK_URL': None,

'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
Expand Down
10 changes: 8 additions & 2 deletions rest_framework_simplejwt/state.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
from .backends import TokenBackend
from .settings import api_settings

token_backend = TokenBackend(api_settings.ALGORITHM, api_settings.SIGNING_KEY,
api_settings.VERIFYING_KEY, api_settings.AUDIENCE, api_settings.ISSUER)
token_backend = TokenBackend(
api_settings.ALGORITHM,
api_settings.SIGNING_KEY,
api_settings.VERIFYING_KEY,
api_settings.AUDIENCE,
api_settings.ISSUER,
api_settings.JWK_URL,
)
112 changes: 112 additions & 0 deletions tests/keys.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
PRIVATE_KEY = """
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA3xMJfyl8TOdrsjDLSIodsArJ/NnQB3ZdfbFC5onxATDfRLLA
CHFo3ye694doBKeSe1NFYbfXPvahl6ODX1a23oQyoRQwlL+M99cLcdCa0gGuJXdb
AaF6Em8E+7uSb3290mI+rZmjqyc7gMtKVWKL4e5i2PerFFBoYkZ7E90KOp2t0ZAD
x2uqF4VTOfYLHG0cPgSw9/ptDStJqJVAOiRRqbv0j0GOFMDYNcN0mDlnpryhQFbQ
iMqn4IJIURZUVBJujFSa45cJPvSmMb6NrzZ1crg5UN6/5Mu2mxQzAi21+vpgGL+E
EuekUd7sRgEAjTHjLKzotLAGo7EGa8sL1vMSFwIDAQABAoIBAQCGGWabF/BONswq
CWUazVR9cG7uXm3NHp2jIr1p40CLC7scDCyeprZ5d+PQS4j/S1Ema++Ih8CQbCjG
BJjD5lf2OhhJdt6hfOkcUBzkJZf8aOAsS6zctRqyHCUtwxuLhFZpM4AkUfjuuZ3u
lcawv5YBkpG/hltE0fV+Jop0bWtpwiKxVsHXVcS0WEPXic0lsOTBCw8m81JXqjir
PCBOnkxgNpHSt69S1xnW3l9fPUWVlduO3EIZ5PZG2BxU081eZW31yIlKsDJhfgm6
R5Vlr5DynqeojAd6SNliCzNXZP28GOpQBrYIeVQWA1yMANvkvd4apz9GmDrjF/Fd
g8Chah+5AoGBAPc/+zyuDZKVHK7MxwLPlchCm5Zb4eou4ycbwEB+P3gDS7MODGu4
qvx7cstTZMuMavNRcJsfoiMMrke9JrqGe4rFGiKRFLVBY2Xwr+95pKNC11EWI1lF
5qDAmreDsj2alVJT5yZ9hsAWTsk2i+xj+/XHWYVkr67pRvOPRAmGMB+NAoGBAOb4
CBHe184Hn6Ie+gSD4OjewyUVmr3JDJ41s8cjb1kBvDJ/wv9Rvo9yz2imMr2F0YGc
ytHraM77v8KOJuJWpvGjEg8I0a/rSttxWQ+J0oYJSIPn+eDpAijNWfOp1aKRNALT
pboCXcnSn+djJFKkNJ2hR7R/vrrM6Jyly1jcVS0zAoGAQpdt4Cr0pt0YS5AFraEh
Mz2VUArRLtSQA3F69yPJjlY85i3LdJvZGYVaJp8AT74y8/OkQ3NipNP+gH3WV3hu
/7IUVukCTcsdrVAE4pe9mucevM0cmie0dOlLAlArCmJ/Axxr7jbyuvuHHrRdPT60
lr6pQr8afh6AKIsWhQYqIeUCgYA+v9IJcN52hhGzjPDl+yJGggbIc3cn6pA4B2UB
TDo7F0KXAajrjrzT4iBBUS3l2Y5SxVNA9tDxsumlJNOhmGMgsOn+FapKPgWHWuMU
WqBMdAc0dvinRwakKS4wCcsVsJdN0UxsHap3Y3a3+XJr1VrKHIALpM0fmP31WQHG
8Y1eiwKBgF6AYXxo0FzZacAommZrAYoxFZT1u4/rE/uvJ2K9HYRxLOVKZe+89ki3
D7AOmrxe/CAc/D+nNrtUIv3RFGfadfSBWzyLw36ekW76xPdJgqJsSz5XJ/FgzDW+
WNC5oOtiPOMCymP75oKOjuZJZ2SPLRmiuO/qvI5uAzBHxRC1BKdt
-----END RSA PRIVATE KEY-----
"""

PUBLIC_KEY = """
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3xMJfyl8TOdrsjDLSIod
sArJ/NnQB3ZdfbFC5onxATDfRLLACHFo3ye694doBKeSe1NFYbfXPvahl6ODX1a2
3oQyoRQwlL+M99cLcdCa0gGuJXdbAaF6Em8E+7uSb3290mI+rZmjqyc7gMtKVWKL
4e5i2PerFFBoYkZ7E90KOp2t0ZADx2uqF4VTOfYLHG0cPgSw9/ptDStJqJVAOiRR
qbv0j0GOFMDYNcN0mDlnpryhQFbQiMqn4IJIURZUVBJujFSa45cJPvSmMb6NrzZ1
crg5UN6/5Mu2mxQzAi21+vpgGL+EEuekUd7sRgEAjTHjLKzotLAGo7EGa8sL1vMS
FwIDAQAB
-----END PUBLIC KEY-----
"""

PRIVATE_KEY_2 = """
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
"""

PUBLIC_KEY_2 = """
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwpwcivLRv5T/2pb9zZpf
h4Fy1vla3vm5N2WtkhjB0DX1HmOhSN9O1Q3byW9VLQtuNAdZ8+hc2jU5DIVApFF+
b1Uo1C76qZWOLLwK/yvweACTdT4MISeYHaUd/B7YUNtUQEZF0xxjMM8jRXEuI902
pLEUBx9cd2d8KzCgXyC2dsNIt3zgWCu6RhL1V10lhBXMwl0N1+DAIRsXjCazwaxB
MrTNOsXrex98aaPk3++V1KkPbLnVZk8I7dbpt9EYSvMB7DaXFiLL4XySamFfYmtq
9n9bxKXxnzpObolWkD0SbioaxkCqvEFyy2/y4ZIxYC1mHhRgTtSicnk8WGOgv6Ax
1sRHpbRVDtXMmDHn7oiJdd8YCGvT9AQoibeccPUZbwaiuiULSVjphKErXLg7nH6/
ct8bVYZnh0GnYTljC3O0f3/D1A9ZPxLHJPc9K2GCmZ+TNMmDDmGbLz/TgFiSZE3C
QEORxbGyNh5MZuIrGub9xaKB4HonRf8HXwVa/zL4dKxpzYzHNBH8wqPOWCLXjp65
lcMW7JBAnrqshI0wOCEbk13uLudBaNL4k/h9M9RKaEW24tKpg29XsWu9II3Bt94x
7gwhokE7gICsycUSQWTGaBovbvn0C8k4gkPq6teGAD6AJ6/YV2hiPbdoRL5EkMS3
E01hmaHk9xlOpo73IjUxhXUCAwEAAQ==
-----END PUBLIC KEY-----
"""
77 changes: 35 additions & 42 deletions tests/test_backends.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import datetime, timedelta
from unittest import mock
from unittest.mock import patch

import jwt
Expand All @@ -10,55 +11,16 @@
from rest_framework_simplejwt.utils import (
aware_utcnow, datetime_to_epoch, make_utc,
)
from tests.keys import PRIVATE_KEY, PRIVATE_KEY_2, PUBLIC_KEY, PUBLIC_KEY_2

SECRET = 'not_secret'

PRIVATE_KEY = '''
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA3xMJfyl8TOdrsjDLSIodsArJ/NnQB3ZdfbFC5onxATDfRLLA
CHFo3ye694doBKeSe1NFYbfXPvahl6ODX1a23oQyoRQwlL+M99cLcdCa0gGuJXdb
AaF6Em8E+7uSb3290mI+rZmjqyc7gMtKVWKL4e5i2PerFFBoYkZ7E90KOp2t0ZAD
x2uqF4VTOfYLHG0cPgSw9/ptDStJqJVAOiRRqbv0j0GOFMDYNcN0mDlnpryhQFbQ
iMqn4IJIURZUVBJujFSa45cJPvSmMb6NrzZ1crg5UN6/5Mu2mxQzAi21+vpgGL+E
EuekUd7sRgEAjTHjLKzotLAGo7EGa8sL1vMSFwIDAQABAoIBAQCGGWabF/BONswq
CWUazVR9cG7uXm3NHp2jIr1p40CLC7scDCyeprZ5d+PQS4j/S1Ema++Ih8CQbCjG
BJjD5lf2OhhJdt6hfOkcUBzkJZf8aOAsS6zctRqyHCUtwxuLhFZpM4AkUfjuuZ3u
lcawv5YBkpG/hltE0fV+Jop0bWtpwiKxVsHXVcS0WEPXic0lsOTBCw8m81JXqjir
PCBOnkxgNpHSt69S1xnW3l9fPUWVlduO3EIZ5PZG2BxU081eZW31yIlKsDJhfgm6
R5Vlr5DynqeojAd6SNliCzNXZP28GOpQBrYIeVQWA1yMANvkvd4apz9GmDrjF/Fd
g8Chah+5AoGBAPc/+zyuDZKVHK7MxwLPlchCm5Zb4eou4ycbwEB+P3gDS7MODGu4
qvx7cstTZMuMavNRcJsfoiMMrke9JrqGe4rFGiKRFLVBY2Xwr+95pKNC11EWI1lF
5qDAmreDsj2alVJT5yZ9hsAWTsk2i+xj+/XHWYVkr67pRvOPRAmGMB+NAoGBAOb4
CBHe184Hn6Ie+gSD4OjewyUVmr3JDJ41s8cjb1kBvDJ/wv9Rvo9yz2imMr2F0YGc
ytHraM77v8KOJuJWpvGjEg8I0a/rSttxWQ+J0oYJSIPn+eDpAijNWfOp1aKRNALT
pboCXcnSn+djJFKkNJ2hR7R/vrrM6Jyly1jcVS0zAoGAQpdt4Cr0pt0YS5AFraEh
Mz2VUArRLtSQA3F69yPJjlY85i3LdJvZGYVaJp8AT74y8/OkQ3NipNP+gH3WV3hu
/7IUVukCTcsdrVAE4pe9mucevM0cmie0dOlLAlArCmJ/Axxr7jbyuvuHHrRdPT60
lr6pQr8afh6AKIsWhQYqIeUCgYA+v9IJcN52hhGzjPDl+yJGggbIc3cn6pA4B2UB
TDo7F0KXAajrjrzT4iBBUS3l2Y5SxVNA9tDxsumlJNOhmGMgsOn+FapKPgWHWuMU
WqBMdAc0dvinRwakKS4wCcsVsJdN0UxsHap3Y3a3+XJr1VrKHIALpM0fmP31WQHG
8Y1eiwKBgF6AYXxo0FzZacAommZrAYoxFZT1u4/rE/uvJ2K9HYRxLOVKZe+89ki3
D7AOmrxe/CAc/D+nNrtUIv3RFGfadfSBWzyLw36ekW76xPdJgqJsSz5XJ/FgzDW+
WNC5oOtiPOMCymP75oKOjuZJZ2SPLRmiuO/qvI5uAzBHxRC1BKdt
-----END RSA PRIVATE KEY-----
'''

PUBLIC_KEY = '''
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3xMJfyl8TOdrsjDLSIod
sArJ/NnQB3ZdfbFC5onxATDfRLLACHFo3ye694doBKeSe1NFYbfXPvahl6ODX1a2
3oQyoRQwlL+M99cLcdCa0gGuJXdbAaF6Em8E+7uSb3290mI+rZmjqyc7gMtKVWKL
4e5i2PerFFBoYkZ7E90KOp2t0ZADx2uqF4VTOfYLHG0cPgSw9/ptDStJqJVAOiRR
qbv0j0GOFMDYNcN0mDlnpryhQFbQiMqn4IJIURZUVBJujFSa45cJPvSmMb6NrzZ1
crg5UN6/5Mu2mxQzAi21+vpgGL+EEuekUd7sRgEAjTHjLKzotLAGo7EGa8sL1vMS
FwIDAQAB
-----END PUBLIC KEY-----
'''

AUDIENCE = 'openid-client-id'

ISSUER = 'https://www.myoidcprovider.com'

JWK_URL = 'https://randomstring.auth0.com/.well-known/jwks.json'


class TestTokenBackend(TestCase):
def setUp(self):
Expand Down Expand Up @@ -276,6 +238,37 @@ def test_decode_aud_iss_success(self):

self.assertEqual(self.aud_iss_token_backend.decode(token), self.payload)

def test_decode_rsa_aud_iss_jwk_success(self):
self.payload["exp"] = aware_utcnow() + timedelta(days=1)
self.payload["foo"] = "baz"
self.payload["aud"] = AUDIENCE
self.payload["iss"] = ISSUER

token = jwt.encode(
self.payload,
PRIVATE_KEY_2,
algorithm="RS256",
headers={"kid": "230498151c214b788dd97f22b85410a5"},
)
# Payload copied
self.payload["exp"] = datetime_to_epoch(self.payload["exp"])

mock_jwk_module = mock.MagicMock()
with patch("rest_framework_simplejwt.backends.PyJWKClient") as mock_jwk_module:
mock_jwk_client = mock.MagicMock()
mock_signing_key = mock.MagicMock()

mock_jwk_module.return_value = mock_jwk_client
mock_jwk_client.get_signing_key_from_jwt.return_value = mock_signing_key
type(mock_signing_key).key = mock.PropertyMock(return_value=PUBLIC_KEY_2)

# Note the PRIV,PUB care is intentially the original pairing
jwk_token_backend = TokenBackend(
"RS256", PRIVATE_KEY, PUBLIC_KEY, AUDIENCE, ISSUER, JWK_URL
)

self.assertEqual(jwk_token_backend.decode(token), self.payload)

def test_decode_when_algorithm_not_available(self):
token = jwt.encode(self.payload, PRIVATE_KEY, algorithm='RS256')

Expand Down
3 changes: 1 addition & 2 deletions tests/test_token_blacklist.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def test_token_verify_serializer_should_not_honour_blacklist_if_blacklisting_not
serializer = TokenVerifySerializer(data={"token": str(refresh_token)})
self.assertTrue(serializer.is_valid())


class TestBigAutoFieldIDMigration(MigrationTestCase):
migrate_from = ('token_blacklist', '0007_auto_20171017_2214')
migrate_to = ('token_blacklist', '0008_migrate_to_bigautofield')
Expand All @@ -234,4 +234,3 @@ def test_outstandingtoken_id_field_is_biagauto_field(self):
def test_blacklistedtoken_id_field_is_biagauto_field(self):
BlacklistedToken = self.apps.get_model('token_blacklist', 'BlacklistedToken')
assert isinstance(BlacklistedToken._meta.get_field('id'), BigAutoField)

0 comments on commit f97ef69

Please sign in to comment.