diff --git a/oauth2_provider/admin.py b/oauth2_provider/admin.py index d529f5073..a8d69e623 100644 --- a/oauth2_provider/admin.py +++ b/oauth2_provider/admin.py @@ -1,11 +1,8 @@ from django.contrib import admin from .models import ( - get_access_token_model, - get_application_model, - get_grant_model, - get_refresh_token_model, - get_id_token_model, + get_access_token_model, get_application_model, + get_grant_model, get_id_token_model, get_refresh_token_model ) diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index 0bdb5294a..1421c89eb 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -1,10 +1,8 @@ -import logging import json +import logging from datetime import timedelta from urllib.parse import parse_qsl, urlparse -from jwcrypto import jwk, jwt - from django.apps import apps from django.conf import settings from django.core.exceptions import ImproperlyConfigured @@ -12,6 +10,7 @@ from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from jwcrypto import jwk, jwt from .generators import generate_client_id, generate_client_secret from .scopes import get_scopes_backend diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 36a2e3166..f6ede19ac 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -1,7 +1,7 @@ import base64 import binascii -import json import hashlib +import json import logging from collections import OrderedDict from datetime import datetime, timedelta @@ -16,21 +16,16 @@ from django.utils import dateformat, timezone from django.utils.timezone import make_aware from django.utils.translation import gettext_lazy as _ -from oauthlib.oauth2 import RequestValidator -from oauthlib.oauth2.rfc6749 import utils - -from jwcrypto.common import JWException from jwcrypto import jwk, jwt +from jwcrypto.common import JWException from jwcrypto.jwt import JWTExpired +from oauthlib.oauth2 import RequestValidator +from oauthlib.oauth2.rfc6749 import utils from .exceptions import FatalClientError from .models import ( - AbstractApplication, - get_access_token_model, - get_id_token_model, - get_application_model, - get_grant_model, - get_refresh_token_model, + AbstractApplication, get_access_token_model, get_application_model, + get_grant_model, get_id_token_model, get_refresh_token_model ) from .scopes import get_scopes_backend from .settings import oauth2_settings @@ -214,7 +209,7 @@ def _set_oauth2_error_on_request(self, request, access_token, scopes): ) else: log.warning("OAuth2 access token is invalid for an unknown reason.") - error = OrderedDict([("error", "invalid_token",),]) + error = OrderedDict([("error", "invalid_token",), ]) request.oauth2_error = error return request @@ -793,7 +788,7 @@ def get_id_token(self, token, token_handler, request): # http://openid.net/specs/openid-connect-core-1_0.html#ImplicitIDToken # if request.grant_type in 'authorization_code' and 'access_token' in token: if ( - (request.grant_type is "authorization_code" and "access_token" in token) + (request.grant_type == "authorization_code" and "access_token" in token) or request.response_type == "code id_token token" or (request.response_type == "id_token token" and "access_token" in token) ): @@ -876,4 +871,4 @@ def get_authorization_code_nonce(self, client_id, code, redirect_uri, request): - Authorization Token Grant Dispatcher """ # TODO: Fix this ;) - return "" + return "" diff --git a/oauth2_provider/urls.py b/oauth2_provider/urls.py index 333f11933..4baef4704 100644 --- a/oauth2_provider/urls.py +++ b/oauth2_provider/urls.py @@ -28,7 +28,8 @@ ] oidc_urlpatterns = [ - url(r"^\.well-known/openid-configuration/$", views.ConnectDiscoveryInfoView.as_view(), name="oidc-connect-discovery-info"), + url(r"^\.well-known/openid-configuration/$", views.ConnectDiscoveryInfoView.as_view(), + name="oidc-connect-discovery-info"), url(r"^jwks/$", views.JwksInfoView.as_view(), name="jwks-info"), url(r"^userinfo/$", views.UserInfoView.as_view(), name="user-info") ] diff --git a/oauth2_provider/views/oidc.py b/oauth2_provider/views/oidc.py index 7f3c9d5d4..732965a5d 100644 --- a/oauth2_provider/views/oidc.py +++ b/oauth2_provider/views/oidc.py @@ -5,10 +5,8 @@ from django.http import JsonResponse from django.urls import reverse_lazy from django.views.generic import View - -from rest_framework.views import APIView - from jwcrypto import jwk +from rest_framework.views import APIView from ..settings import oauth2_settings @@ -27,8 +25,10 @@ def get(self, request, *args, **kwargs): "jwks_uri": "{}{}".format(issuer_url, reverse_lazy("oauth2_provider:jwks-info")), "response_types_supported": oauth2_settings.OIDC_RESPONSE_TYPES_SUPPORTED, "subject_types_supported": oauth2_settings.OIDC_SUBJECT_TYPES_SUPPORTED, - "id_token_signing_alg_values_supported": oauth2_settings.OIDC_ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, - "token_endpoint_auth_methods_supported": oauth2_settings.OIDC_TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, + "id_token_signing_alg_values_supported": + oauth2_settings.OIDC_ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, + "token_endpoint_auth_methods_supported": + oauth2_settings.OIDC_TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, } response = JsonResponse(data) response["Access-Control-Allow-Origin"] = "*" diff --git a/tests/settings.py b/tests/settings.py index 19f95e31f..edd1ae679 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -131,13 +131,29 @@ } } +OIDC_RSA_PRIVATE_KEY = """-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCbCYh5h2NmQuBqVO6G+/CO+cHm9VBzsb0MeA6bbQfDnbhstVOT +j0hcnZJzDjYc6ajBZZf6gxVP9xrdm9Uh599VI3X5PFXLbMHrmzTAMzCGIyg+/fnP +0gocYxmCX2+XKyj/Zvt1pUX8VAN2AhrJSfxNDKUHERTVEV9bRBJg4F0C3wIDAQAB +AoGAP+i4nNw+Ec/8oWh8YSFm4xE6qKG0NdTtSMAOyWwy+KTB+vHuT1QPsLn1vj77 ++IQrX/moogg6F1oV9YdA3vat3U7rwt1sBGsRrLhA+Spp9WEQtglguNo4+QfVo2ju +YBa2rG+h75qjiA3xnU//F3rvwnAsOWv0NUVdVeguyR+u6okCQQDBUmgWeH2WHmUn +2nLNCz+9wj28rqhfOr9Ptem2gqk+ywJmuIr4Y5S1OdavOr2UZxOcEwncJ/MLVYQq +MH+x4V5HAkEAzU2GMR5OdVLcxfVTjzuIC76paoHVWnLibd1cdANpPmE6SM+pf5el +fVSwuH9Fmlizu8GiPCxbJUoXB/J1tGEKqQJBALhClEU+qOzpoZ6/voYi/6kdN3zc +uEy0EN6n09AKb8gS9QH1STgAqh+ltjMkeMe3C2DKYK5/QU9/Pc58lWl1FkcCQG67 +ZamQgxjcvJ85FvymS1aqW45KwNysIlzHjFo2jMlMf7dN6kobbPMQftDENLJvLWIT +qoFyGycdsxZiPAIyZSECQQCZFn3Dl6hnJxWZH8Fsa9hj79kZ/WVkIXGmtdgt0fNr +dTnvCVtA59ne4LEVie/PMH/odQWY0SxVm/76uBZv/1vY +-----END RSA PRIVATE KEY-----""" + OAUTH2_PROVIDER = { "OIDC_ISS_ENDPOINT": "http://localhost", "OIDC_USERINFO_ENDPOINT": "http://localhost/userinfo/", - "OIDC_RSA_PRIVATE_KEY": "-----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQCbCYh5h2NmQuBqVO6G+/CO+cHm9VBzsb0MeA6bbQfDnbhstVOT\nj0hcnZJzDjYc6ajBZZf6gxVP9xrdm9Uh599VI3X5PFXLbMHrmzTAMzCGIyg+/fnP\n0gocYxmCX2+XKyj/Zvt1pUX8VAN2AhrJSfxNDKUHERTVEV9bRBJg4F0C3wIDAQAB\nAoGAP+i4nNw+Ec/8oWh8YSFm4xE6qKG0NdTtSMAOyWwy+KTB+vHuT1QPsLn1vj77\n+IQrX/moogg6F1oV9YdA3vat3U7rwt1sBGsRrLhA+Spp9WEQtglguNo4+QfVo2ju\nYBa2rG+h75qjiA3xnU//F3rvwnAsOWv0NUVdVeguyR+u6okCQQDBUmgWeH2WHmUn\n2nLNCz+9wj28rqhfOr9Ptem2gqk+ywJmuIr4Y5S1OdavOr2UZxOcEwncJ/MLVYQq\nMH+x4V5HAkEAzU2GMR5OdVLcxfVTjzuIC76paoHVWnLibd1cdANpPmE6SM+pf5el\nfVSwuH9Fmlizu8GiPCxbJUoXB/J1tGEKqQJBALhClEU+qOzpoZ6/voYi/6kdN3zc\nuEy0EN6n09AKb8gS9QH1STgAqh+ltjMkeMe3C2DKYK5/QU9/Pc58lWl1FkcCQG67\nZamQgxjcvJ85FvymS1aqW45KwNysIlzHjFo2jMlMf7dN6kobbPMQftDENLJvLWIT\nqoFyGycdsxZiPAIyZSECQQCZFn3Dl6hnJxWZH8Fsa9hj79kZ/WVkIXGmtdgt0fNr\ndTnvCVtA59ne4LEVie/PMH/odQWY0SxVm/76uBZv/1vY\n-----END RSA PRIVATE KEY-----" + "OIDC_RSA_PRIVATE_KEY": OIDC_RSA_PRIVATE_KEY, } -OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = 'oauth2_provider.AccessToken' -OAUTH2_PROVIDER_APPLICATION_MODEL = 'oauth2_provider.Application' -OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = 'oauth2_provider.RefreshToken' -OAUTH2_PROVIDER_ID_TOKEN_MODEL = 'oauth2_provider.IDToken' +OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = "oauth2_provider.AccessToken" +OAUTH2_PROVIDER_APPLICATION_MODEL = "oauth2_provider.Application" +OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "oauth2_provider.RefreshToken" +OAUTH2_PROVIDER_ID_TOKEN_MODEL = "oauth2_provider.IDToken" diff --git a/tests/test_authorization_code.py b/tests/test_authorization_code.py index 19b92d4cf..0c6c71705 100644 --- a/tests/test_authorization_code.py +++ b/tests/test_authorization_code.py @@ -13,10 +13,8 @@ from oauthlib.oauth2.rfc6749 import errors as oauthlib_errors from oauth2_provider.models import ( - get_access_token_model, - get_application_model, - get_grant_model, - get_refresh_token_model, + get_access_token_model, get_application_model, + get_grant_model, get_refresh_token_model ) from oauth2_provider.settings import oauth2_settings from oauth2_provider.views import ProtectedResourceView @@ -159,7 +157,7 @@ def test_pre_auth_invalid_client(self): self.client.login(username="test_user", password="123456") query_string = urlencode( - {"client_id": "fakeclientid", "response_type": "code",} + {"client_id": "fakeclientid", "response_type": "code", } ) url = "{url}?{qs}".format( url=reverse("oauth2_provider:authorize"), qs=query_string @@ -355,7 +353,7 @@ def test_pre_auth_default_redirect(self): self.client.login(username="test_user", password="123456") query_string = urlencode( - {"client_id": self.application.client_id, "response_type": "code",} + {"client_id": self.application.client_id, "response_type": "code", } ) url = "{url}?{qs}".format( url=reverse("oauth2_provider:authorize"), qs=query_string @@ -394,7 +392,7 @@ def test_pre_auth_wrong_response_type(self): self.client.login(username="test_user", password="123456") query_string = urlencode( - {"client_id": self.application.client_id, "response_type": "WRONG",} + {"client_id": self.application.client_id, "response_type": "WRONG", } ) url = "{url}?{qs}".format( url=reverse("oauth2_provider:authorize"), qs=query_string @@ -1860,7 +1858,6 @@ def test_id_token_code_exchange_succeed_when_redirect_uri_match_with_multiple_qu content["expires_in"], oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS ) - def test_oob_as_html(self): """ Test out-of-band authentication. @@ -1954,7 +1951,6 @@ def test_oob_as_json(self): ) - class TestAuthorizationCodeProtectedResource(BaseTest): def test_resource_access_allowed(self): self.client.login(username="test_user", password="123456") diff --git a/tests/test_hybrid.py b/tests/test_hybrid.py index da3d0b5e6..1f45aeeec 100644 --- a/tests/test_hybrid.py +++ b/tests/test_hybrid.py @@ -1,7 +1,6 @@ import base64 import datetime import json - from urllib.parse import parse_qs, urlencode, urlparse from django.contrib.auth import get_user_model @@ -731,7 +730,9 @@ def test_code_post_auth_failing_redirection_uri_with_querystring(self): response = self.client.post(reverse("oauth2_provider:authorize"), data=form_data) self.assertEqual(response.status_code, 302) - self.assertEqual("http://example.com?foo=bar&error=access_denied&state=random_state_string", response["Location"]) + self.assertEqual( + "http://example.com?foo=bar&error=access_denied&state=random_state_string", response["Location"] + ) def test_code_post_auth_fails_when_redirect_uri_path_is_invalid(self): """ diff --git a/tests/test_implicit.py b/tests/test_implicit.py index a0abb73b8..4e8879a9e 100644 --- a/tests/test_implicit.py +++ b/tests/test_implicit.py @@ -1,11 +1,9 @@ -from urllib.parse import parse_qs, urlencode, urlparse - import json +from urllib.parse import parse_qs, urlencode, urlparse from django.contrib.auth import get_user_model from django.test import RequestFactory, TestCase from django.urls import reverse - from jwcrypto import jwk, jwt from oauth2_provider.models import get_application_model diff --git a/tests/test_oauth2_backends.py b/tests/test_oauth2_backends.py index 2381e9cdc..0d98dad8b 100644 --- a/tests/test_oauth2_backends.py +++ b/tests/test_oauth2_backends.py @@ -65,7 +65,9 @@ def test_create_token_response_gets_extra_credentials(self): payload = "grant_type=password&username=john&password=123456" request = self.factory.post("/o/token/", payload, content_type="application/x-www-form-urlencoded") - with mock.patch("oauthlib.openid.connect.core.endpoints.pre_configured.Server.create_token_response") as create_token_response: + with mock.patch( + "oauthlib.openid.connect.core.endpoints.pre_configured.Server.create_token_response" + ) as create_token_response: mocked = mock.MagicMock() create_token_response.return_value = mocked, mocked, mocked core = self.MyOAuthLibCore() diff --git a/tests/test_oidc_views.py b/tests/test_oidc_views.py index 2105b4fd0..43e46d297 100644 --- a/tests/test_oidc_views.py +++ b/tests/test_oidc_views.py @@ -39,7 +39,7 @@ def test_get_jwks_info(self): "kid": "s4a1o8mFEd1tATAIH96caMlu4hOxzBUaI2QTqbYNBHs", "e": "AQAB", "kty": "RSA", - "n": "mwmIeYdjZkLgalTuhvvwjvnB5vVQc7G9DHgOm20Hw524bLVTk49IXJ2Scw42HOmowWWX-oMVT_ca3ZvVIeffVSN1-TxVy2zB65s0wDMwhiMoPv35z9IKHGMZgl9vlyso_2b7daVF_FQDdgIayUn8TQylBxEU1RFfW0QSYOBdAt8" + "n": "mwmIeYdjZkLgalTuhvvwjvnB5vVQc7G9DHgOm20Hw524bLVTk49IXJ2Scw42HOmowWWX-oMVT_ca3ZvVIeffVSN1-TxVy2zB65s0wDMwhiMoPv35z9IKHGMZgl9vlyso_2b7daVF_FQDdgIayUn8TQylBxEU1RFfW0QSYOBdAt8" # noqa }] } response = self.client.get(reverse("oauth2_provider:jwks-info")) diff --git a/tox.ini b/tox.ini index d2c43da4a..7d2de237e 100644 --- a/tox.ini +++ b/tox.ini @@ -41,9 +41,10 @@ basepython = python changedir = docs whitelist_externals = make commands = make html -deps = sphinx +deps = sphinx<3 oauthlib>=3.0.1 m2r>=0.2.1 + jwcrypto [testenv:py37-flake8] skip_install = True