From e3c615eab216f97162efeb789d2791e81afdbe78 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Tue, 8 Sep 2020 01:37:33 +0200 Subject: [PATCH 01/19] Updated the tests to match the new criterias --- oidc_auth/authentication.py | 155 ++++++++++++++++++++++------------- tests/settings.py | 2 +- tests/test_authentication.py | 89 ++++++++++++-------- tests/test_util.py | 7 +- 4 files changed, 161 insertions(+), 92 deletions(-) diff --git a/oidc_auth/authentication.py b/oidc_auth/authentication.py index 063f3b3..4d4eb3e 100644 --- a/oidc_auth/authentication.py +++ b/oidc_auth/authentication.py @@ -1,20 +1,24 @@ -from calendar import timegm -import datetime +import time + +import requests from django.contrib.auth import get_user_model -from django.utils.encoding import smart_text +from django.utils.encoding import smart_str from django.utils.functional import cached_property -from jwkest import JWKESTException -from jwkest.jwk import KEYS -from jwkest.jws import JWS -import requests +from django.utils.translation import ugettext as _ from requests import request from requests.exceptions import HTTPError -from rest_framework.authentication import BaseAuthentication, get_authorization_header +from rest_framework.authentication import (BaseAuthentication, + get_authorization_header) from rest_framework.exceptions import AuthenticationFailed -import six -from .util import cache + +from authlib.jose import JsonWebKey, jwt +from authlib.oidc.core.claims import IDToken +from authlib.jose.errors import (BadSignatureError, DecodeError, + ExpiredTokenError, JoseError) +from authlib.oidc.discovery import get_well_known_url + from .settings import api_settings -from django.utils.translation import ugettext as _ +from .util import cache def get_user_by_id(request, id_token): @@ -27,11 +31,42 @@ def get_user_by_id(request, id_token): return user +class DRFIDToken(IDToken): + + def validate_exp(self, now, leeway): + super().validate_exp(now, leeway) + if now > self['exp']: + msg = _('Invalid Authorization header. JWT has expired.') + raise AuthenticationFailed(msg) + + def validate_iat(self, now, leeway): + super().validate_iat(now, leeway) + if self['iat'] < leeway: + msg = _('Invalid Authorization header. JWT too old.') + raise AuthenticationFailed(msg) + + def validate_aud(self): + super().validate_aud() + if isinstance(self['aud'], str): + self['aud'] = [self['aud']] + if len(self['aud']) > 1 and 'azp' not in self: + msg = _('Invalid Authorization header. Missing JWT authorized party.') + raise AuthenticationFailed(msg) + else: + super().validate_azp() + + + class BaseOidcAuthentication(BaseAuthentication): @property @cache(ttl=api_settings.OIDC_BEARER_TOKEN_EXPIRATION_TIME) def oidc_config(self): - return requests.get(api_settings.OIDC_ENDPOINT + '/.well-known/openid-configuration').json() + return requests.get( + get_well_known_url( + api_settings.OIDC_ENDPOINT, + external=True + ) + ).json() class BearerTokenAuthentication(BaseOidcAuthentication): @@ -55,23 +90,28 @@ def authenticate(self, request): def get_bearer_token(self, request): auth = get_authorization_header(request).split() auth_header_prefix = api_settings.BEARER_AUTH_HEADER_PREFIX.lower() - - if not auth or smart_text(auth[0].lower()) != auth_header_prefix: + if not auth or smart_str(auth[0].lower()) != auth_header_prefix: return None if len(auth) == 1: msg = _('Invalid Authorization header. No credentials provided') raise AuthenticationFailed(msg) elif len(auth) > 2: - msg = _('Invalid Authorization header. Credentials string should not contain spaces.') + msg = _( + 'Invalid Authorization header. Credentials string should not contain spaces.') raise AuthenticationFailed(msg) return auth[1] @cache(ttl=api_settings.OIDC_BEARER_TOKEN_EXPIRATION_TIME) def get_userinfo(self, token): - response = requests.get(self.oidc_config['userinfo_endpoint'], - headers={'Authorization': 'Bearer {0}'.format(token.decode('ascii'))}) + response = requests.get( + self.oidc_config['userinfo_endpoint'], + headers={'Authorization': 'Bearer {0}'.format( + token.decode('ascii') + ) + } + ) response.raise_for_status() return response.json() @@ -86,7 +126,6 @@ def authenticate(self, request): jwt_value = self.get_jwt_value(request) if jwt_value is None: return None - payload = self.decode_jwt(jwt_value) self.validate_claims(payload) @@ -98,70 +137,76 @@ def get_jwt_value(self, request): auth = get_authorization_header(request).split() auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower() - if not auth or smart_text(auth[0].lower()) != auth_header_prefix: + if not auth or smart_str(auth[0].lower()) != auth_header_prefix: return None if len(auth) == 1: msg = _('Invalid Authorization header. No credentials provided') raise AuthenticationFailed(msg) elif len(auth) > 2: - msg = _('Invalid Authorization header. Credentials string should not contain spaces.') + msg = _( + 'Invalid Authorization header. Credentials string should not contain spaces.') raise AuthenticationFailed(msg) return auth[1] + # @cache(ttl=api_settings.OIDC_JWKS_EXPIRATION_TIME) def jwks(self): - keys = KEYS() - keys.load_jwks(self.jwks_data()) - return keys - - @cache(ttl=api_settings.OIDC_JWKS_EXPIRATION_TIME) - def jwks_data(self): r = request("GET", self.oidc_config['jwks_uri'], allow_redirects=True) r.raise_for_status() - return r.text + keys = JsonWebKey.import_key_set(r.json()) + return keys @cached_property def issuer(self): return self.oidc_config['issuer'] - def decode_jwt(self, jwt_value): - keys = self.jwks() + def decode_jwt(self, jwt_value: bytes): try: - id_token = JWS().verify_compact(jwt_value, keys=keys) - except (JWKESTException, ValueError): - msg = _('Invalid Authorization header. JWT Signature verification failed.') + # assert isinstance(jwt_value, bytes) + claims_options = { + 'iss': { + 'essential': True, + 'value': self.issuer + }, + 'aud': { + 'values': self.get_audiences() + } + } + + id_token = jwt.decode( + jwt_value.decode('ascii'), + self.jwks(), + claims_cls=DRFIDToken, + claims_options=claims_options + ) + + except (BadSignatureError, DecodeError): + msg = _( + 'Invalid Authorization header. JWT Signature verification failed.') + raise AuthenticationFailed(msg) + except AssertionError: + msg = _( + 'Invalid Authorization header. Please provide base64 encoded ID Token' + ) raise AuthenticationFailed(msg) return id_token - def get_audiences(self, id_token): - return api_settings.OIDC_AUDIENCES + def get_audiences(self): + return [audience for audience in api_settings.OIDC_AUDIENCES] def validate_claims(self, id_token): - if isinstance(id_token.get('aud'), six.string_types): - # Support for multiple audiences - id_token['aud'] = [id_token['aud']] - - if id_token.get('iss') != self.issuer: - msg = _('Invalid Authorization header. Invalid JWT issuer.') - raise AuthenticationFailed(msg) - if not any(aud in self.get_audiences(id_token) for aud in id_token.get('aud', [])): - msg = _('Invalid Authorization header. Invalid JWT audience.') - raise AuthenticationFailed(msg) - if len(id_token['aud']) > 1 and 'azp' not in id_token: - msg = _('Invalid Authorization header. Missing JWT authorized party.') - raise AuthenticationFailed(msg) - - utc_timestamp = timegm(datetime.datetime.utcnow().utctimetuple()) - if utc_timestamp > id_token.get('exp', 0): + try: + id_token.validate( + now=int(time.time()), + leeway=int(time.time()-api_settings.OIDC_LEEWAY) + ) + except ExpiredTokenError: msg = _('Invalid Authorization header. JWT has expired.') raise AuthenticationFailed(msg) - if 'nbf' in id_token and utc_timestamp < id_token['nbf']: - msg = _('Invalid Authorization header. JWT not yet valid.') - raise AuthenticationFailed(msg) - if utc_timestamp > id_token.get('iat', 0) + api_settings.OIDC_LEEWAY: - msg = _('Invalid Authorization header. JWT too old.') + except JoseError as e: + msg = _(str(type(e)) + str(e)) raise AuthenticationFailed(msg) def authenticate_header(self, request): diff --git a/tests/settings.py b/tests/settings.py index 8f768da..292ab08 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,4 +1,4 @@ -SECRET_KEY='secret' +SECRET_KEY = 'secret' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', diff --git a/tests/test_authentication.py b/tests/test_authentication.py index 017ecd0..8f3e7c9 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -1,17 +1,18 @@ -from django.contrib.auth.models import User import json +import sys -from jwkest import long_to_base64 -from rest_framework.permissions import IsAuthenticated from django.conf.urls import url +from django.contrib.auth.models import User from django.http import HttpResponse from django.test import TestCase -from jwkest.jwk import RSAKey, KEYS -from jwkest.jws import JWS +from requests import Response +from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView -from requests import Response, HTTPError, ConnectionError -from oidc_auth.authentication import JSONWebTokenAuthentication, BearerTokenAuthentication -import sys + +from authlib.jose import JsonWebToken, KeySet, RSAKey +from oidc_auth.authentication import (BearerTokenAuthentication, + JSONWebTokenAuthentication) + if sys.version_info > (3,): long = int try: @@ -22,7 +23,10 @@ class MockView(APIView): permission_classes = (IsAuthenticated,) - authentication_classes = (JSONWebTokenAuthentication, BearerTokenAuthentication) + authentication_classes = ( + JSONWebTokenAuthentication, + BearerTokenAuthentication + ) def get(self, request): return HttpResponse('a') @@ -32,16 +36,14 @@ def get(self, request): url(r'^test/$', MockView.as_view(), name="testview") ] -key = RSAKey(kid="test", - kty="RSA", - e=long_to_base64(long(65537)), - n=long_to_base64(long(103144733181541730170695212353035735911272360475451101847332641719504193145911782103718552703497383385072400068398348471608551845979550140132066577502098324638900101678499876506366406838561711807168917151266210861310839976066381600661109647310812646802675105044570916072792610952531033569123889433857109695663)), - d=long_to_base64(long(87474011172773995802176478974956531454728135178991596207863469898989014679490621318105454312226445649668492543167679449044101982079487873850500638991205330610459744732712633893362912169260215247013564296846583369572335796121742404877695795618480142002129365141632060905382558309932032446524457731175746076993))) +key = RSAKey.generate_key(is_private=True) def make_jwt(payload): - jws = JWS(payload, alg='RS256') - return jws.sign_compact([key]) + jwt = JsonWebToken(['RS256']) + jws = jwt.encode( + {'alg': 'RS256', 'kid': key.as_dict(add_kid=True).get('kid')}, payload, key=key) + return jws def make_id_token(sub, @@ -50,14 +52,16 @@ def make_id_token(sub, exp=999999999999, # tests will start failing in September 33658 iat=999999999999, **kwargs): - return make_jwt(dict( - iss=iss, - aud=aud, - exp=exp, - iat=iat, - sub=str(sub), - **kwargs - )) + return make_jwt( + dict( + iss=iss, + aud=aud, + exp=exp, + iat=iat, + sub=str(sub), + **kwargs + ) + ).decode('ascii') class FakeRequests(object): @@ -99,23 +103,30 @@ def setUp(self): "userinfo_endpoint": "http://example.com/userinfo"}) self.mock_get = self.patch('requests.get') self.mock_get.side_effect = self.responder.get - keys = KEYS() - keys.add({'key': key, 'kty': 'RSA', 'kid': key.kid}) - self.patch('oidc_auth.authentication.request', return_value=Mock(status_code=200, - text=keys.dump_jwks())) + keys = KeySet(keys=[key]) + self.patch( + 'oidc_auth.authentication.request', + return_value=Mock( + status_code=200, + json=keys.as_json + ) + ) class TestBearerAuthentication(AuthenticationTestCase): def test_using_valid_bearer_token(self): - self.responder.set_response('http://example.com/userinfo', {'sub': self.user.username}) + self.responder.set_response( + 'http://example.com/userinfo', {'sub': self.user.username}) auth = 'Bearer abcdefg' resp = self.client.get('/test/', HTTP_AUTHORIZATION=auth) self.assertEqual(resp.content.decode(), 'a') self.assertEqual(resp.status_code, 200) - self.mock_get.assert_called_with('http://example.com/userinfo', headers={'Authorization': auth}) + self.mock_get.assert_called_with( + 'http://example.com/userinfo', headers={'Authorization': auth}) def test_cache_of_valid_bearer_token(self): - self.responder.set_response('http://example.com/userinfo', {'sub': self.user.username}) + self.responder.set_response( + 'http://example.com/userinfo', {'sub': self.user.username}) auth = 'Bearer egergerg' resp = self.client.get('/test/', HTTP_AUTHORIZATION=auth) self.assertEqual(resp.status_code, 200) @@ -138,7 +149,8 @@ def test_cache_of_invalid_bearer_token(self): self.assertEqual(resp.status_code, 401) # Token becomes valid - self.responder.set_response('http://example.com/userinfo', {'sub': self.user.username}) + self.responder.set_response( + 'http://example.com/userinfo', {'sub': self.user.username}) resp = self.client.get('/test/', HTTP_AUTHORIZATION=auth) self.assertEqual(resp.status_code, 200) @@ -171,7 +183,7 @@ def test_without_jwt(self): self.assertEqual(resp.status_code, 401) def test_with_invalid_jwt(self): - auth = 'JWT bla' + auth = 'JWT e30=' resp = self.client.get('/test/', HTTP_AUTHORIZATION=auth) self.assertEqual(resp.status_code, 401) @@ -191,7 +203,8 @@ def test_with_old_jwt(self): self.assertEqual(resp.status_code, 401) def test_with_invalid_issuer(self): - auth = 'JWT ' + make_id_token(self.user.username, iss='http://something.com') + auth = 'JWT ' + \ + make_id_token(self.user.username, iss='http://something.com') resp = self.client.get('/test/', HTTP_AUTHORIZATION=auth) self.assertEqual(resp.status_code, 401) @@ -215,8 +228,14 @@ def test_with_multiple_audiences(self): resp = self.client.get('/test/', HTTP_AUTHORIZATION=auth) self.assertEqual(resp.status_code, 401) + def test_with_invalid_multiple_audiences(self): + auth = 'JWT ' + make_id_token(self.user.username, aud=['we', 'me']) + resp = self.client.get('/test/', HTTP_AUTHORIZATION=auth) + self.assertEqual(resp.status_code, 401) + def test_with_multiple_audiences_and_authorized_party(self): - auth = 'JWT ' + make_id_token(self.user.username, aud=['you', 'me'], azp='you') + auth = 'JWT ' + \ + make_id_token(self.user.username, aud=['you', 'me'], azp='you') resp = self.client.get('/test/', HTTP_AUTHORIZATION=auth) self.assertEqual(resp.status_code, 200) diff --git a/tests/test_util.py b/tests/test_util.py index 411300d..30a8e15 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -79,4 +79,9 @@ def test_respects_cache_prefix(self, caches): caches['default'].get.return_value = None self.mymethod() caches['default'].get.assert_called_once_with('some-other-prefix.mymethod', version=1) - caches['default'].set.assert_called_once_with('some-other-prefix.mymethod', ANY, timeout=1, version=1) + caches['default'].set.assert_called_once_with( + 'some-other-prefix.mymethod', + ANY, + timeout=1, + version=1 + ) From 96a874702e50d5252d2557078e095103c3c99680 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Tue, 8 Sep 2020 01:37:53 +0200 Subject: [PATCH 02/19] Use authlib in drf-oidc-auth --- oidc_auth/authentication.py | 3 +-- oidc_auth/settings.py | 1 - oidc_auth/util.py | 6 ++++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/oidc_auth/authentication.py b/oidc_auth/authentication.py index 4d4eb3e..5719f92 100644 --- a/oidc_auth/authentication.py +++ b/oidc_auth/authentication.py @@ -12,9 +12,9 @@ from rest_framework.exceptions import AuthenticationFailed from authlib.jose import JsonWebKey, jwt -from authlib.oidc.core.claims import IDToken from authlib.jose.errors import (BadSignatureError, DecodeError, ExpiredTokenError, JoseError) +from authlib.oidc.core.claims import IDToken from authlib.oidc.discovery import get_well_known_url from .settings import api_settings @@ -56,7 +56,6 @@ def validate_aud(self): super().validate_azp() - class BaseOidcAuthentication(BaseAuthentication): @property @cache(ttl=api_settings.OIDC_BEARER_TOKEN_EXPIRATION_TIME) diff --git a/oidc_auth/settings.py b/oidc_auth/settings.py index 284ad6c..0d3e3d0 100644 --- a/oidc_auth/settings.py +++ b/oidc_auth/settings.py @@ -1,7 +1,6 @@ from django.conf import settings from rest_framework.settings import APISettings - USER_SETTINGS = getattr(settings, 'OIDC_AUTH', None) DEFAULTS = { diff --git a/oidc_auth/util.py b/oidc_auth/util.py index 75761d0..7e397be 100644 --- a/oidc_auth/util.py +++ b/oidc_auth/util.py @@ -1,14 +1,16 @@ import functools from django.core.cache import caches + from .settings import api_settings class cache(object): """ Cache decorator that memoizes the return value of a method for some time. - Increment the cache_version everytime your method's implementation changes in such a way that it returns values - that are not backwards compatible. For more information, see the Django cache documentation: + Increment the cache_version everytime your method's implementation changes + in such a way that it returns values that are not backwards compatible. + For more information, see the Django cache documentation: https://docs.djangoproject.com/en/2.2/topics/cache/#cache-versioning """ From 8727f97aacfb36e4b56d47de1ff7535cd15a3847 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Tue, 8 Sep 2020 01:38:10 +0200 Subject: [PATCH 03/19] Ignore .vscode folder --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 6d9b88f..8d7a84f 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,6 @@ target/ # IDEA .idea/ + +# VSCode +.vscode/ From b7093d6c7c3f9dd1178f67fc9ca48445e672c976 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Tue, 8 Sep 2020 01:38:27 +0200 Subject: [PATCH 04/19] Added basic flake8 line length --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index 5e40900..bc26005 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,4 @@ [wheel] universal = 1 +[flake8] +max-line-length=100 From 7c331f2ae5daeeb26b3946e19c170deb23cb1a11 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Tue, 8 Sep 2020 01:39:34 +0200 Subject: [PATCH 05/19] Introduced authlib 0.15.0 and requests. --- setup.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index aca8283..4858509 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='drf-oidc-auth', - version='0.10.0', + version='0.11.0', packages=['oidc_auth'], url='https://github.com/ByteInternet/drf-oidc-auth', license='MIT', @@ -10,9 +10,10 @@ author_email='support@byte.nl', description='OpenID Connect authentication for Django Rest Framework', install_requires=[ - 'pyjwkest>=1.0.3', + 'authlib>=0.15.0', 'django>=1.8.0', 'djangorestframework>=3.0.0', + 'requests>=2.20.0' ], classifiers=[ 'Development Status :: 4 - Beta', @@ -26,6 +27,10 @@ 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Topic :: Internet', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', From 957b000c3d27b6b3f7c37735539095174655559f Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Tue, 8 Sep 2020 01:40:05 +0200 Subject: [PATCH 06/19] Added Pipfiles for pipenv dev support --- Pipfile | 20 ++++ Pipfile.lock | 318 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 338 insertions(+) create mode 100644 Pipfile create mode 100644 Pipfile.lock diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..2d5158f --- /dev/null +++ b/Pipfile @@ -0,0 +1,20 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] +autopep8 = "*" +flake8 = "*" +tox = "<3.8.0" +tox-pipenv = "*" + +[packages] +django = "*" +djangorestframework = "*" +# authlib = "*" +authlib = {editable = true, ref = "master", git = "https://github.com/lepture/authlib.git"} +requests = "*" + +[requires] +python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..616de08 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,318 @@ +{ + "_meta": { + "hash": { + "sha256": "05e25e0a486b9c6454bb89d88cd887e75dea658eab244fcbc8062a60af8fb451" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.8" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "asgiref": { + "hashes": [ + "sha256:7e51911ee147dd685c3c8b805c0ad0cb58d360987b56953878f8c06d2d1c6f1a", + "sha256:9fc6fb5d39b8af147ba40765234fa822b39818b12cc80b35ad9b0cef3a476aed" + ], + "markers": "python_version >= '3.5'", + "version": "==3.2.10" + }, + "authlib": { + "editable": true, + "git": "https://github.com/lepture/authlib.git", + "ref": "319eb60268e7f18f0d3e837bd3024d4c685d86fa" + }, + "certifi": { + "hashes": [ + "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", + "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" + ], + "version": "==2020.6.20" + }, + "cffi": { + "hashes": [ + "sha256:0da50dcbccd7cb7e6c741ab7912b2eff48e85af217d72b57f80ebc616257125e", + "sha256:12a453e03124069b6896107ee133ae3ab04c624bb10683e1ed1c1663df17c13c", + "sha256:15419020b0e812b40d96ec9d369b2bc8109cc3295eac6e013d3261343580cc7e", + "sha256:15a5f59a4808f82d8ec7364cbace851df591c2d43bc76bcbe5c4543a7ddd1bf1", + "sha256:23e44937d7695c27c66a54d793dd4b45889a81b35c0751ba91040fe825ec59c4", + "sha256:29c4688ace466a365b85a51dcc5e3c853c1d283f293dfcc12f7a77e498f160d2", + "sha256:57214fa5430399dffd54f4be37b56fe22cedb2b98862550d43cc085fb698dc2c", + "sha256:577791f948d34d569acb2d1add5831731c59d5a0c50a6d9f629ae1cefd9ca4a0", + "sha256:6539314d84c4d36f28d73adc1b45e9f4ee2a89cdc7e5d2b0a6dbacba31906798", + "sha256:65867d63f0fd1b500fa343d7798fa64e9e681b594e0a07dc934c13e76ee28fb1", + "sha256:672b539db20fef6b03d6f7a14b5825d57c98e4026401fce838849f8de73fe4d4", + "sha256:6843db0343e12e3f52cc58430ad559d850a53684f5b352540ca3f1bc56df0731", + "sha256:7057613efefd36cacabbdbcef010e0a9c20a88fc07eb3e616019ea1692fa5df4", + "sha256:76ada88d62eb24de7051c5157a1a78fd853cca9b91c0713c2e973e4196271d0c", + "sha256:837398c2ec00228679513802e3744d1e8e3cb1204aa6ad408b6aff081e99a487", + "sha256:8662aabfeab00cea149a3d1c2999b0731e70c6b5bac596d95d13f643e76d3d4e", + "sha256:95e9094162fa712f18b4f60896e34b621df99147c2cee216cfa8f022294e8e9f", + "sha256:99cc66b33c418cd579c0f03b77b94263c305c389cb0c6972dac420f24b3bf123", + "sha256:9b219511d8b64d3fa14261963933be34028ea0e57455baf6781fe399c2c3206c", + "sha256:ae8f34d50af2c2154035984b8b5fc5d9ed63f32fe615646ab435b05b132ca91b", + "sha256:b9aa9d8818c2e917fa2c105ad538e222a5bce59777133840b93134022a7ce650", + "sha256:bf44a9a0141a082e89c90e8d785b212a872db793a0080c20f6ae6e2a0ebf82ad", + "sha256:c0b48b98d79cf795b0916c57bebbc6d16bb43b9fc9b8c9f57f4cf05881904c75", + "sha256:da9d3c506f43e220336433dffe643fbfa40096d408cb9b7f2477892f369d5f82", + "sha256:e4082d832e36e7f9b2278bc774886ca8207346b99f278e54c9de4834f17232f7", + "sha256:e4b9b7af398c32e408c00eb4e0d33ced2f9121fd9fb978e6c1b57edd014a7d15", + "sha256:e613514a82539fc48291d01933951a13ae93b6b444a88782480be32245ed4afa", + "sha256:f5033952def24172e60493b68717792e3aebb387a8d186c43c020d9363ee7281" + ], + "version": "==1.14.2" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "cryptography": { + "hashes": [ + "sha256:10c9775a3f31610cf6b694d1fe598f2183441de81cedcf1814451ae53d71b13a", + "sha256:180c9f855a8ea280e72a5d61cf05681b230c2dce804c48e9b2983f491ecc44ed", + "sha256:247df238bc05c7d2e934a761243bfdc67db03f339948b1e2e80c75d41fc7cc36", + "sha256:26409a473cc6278e4c90f782cd5968ebad04d3911ed1c402fc86908c17633e08", + "sha256:2a27615c965173c4c88f2961cf18115c08fedfb8bdc121347f26e8458dc6d237", + "sha256:2e26223ac636ca216e855748e7d435a1bf846809ed12ed898179587d0cf74618", + "sha256:321761d55fb7cb256b771ee4ed78e69486a7336be9143b90c52be59d7657f50f", + "sha256:4005b38cd86fc51c955db40b0f0e52ff65340874495af72efabb1bb8ca881695", + "sha256:4b9e96543d0784acebb70991ebc2dbd99aa287f6217546bb993df22dd361d41c", + "sha256:548b0818e88792318dc137d8b1ec82a0ab0af96c7f0603a00bb94f896fbf5e10", + "sha256:725875681afe50b41aee7fdd629cedbc4720bab350142b12c55c0a4d17c7416c", + "sha256:7a63e97355f3cd77c94bd98c59cb85fe0efd76ea7ef904c9b0316b5bbfde6ed1", + "sha256:94191501e4b4009642be21dde2a78bd3c2701a81ee57d3d3d02f1d99f8b64a9e", + "sha256:969ae512a250f869c1738ca63be843488ff5cc031987d302c1f59c7dbe1b225f", + "sha256:9f734423eb9c2ea85000aa2476e0d7a58e021bc34f0a373ac52a5454cd52f791", + "sha256:b45ab1c6ece7c471f01c56f5d19818ca797c34541f0b2351635a5c9fe09ac2e0", + "sha256:cc6096c86ec0de26e2263c228fb25ee01c3ff1346d3cfc219d67d49f303585af", + "sha256:dc3f437ca6353979aace181f1b790f0fc79e446235b14306241633ab7d61b8f8", + "sha256:e7563eb7bc5c7e75a213281715155248cceba88b11cb4b22957ad45b85903761", + "sha256:e7dad66a9e5684a40f270bd4aee1906878193ae50a4831922e454a2a457f1716", + "sha256:eb80a288e3cfc08f679f95da72d2ef90cb74f6d8a8ba69d2f215c5e110b2ca32", + "sha256:fa7fbcc40e2210aca26c7ac8a39467eae444d90a2c346cbcffd9133a166bcc67" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==3.1" + }, + "django": { + "hashes": [ + "sha256:59c8125ca873ed3bdae9c12b146fbbd6ed8d0f743e4cf5f5817af50c51f1fc2f", + "sha256:b5fbb818e751f660fa2d576d9f40c34a4c615c8b48dd383f5216e609f383371f" + ], + "index": "pypi", + "version": "==3.1.1" + }, + "djangorestframework": { + "hashes": [ + "sha256:6dd02d5a4bd2516fb93f80360673bf540c3b6641fec8766b1da2870a5aa00b32", + "sha256:8b1ac62c581dbc5799b03e535854b92fc4053ecfe74bad3f9c05782063d4196b" + ], + "index": "pypi", + "version": "==3.11.1" + }, + "idna": { + "hashes": [ + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.10" + }, + "pycparser": { + "hashes": [ + "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", + "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.20" + }, + "pytz": { + "hashes": [ + "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed", + "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048" + ], + "version": "==2020.1" + }, + "requests": { + "hashes": [ + "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", + "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" + ], + "index": "pypi", + "version": "==2.24.0" + }, + "six": { + "hashes": [ + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.15.0" + }, + "sqlparse": { + "hashes": [ + "sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e", + "sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.3.1" + }, + "urllib3": { + "hashes": [ + "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", + "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "version": "==1.25.10" + } + }, + "develop": { + "appdirs": { + "hashes": [ + "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", + "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128" + ], + "version": "==1.4.4" + }, + "autopep8": { + "hashes": [ + "sha256:d21d3901cb0da6ebd1e83fc9b0dfbde8b46afc2ede4fe32fbda0c7c6118ca094" + ], + "index": "pypi", + "version": "==1.5.4" + }, + "certifi": { + "hashes": [ + "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", + "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" + ], + "version": "==2020.6.20" + }, + "distlib": { + "hashes": [ + "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb", + "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1" + ], + "version": "==0.3.1" + }, + "filelock": { + "hashes": [ + "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", + "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" + ], + "version": "==3.0.12" + }, + "flake8": { + "hashes": [ + "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c", + "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208" + ], + "index": "pypi", + "version": "==3.8.3" + }, + "mccabe": { + "hashes": [ + "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", + "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + ], + "version": "==0.6.1" + }, + "pipenv": { + "hashes": [ + "sha256:448ac3a36443db633d52a2359cac15ecbc4f429eab4ddd420697602b721d1c5a", + "sha256:eff0e10eadb330f612edfa5051d3d8e775e9e0e918c3c50361da703bd0daa035" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2020.8.13" + }, + "pluggy": { + "hashes": [ + "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", + "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.13.1" + }, + "py": { + "hashes": [ + "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2", + "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.9.0" + }, + "pycodestyle": { + "hashes": [ + "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", + "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.6.0" + }, + "pyflakes": { + "hashes": [ + "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", + "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.2.0" + }, + "six": { + "hashes": [ + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.15.0" + }, + "toml": { + "hashes": [ + "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", + "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88" + ], + "version": "==0.10.1" + }, + "tox": { + "hashes": [ + "sha256:04f8f1aa05de8e76d7a266ccd14e0d665d429977cd42123bc38efa9b59964e9e", + "sha256:25ef928babe88c71e3ed3af0c464d1160b01fca2dd1870a5bb26c2dea61a17fc" + ], + "index": "pypi", + "version": "==3.7.0" + }, + "tox-pipenv": { + "hashes": [ + "sha256:23c44c787e6ab2f2b00e9a68820dde677fd93345d74edbd27cc19bc035a99b18", + "sha256:4d3207954d97f52abe8835d8538916f4d34975de2329d0cdef611cfb93355542" + ], + "index": "pypi", + "version": "==1.10.0" + }, + "virtualenv": { + "hashes": [ + "sha256:43add625c53c596d38f971a465553f6318decc39d98512bc100fa1b1e839c8dc", + "sha256:e0305af10299a7fb0d69393d8f04cb2965dda9351140d11ac8db4e5e3970451b" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==20.0.31" + }, + "virtualenv-clone": { + "hashes": [ + "sha256:07e74418b7cc64f4fda987bf5bc71ebd59af27a7bc9e8a8ee9fd54b1f2390a27", + "sha256:665e48dd54c84b98b71a657acb49104c54e7652bce9c1c4f6c6976ed4c827a29" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.5.4" + } + } +} From 8b6195bed617d4409a3770678183465fa098c229 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Wed, 16 Sep 2020 01:37:48 +0200 Subject: [PATCH 07/19] Fixed the reviewed parts and found a bug with authlib and caching. --- oidc_auth/authentication.py | 48 +++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/oidc_auth/authentication.py b/oidc_auth/authentication.py index 2754c52..f8163c4 100644 --- a/oidc_auth/authentication.py +++ b/oidc_auth/authentication.py @@ -124,6 +124,24 @@ class JSONWebTokenAuthentication(BaseOidcAuthentication): www_authenticate_realm = 'api' + @property + def audiences(self): + return [audience for audience in api_settings.OIDC_AUDIENCES] + + @property + def claims_options(self): + return { + 'iss': { + 'essential': True, + 'value': self.issuer + }, + 'aud': { + 'values': self.audiences + } + + + } + def authenticate(self, request): jwt_value = self.get_jwt_value(request) if jwt_value is None: @@ -152,12 +170,17 @@ def get_jwt_value(self, request): return auth[1] - # @cache(ttl=api_settings.OIDC_JWKS_EXPIRATION_TIME) - def jwks(self): + @cache(ttl=api_settings.OIDC_JWKS_EXPIRATION_TIME) + def jwks_request(self): r = request("GET", self.oidc_config['jwks_uri'], allow_redirects=True) r.raise_for_status() - keys = JsonWebKey.import_key_set(r.json()) - return keys + return r.json() + + def jwks(self): + # Is needed because caching of JsonWebKey is not working as expected. + # I don't want to debug it deeper. + # This way it works and the request is saved until ttl. + return JsonWebKey.import_key_set(self.jwks_request()) @cached_property def issuer(self): @@ -165,24 +188,12 @@ def issuer(self): def decode_jwt(self, jwt_value: bytes): try: - # assert isinstance(jwt_value, bytes) - claims_options = { - 'iss': { - 'essential': True, - 'value': self.issuer - }, - 'aud': { - 'values': self.get_audiences() - } - } - id_token = jwt.decode( jwt_value.decode('ascii'), self.jwks(), claims_cls=DRFIDToken, - claims_options=claims_options + claims_options=self.claims_options ) - except (BadSignatureError, DecodeError): msg = _( 'Invalid Authorization header. JWT Signature verification failed.') @@ -196,9 +207,6 @@ def decode_jwt(self, jwt_value: bytes): return id_token - def get_audiences(self): - return [audience for audience in api_settings.OIDC_AUDIENCES] - def validate_claims(self, id_token): try: id_token.validate( From da64bbe434f0ab6d6d03e7e35f37071a16c573c2 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Wed, 16 Sep 2020 01:40:23 +0200 Subject: [PATCH 08/19] Name it as it was before. jwks_data --- oidc_auth/authentication.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/oidc_auth/authentication.py b/oidc_auth/authentication.py index f8163c4..0606fa1 100644 --- a/oidc_auth/authentication.py +++ b/oidc_auth/authentication.py @@ -171,16 +171,13 @@ def get_jwt_value(self, request): return auth[1] @cache(ttl=api_settings.OIDC_JWKS_EXPIRATION_TIME) - def jwks_request(self): + def jwks_data(self): r = request("GET", self.oidc_config['jwks_uri'], allow_redirects=True) r.raise_for_status() return r.json() def jwks(self): - # Is needed because caching of JsonWebKey is not working as expected. - # I don't want to debug it deeper. - # This way it works and the request is saved until ttl. - return JsonWebKey.import_key_set(self.jwks_request()) + return JsonWebKey.import_key_set(self.jwks_data()) @cached_property def issuer(self): From 38d52d41dfbba896a1afd0153603bc78b1d5207c Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Wed, 16 Sep 2020 01:45:04 +0200 Subject: [PATCH 09/19] Put it on the same spot to not confuse git. --- oidc_auth/authentication.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/oidc_auth/authentication.py b/oidc_auth/authentication.py index 0606fa1..281e137 100644 --- a/oidc_auth/authentication.py +++ b/oidc_auth/authentication.py @@ -170,15 +170,15 @@ def get_jwt_value(self, request): return auth[1] + def jwks(self): + return JsonWebKey.import_key_set(self.jwks_data()) + @cache(ttl=api_settings.OIDC_JWKS_EXPIRATION_TIME) def jwks_data(self): r = request("GET", self.oidc_config['jwks_uri'], allow_redirects=True) r.raise_for_status() return r.json() - def jwks(self): - return JsonWebKey.import_key_set(self.jwks_data()) - @cached_property def issuer(self): return self.oidc_config['issuer'] From a29bebf195be8e6ae55b743ca59e4744f65014a6 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Wed, 16 Sep 2020 01:46:20 +0200 Subject: [PATCH 10/19] Don't do type hints to be backwards compatible. --- oidc_auth/authentication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oidc_auth/authentication.py b/oidc_auth/authentication.py index 281e137..e274063 100644 --- a/oidc_auth/authentication.py +++ b/oidc_auth/authentication.py @@ -183,7 +183,7 @@ def jwks_data(self): def issuer(self): return self.oidc_config['issuer'] - def decode_jwt(self, jwt_value: bytes): + def decode_jwt(self, jwt_value): try: id_token = jwt.decode( jwt_value.decode('ascii'), From 736b2ccb6975d615bd04eb081f35615c65c889a2 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Mon, 12 Oct 2020 22:31:12 +0200 Subject: [PATCH 11/19] Dropped python 2.7 support --- Pipfile | 6 +- Pipfile.lock | 202 +++++++++++++++++++++++++-------------------------- tox.ini | 8 +- 3 files changed, 105 insertions(+), 111 deletions(-) diff --git a/Pipfile b/Pipfile index 2d5158f..9d235b3 100644 --- a/Pipfile +++ b/Pipfile @@ -6,14 +6,12 @@ verify_ssl = true [dev-packages] autopep8 = "*" flake8 = "*" -tox = "<3.8.0" -tox-pipenv = "*" +tox = "*" [packages] django = "*" djangorestframework = "*" -# authlib = "*" -authlib = {editable = true, ref = "master", git = "https://github.com/lepture/authlib.git"} +authlib = "*" requests = "*" [requires] diff --git a/Pipfile.lock b/Pipfile.lock index 616de08..d827dda 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "05e25e0a486b9c6454bb89d88cd887e75dea658eab244fcbc8062a60af8fb451" + "sha256": "ffab26b98f6d64d91ff47685d21d0eb8599a8d78b75b3cb09039ec6c75514627" }, "pipfile-spec": 6, "requires": { @@ -25,9 +25,12 @@ "version": "==3.2.10" }, "authlib": { - "editable": true, - "git": "https://github.com/lepture/authlib.git", - "ref": "319eb60268e7f18f0d3e837bd3024d4c685d86fa" + "hashes": [ + "sha256:7b6b89287ce88a13ca35fd0de9841d17316ddb553661d044d8d576c0a77ee316", + "sha256:f107574d718764b9a88528e1ce29de439dedea7307400adc4fefe960ec78d1f2" + ], + "index": "pypi", + "version": "==0.15" }, "certifi": { "hashes": [ @@ -38,36 +41,44 @@ }, "cffi": { "hashes": [ - "sha256:0da50dcbccd7cb7e6c741ab7912b2eff48e85af217d72b57f80ebc616257125e", - "sha256:12a453e03124069b6896107ee133ae3ab04c624bb10683e1ed1c1663df17c13c", - "sha256:15419020b0e812b40d96ec9d369b2bc8109cc3295eac6e013d3261343580cc7e", - "sha256:15a5f59a4808f82d8ec7364cbace851df591c2d43bc76bcbe5c4543a7ddd1bf1", - "sha256:23e44937d7695c27c66a54d793dd4b45889a81b35c0751ba91040fe825ec59c4", - "sha256:29c4688ace466a365b85a51dcc5e3c853c1d283f293dfcc12f7a77e498f160d2", - "sha256:57214fa5430399dffd54f4be37b56fe22cedb2b98862550d43cc085fb698dc2c", - "sha256:577791f948d34d569acb2d1add5831731c59d5a0c50a6d9f629ae1cefd9ca4a0", - "sha256:6539314d84c4d36f28d73adc1b45e9f4ee2a89cdc7e5d2b0a6dbacba31906798", - "sha256:65867d63f0fd1b500fa343d7798fa64e9e681b594e0a07dc934c13e76ee28fb1", - "sha256:672b539db20fef6b03d6f7a14b5825d57c98e4026401fce838849f8de73fe4d4", - "sha256:6843db0343e12e3f52cc58430ad559d850a53684f5b352540ca3f1bc56df0731", - "sha256:7057613efefd36cacabbdbcef010e0a9c20a88fc07eb3e616019ea1692fa5df4", - "sha256:76ada88d62eb24de7051c5157a1a78fd853cca9b91c0713c2e973e4196271d0c", - "sha256:837398c2ec00228679513802e3744d1e8e3cb1204aa6ad408b6aff081e99a487", - "sha256:8662aabfeab00cea149a3d1c2999b0731e70c6b5bac596d95d13f643e76d3d4e", - "sha256:95e9094162fa712f18b4f60896e34b621df99147c2cee216cfa8f022294e8e9f", - "sha256:99cc66b33c418cd579c0f03b77b94263c305c389cb0c6972dac420f24b3bf123", - "sha256:9b219511d8b64d3fa14261963933be34028ea0e57455baf6781fe399c2c3206c", - "sha256:ae8f34d50af2c2154035984b8b5fc5d9ed63f32fe615646ab435b05b132ca91b", - "sha256:b9aa9d8818c2e917fa2c105ad538e222a5bce59777133840b93134022a7ce650", - "sha256:bf44a9a0141a082e89c90e8d785b212a872db793a0080c20f6ae6e2a0ebf82ad", - "sha256:c0b48b98d79cf795b0916c57bebbc6d16bb43b9fc9b8c9f57f4cf05881904c75", - "sha256:da9d3c506f43e220336433dffe643fbfa40096d408cb9b7f2477892f369d5f82", - "sha256:e4082d832e36e7f9b2278bc774886ca8207346b99f278e54c9de4834f17232f7", - "sha256:e4b9b7af398c32e408c00eb4e0d33ced2f9121fd9fb978e6c1b57edd014a7d15", - "sha256:e613514a82539fc48291d01933951a13ae93b6b444a88782480be32245ed4afa", - "sha256:f5033952def24172e60493b68717792e3aebb387a8d186c43c020d9363ee7281" - ], - "version": "==1.14.2" + "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d", + "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b", + "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4", + "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f", + "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3", + "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579", + "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537", + "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e", + "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05", + "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171", + "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca", + "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522", + "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c", + "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc", + "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d", + "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808", + "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828", + "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869", + "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d", + "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9", + "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0", + "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc", + "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15", + "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c", + "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a", + "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3", + "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1", + "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768", + "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d", + "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b", + "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e", + "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d", + "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730", + "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394", + "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1", + "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591" + ], + "version": "==1.14.3" }, "chardet": { "hashes": [ @@ -78,47 +89,47 @@ }, "cryptography": { "hashes": [ - "sha256:10c9775a3f31610cf6b694d1fe598f2183441de81cedcf1814451ae53d71b13a", - "sha256:180c9f855a8ea280e72a5d61cf05681b230c2dce804c48e9b2983f491ecc44ed", - "sha256:247df238bc05c7d2e934a761243bfdc67db03f339948b1e2e80c75d41fc7cc36", - "sha256:26409a473cc6278e4c90f782cd5968ebad04d3911ed1c402fc86908c17633e08", - "sha256:2a27615c965173c4c88f2961cf18115c08fedfb8bdc121347f26e8458dc6d237", - "sha256:2e26223ac636ca216e855748e7d435a1bf846809ed12ed898179587d0cf74618", - "sha256:321761d55fb7cb256b771ee4ed78e69486a7336be9143b90c52be59d7657f50f", - "sha256:4005b38cd86fc51c955db40b0f0e52ff65340874495af72efabb1bb8ca881695", - "sha256:4b9e96543d0784acebb70991ebc2dbd99aa287f6217546bb993df22dd361d41c", - "sha256:548b0818e88792318dc137d8b1ec82a0ab0af96c7f0603a00bb94f896fbf5e10", - "sha256:725875681afe50b41aee7fdd629cedbc4720bab350142b12c55c0a4d17c7416c", - "sha256:7a63e97355f3cd77c94bd98c59cb85fe0efd76ea7ef904c9b0316b5bbfde6ed1", - "sha256:94191501e4b4009642be21dde2a78bd3c2701a81ee57d3d3d02f1d99f8b64a9e", - "sha256:969ae512a250f869c1738ca63be843488ff5cc031987d302c1f59c7dbe1b225f", - "sha256:9f734423eb9c2ea85000aa2476e0d7a58e021bc34f0a373ac52a5454cd52f791", - "sha256:b45ab1c6ece7c471f01c56f5d19818ca797c34541f0b2351635a5c9fe09ac2e0", - "sha256:cc6096c86ec0de26e2263c228fb25ee01c3ff1346d3cfc219d67d49f303585af", - "sha256:dc3f437ca6353979aace181f1b790f0fc79e446235b14306241633ab7d61b8f8", - "sha256:e7563eb7bc5c7e75a213281715155248cceba88b11cb4b22957ad45b85903761", - "sha256:e7dad66a9e5684a40f270bd4aee1906878193ae50a4831922e454a2a457f1716", - "sha256:eb80a288e3cfc08f679f95da72d2ef90cb74f6d8a8ba69d2f215c5e110b2ca32", - "sha256:fa7fbcc40e2210aca26c7ac8a39467eae444d90a2c346cbcffd9133a166bcc67" + "sha256:21b47c59fcb1c36f1113f3709d37935368e34815ea1d7073862e92f810dc7499", + "sha256:451cdf60be4dafb6a3b78802006a020e6cd709c22d240f94f7a0696240a17154", + "sha256:4549b137d8cbe3c2eadfa56c0c858b78acbeff956bd461e40000b2164d9167c6", + "sha256:48ee615a779ffa749d7d50c291761dc921d93d7cf203dca2db663b4f193f0e49", + "sha256:559d622aef2a2dff98a892eef321433ba5bc55b2485220a8ca289c1ecc2bd54f", + "sha256:5d52c72449bb02dd45a773a203196e6d4fae34e158769c896012401f33064396", + "sha256:65beb15e7f9c16e15934569d29fb4def74ea1469d8781f6b3507ab896d6d8719", + "sha256:680da076cad81cdf5ffcac50c477b6790be81768d30f9da9e01960c4b18a66db", + "sha256:762bc5a0df03c51ee3f09c621e1cee64e3a079a2b5020de82f1613873d79ee70", + "sha256:89aceb31cd5f9fc2449fe8cf3810797ca52b65f1489002d58fe190bfb265c536", + "sha256:983c0c3de4cb9fcba68fd3f45ed846eb86a2a8b8d8bc5bb18364c4d00b3c61fe", + "sha256:99d4984aabd4c7182050bca76176ce2dbc9fa9748afe583a7865c12954d714ba", + "sha256:9d9fc6a16357965d282dd4ab6531013935425d0dc4950df2e0cf2a1b1ac1017d", + "sha256:a7597ffc67987b37b12e09c029bd1dc43965f75d328076ae85721b84046e9ca7", + "sha256:ab010e461bb6b444eaf7f8c813bb716be2d78ab786103f9608ffd37a4bd7d490", + "sha256:b12e715c10a13ca1bd27fbceed9adc8c5ff640f8e1f7ea76416352de703523c8", + "sha256:b2bded09c578d19e08bd2c5bb8fed7f103e089752c9cf7ca7ca7de522326e921", + "sha256:b372026ebf32fe2523159f27d9f0e9f485092e43b00a5adacf732192a70ba118", + "sha256:cb179acdd4ae1e4a5a160d80b87841b3d0e0be84af46c7bb2cd7ece57a39c4ba", + "sha256:e97a3b627e3cb63c415a16245d6cef2139cca18bb1183d1b9375a1c14e83f3b3", + "sha256:f0e099fc4cc697450c3dd4031791559692dd941a95254cb9aeded66a7aa8b9bc", + "sha256:f99317a0fa2e49917689b8cf977510addcfaaab769b3f899b9c481bbd76730c2" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==3.1" + "version": "==3.1.1" }, "django": { "hashes": [ - "sha256:59c8125ca873ed3bdae9c12b146fbbd6ed8d0f743e4cf5f5817af50c51f1fc2f", - "sha256:b5fbb818e751f660fa2d576d9f40c34a4c615c8b48dd383f5216e609f383371f" + "sha256:a2127ad0150ec6966655bedf15dbbff9697cc86d61653db2da1afa506c0b04cc", + "sha256:c93c28ccf1d094cbd00d860e83128a39e45d2c571d3b54361713aaaf9a94cac4" ], "index": "pypi", - "version": "==3.1.1" + "version": "==3.1.2" }, "djangorestframework": { "hashes": [ - "sha256:6dd02d5a4bd2516fb93f80360673bf540c3b6641fec8766b1da2870a5aa00b32", - "sha256:8b1ac62c581dbc5799b03e535854b92fc4053ecfe74bad3f9c05782063d4196b" + "sha256:5c5071fcbad6dce16f566d492015c829ddb0df42965d488b878594aabc3aed21", + "sha256:d54452aedebb4b650254ca092f9f4f5df947cb1de6ab245d817b08b4f4156249" ], "index": "pypi", - "version": "==3.11.1" + "version": "==3.12.1" }, "idna": { "hashes": [ @@ -161,11 +172,11 @@ }, "sqlparse": { "hashes": [ - "sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e", - "sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548" + "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", + "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.3.1" + "markers": "python_version >= '3.5'", + "version": "==0.4.1" }, "urllib3": { "hashes": [ @@ -191,13 +202,6 @@ "index": "pypi", "version": "==1.5.4" }, - "certifi": { - "hashes": [ - "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", - "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" - ], - "version": "==2020.6.20" - }, "distlib": { "hashes": [ "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb", @@ -214,11 +218,11 @@ }, "flake8": { "hashes": [ - "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c", - "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208" + "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839", + "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b" ], "index": "pypi", - "version": "==3.8.3" + "version": "==3.8.4" }, "mccabe": { "hashes": [ @@ -227,13 +231,13 @@ ], "version": "==0.6.1" }, - "pipenv": { + "packaging": { "hashes": [ - "sha256:448ac3a36443db633d52a2359cac15ecbc4f429eab4ddd420697602b721d1c5a", - "sha256:eff0e10eadb330f612edfa5051d3d8e775e9e0e918c3c50361da703bd0daa035" + "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8", + "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2020.8.13" + "version": "==20.4" }, "pluggy": { "hashes": [ @@ -267,6 +271,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.2.0" }, + "pyparsing": { + "hashes": [ + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.4.7" + }, "six": { "hashes": [ "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", @@ -284,35 +296,19 @@ }, "tox": { "hashes": [ - "sha256:04f8f1aa05de8e76d7a266ccd14e0d665d429977cd42123bc38efa9b59964e9e", - "sha256:25ef928babe88c71e3ed3af0c464d1160b01fca2dd1870a5bb26c2dea61a17fc" + "sha256:42ce19ce5dc2f6d6b1fdc5666c476e1f1e2897359b47e0aa3a5b774f335d57c2", + "sha256:4321052bfe28f9d85082341ca8e233e3ea901fdd14dab8a5d3fbd810269fbaf6" ], "index": "pypi", - "version": "==3.7.0" - }, - "tox-pipenv": { - "hashes": [ - "sha256:23c44c787e6ab2f2b00e9a68820dde677fd93345d74edbd27cc19bc035a99b18", - "sha256:4d3207954d97f52abe8835d8538916f4d34975de2329d0cdef611cfb93355542" - ], - "index": "pypi", - "version": "==1.10.0" + "version": "==3.20.1" }, "virtualenv": { "hashes": [ - "sha256:43add625c53c596d38f971a465553f6318decc39d98512bc100fa1b1e839c8dc", - "sha256:e0305af10299a7fb0d69393d8f04cb2965dda9351140d11ac8db4e5e3970451b" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.0.31" - }, - "virtualenv-clone": { - "hashes": [ - "sha256:07e74418b7cc64f4fda987bf5bc71ebd59af27a7bc9e8a8ee9fd54b1f2390a27", - "sha256:665e48dd54c84b98b71a657acb49104c54e7652bce9c1c4f6c6976ed4c827a29" + "sha256:35ecdeb58cfc2147bb0706f7cdef69a8f34f1b81b6d49568174e277932908b8f", + "sha256:a5e0d253fe138097c6559c906c528647254f437d1019af9d5a477b09bfa7300f" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.5.4" + "version": "==20.0.33" } } } diff --git a/tox.ini b/tox.ini index 75250cd..5fc9375 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,12 @@ [tox] envlist = - {py27,py34}-django18-drf{32,33,34,35,36} + {py34}-django18-drf{32,33,34,35,36} {py35}-django18-drf{35,36} - {py27,py34}-django19-drf{33,34,35,36} + {py34}-django19-drf{33,34,35,36} {py35}-django19-drf{35,36} - {py27,py34}-django110-drf{34,35,36,37,38} + {py34}-django110-drf{34,35,36,37,38} {py35}-django110-drf{35,36,37,38} - {py27,py34}-django111-drf{34,35,36,37,38,39} + {py34}-django111-drf{34,35,36,37,38,39} {py35,py36}-django111-drf{35,36,37,38,39} {py37}-django111-drf{39} {py34,py35,py36}-django20-drf{37,38,39} From b80fcd2647cb0c7a7c6374b25d10be428d2e21d2 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Mon, 12 Oct 2020 22:36:14 +0200 Subject: [PATCH 12/19] Resetted to 2.7 suppor --- tox.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tox.ini b/tox.ini index 5fc9375..75250cd 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,12 @@ [tox] envlist = - {py34}-django18-drf{32,33,34,35,36} + {py27,py34}-django18-drf{32,33,34,35,36} {py35}-django18-drf{35,36} - {py34}-django19-drf{33,34,35,36} + {py27,py34}-django19-drf{33,34,35,36} {py35}-django19-drf{35,36} - {py34}-django110-drf{34,35,36,37,38} + {py27,py34}-django110-drf{34,35,36,37,38} {py35}-django110-drf{35,36,37,38} - {py34}-django111-drf{34,35,36,37,38,39} + {py27,py34}-django111-drf{34,35,36,37,38,39} {py35,py36}-django111-drf{35,36,37,38,39} {py37}-django111-drf{39} {py34,py35,py36}-django20-drf{37,38,39} From d84c373bb5da11b9a03129af8f880e99fb1c75d9 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Wed, 14 Oct 2020 22:30:49 +0200 Subject: [PATCH 13/19] Fixed some minor bugs in testing. --- oidc_auth/authentication.py | 44 ++++++++++++------------------------ oidc_auth/settings.py | 11 +++++++-- tests/settings.py | 6 ++++- tests/test_authentication.py | 7 ++++-- 4 files changed, 33 insertions(+), 35 deletions(-) diff --git a/oidc_auth/authentication.py b/oidc_auth/authentication.py index e274063..964ad50 100644 --- a/oidc_auth/authentication.py +++ b/oidc_auth/authentication.py @@ -2,6 +2,11 @@ import time import requests +from authlib.jose import JsonWebKey, jwt +from authlib.jose.errors import (BadSignatureError, DecodeError, + ExpiredTokenError, JoseError) +from authlib.oidc.core.claims import IDToken +from authlib.oidc.discovery import get_well_known_url from django.contrib.auth import get_user_model from django.utils.encoding import smart_str from django.utils.functional import cached_property @@ -12,16 +17,11 @@ get_authorization_header) from rest_framework.exceptions import AuthenticationFailed -from authlib.jose import JsonWebKey, jwt -from authlib.jose.errors import (BadSignatureError, DecodeError, - ExpiredTokenError, JoseError) -from authlib.oidc.core.claims import IDToken -from authlib.oidc.discovery import get_well_known_url - from .settings import api_settings from .util import cache -logger = logging.Logger(__name__) +logging.basicConfig() +logger = logging.getLogger(__name__) def get_user_by_id(request, id_token): @@ -37,27 +37,17 @@ def get_user_by_id(request, id_token): class DRFIDToken(IDToken): def validate_exp(self, now, leeway): - super().validate_exp(now, leeway) + super(DRFIDToken, self).validate_exp(now, leeway) if now > self['exp']: msg = _('Invalid Authorization header. JWT has expired.') raise AuthenticationFailed(msg) def validate_iat(self, now, leeway): - super().validate_iat(now, leeway) + super(DRFIDToken, self).validate_iat(now, leeway) if self['iat'] < leeway: msg = _('Invalid Authorization header. JWT too old.') raise AuthenticationFailed(msg) - def validate_aud(self): - super().validate_aud() - if isinstance(self['aud'], str): - self['aud'] = [self['aud']] - if len(self['aud']) > 1 and 'azp' not in self: - msg = _('Invalid Authorization header. Missing JWT authorized party.') - raise AuthenticationFailed(msg) - else: - super().validate_azp() - class BaseOidcAuthentication(BaseAuthentication): @property @@ -124,23 +114,17 @@ class JSONWebTokenAuthentication(BaseOidcAuthentication): www_authenticate_realm = 'api' - @property - def audiences(self): - return [audience for audience in api_settings.OIDC_AUDIENCES] - @property def claims_options(self): - return { + _claims_options = { 'iss': { 'essential': True, - 'value': self.issuer - }, - 'aud': { - 'values': self.audiences + 'values': [self.issuer] } - - } + for key, value in api_settings.OIDC_CLAIMS_OPTIONS.items(): + _claims_options[key] = value + return _claims_options def authenticate(self, request): jwt_value = self.get_jwt_value(request) diff --git a/oidc_auth/settings.py b/oidc_auth/settings.py index 0d3e3d0..9f1c361 100644 --- a/oidc_auth/settings.py +++ b/oidc_auth/settings.py @@ -5,8 +5,13 @@ DEFAULTS = { 'OIDC_ENDPOINT': None, - 'OIDC_ENDPOINTS': {}, - 'OIDC_AUDIENCES': None, + + # Currently unimplemented + 'OIDC_ENDPOINTS': [], + + # The Claims Options can now be defined by a static string. + # ref: https://docs.authlib.org/en/latest/jose/jwt.html#jwt-payload-claims-validation + 'OIDC_CLAIMS_OPTIONS': {}, # Number of seconds in the past valid tokens can be issued 'OIDC_LEEWAY': 600, @@ -26,6 +31,8 @@ # The Django cache to use 'OIDC_CACHE_NAME': 'default', 'OIDC_CACHE_PREFIX': 'oidc_auth.' + + } # List of settings that may be in string import notation. diff --git a/tests/settings.py b/tests/settings.py index 292ab08..94d7fc1 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -12,5 +12,9 @@ ROOT_URLCONF = 'tests.test_authentication' OIDC_AUTH = { 'OIDC_ENDPOINT': 'http://example.com', - 'OIDC_AUDIENCES': ('you',), + 'OIDC_CLAIMS_OPTIONS': { + 'aud': { + 'values': ['you'], + } + }, } diff --git a/tests/test_authentication.py b/tests/test_authentication.py index 189afe9..ed347bc 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -16,6 +16,9 @@ if sys.version_info > (3,): long = int +else: + class ConnectionError(OSError): + pass try: from unittest.mock import patch, Mock except ImportError: @@ -176,8 +179,8 @@ class TestJWTAuthentication(AuthenticationTestCase): def test_using_valid_jwt(self): auth = 'JWT ' + make_id_token(self.user.username) resp = self.client.get('/test/', HTTP_AUTHORIZATION=auth) - self.assertEqual(resp.content.decode(), 'a') self.assertEqual(resp.status_code, 200) + self.assertEqual(resp.content.decode(), 'a') def test_without_jwt(self): resp = self.client.get('/test/') @@ -227,7 +230,7 @@ def test_with_unknown_subject(self): def test_with_multiple_audiences(self): auth = 'JWT ' + make_id_token(self.user.username, aud=['you', 'me']) resp = self.client.get('/test/', HTTP_AUTHORIZATION=auth) - self.assertEqual(resp.status_code, 401) + self.assertEqual(resp.status_code, 200) def test_with_invalid_multiple_audiences(self): auth = 'JWT ' + make_id_token(self.user.username, aud=['we', 'me']) From e0dc886038132f17bff5dbaa35cde2737b6f8370 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Tue, 10 Nov 2020 09:41:17 +0100 Subject: [PATCH 14/19] Added a non complaining version of cryptography --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4858509..9e65c14 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='drf-oidc-auth', - version='0.11.0', + version='1.0.0', packages=['oidc_auth'], url='https://github.com/ByteInternet/drf-oidc-auth', license='MIT', @@ -11,6 +11,7 @@ description='OpenID Connect authentication for Django Rest Framework', install_requires=[ 'authlib>=0.15.0', + 'cryptography>=2.6', 'django>=1.8.0', 'djangorestframework>=3.0.0', 'requests>=2.20.0' From 09b60e66ca2fed16597c98547b2b9a2880df52a8 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Tue, 10 Nov 2020 09:45:18 +0100 Subject: [PATCH 15/19] Added proper documentation in the README.md --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b6b3e14..dad89c5 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,14 @@ OIDC_AUTH = { # at /.well-known/openid-configuration 'OIDC_ENDPOINT': 'https://accounts.google.com', - # Accepted audiences the ID Tokens can be issued to - 'OIDC_AUDIENCES': ('myapp',), + # The Claims Options can now be defined by a static string. + # ref: https://docs.authlib.org/en/latest/jose/jwt.html#jwt-payload-claims-validation + # The old OIDC_AUDIENCES option is removed in favor of the new option. + 'OIDC_CLAIMS_OPTIONS': { + 'aud': { + 'values': ['myapp'], + } + }, # (Optional) Function that resolves id_token into user. # This function receives a request and an id_token dict and expects to From 85bccf900f0f860bac53d9df39ed61cbe7871ffd Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Tue, 10 Nov 2020 09:45:34 +0100 Subject: [PATCH 16/19] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dad89c5..912fb4a 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ OIDC_AUTH = { # The Claims Options can now be defined by a static string. # ref: https://docs.authlib.org/en/latest/jose/jwt.html#jwt-payload-claims-validation - # The old OIDC_AUDIENCES option is removed in favor of the new option. + # The old OIDC_AUDIENCES option is removed in favor of this new option. 'OIDC_CLAIMS_OPTIONS': { 'aud': { 'values': ['myapp'], From e09d63faa64670a6999c5ea5cee3706394d2ca6b Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Tue, 10 Nov 2020 09:49:38 +0100 Subject: [PATCH 17/19] Added documentation of how to do essential --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 912fb4a..397799c 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,11 @@ OIDC_AUTH = { # The Claims Options can now be defined by a static string. # ref: https://docs.authlib.org/en/latest/jose/jwt.html#jwt-payload-claims-validation # The old OIDC_AUDIENCES option is removed in favor of this new option. + # `aud` is only required, when you set it as required. 'OIDC_CLAIMS_OPTIONS': { 'aud': { 'values': ['myapp'], + 'required': True, } }, From f5d3efd890eac65e84ad9e0e132eb3477ce2e97a Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Tue, 10 Nov 2020 09:50:45 +0100 Subject: [PATCH 18/19] Added essential to the tests --- README.md | 4 ++-- tests/settings.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 397799c..a5712d9 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,11 @@ OIDC_AUTH = { # The Claims Options can now be defined by a static string. # ref: https://docs.authlib.org/en/latest/jose/jwt.html#jwt-payload-claims-validation # The old OIDC_AUDIENCES option is removed in favor of this new option. - # `aud` is only required, when you set it as required. + # `aud` is only required, when you set it as an essential claim. 'OIDC_CLAIMS_OPTIONS': { 'aud': { 'values': ['myapp'], - 'required': True, + 'essential': True, } }, diff --git a/tests/settings.py b/tests/settings.py index 94d7fc1..f51b424 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -15,6 +15,7 @@ 'OIDC_CLAIMS_OPTIONS': { 'aud': { 'values': ['you'], + 'essential': True, } }, } From 23b1798d381f75d3d882995c2b7c9fed0f57fd73 Mon Sep 17 00:00:00 2001 From: Cellebyte Date: Thu, 19 Nov 2020 23:12:01 +0100 Subject: [PATCH 19/19] Make oidc_auth essential --- oidc_auth/settings.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/oidc_auth/settings.py b/oidc_auth/settings.py index 9f1c361..a8f134a 100644 --- a/oidc_auth/settings.py +++ b/oidc_auth/settings.py @@ -11,7 +11,11 @@ # The Claims Options can now be defined by a static string. # ref: https://docs.authlib.org/en/latest/jose/jwt.html#jwt-payload-claims-validation - 'OIDC_CLAIMS_OPTIONS': {}, + 'OIDC_CLAIMS_OPTIONS': { + 'aud': { + 'essential': True, + } + }, # Number of seconds in the past valid tokens can be issued 'OIDC_LEEWAY': 600,