From 5417c881dac24e76a39cc33a73675c7f3cd77c83 Mon Sep 17 00:00:00 2001 From: Giuseppe Date: Mon, 7 Sep 2020 18:12:20 +0200 Subject: [PATCH] Authorization denied for unallowed scopes --- src/oidcendpoint/exception.py | 4 ++ src/oidcendpoint/oauth2/authorization.py | 21 +++++++++++ src/oidcendpoint/oidc/authorization.py | 4 ++ .../test_24_oauth2_authorization_endpoint.py | 37 ++++++++++++++++++- tests/test_24_oidc_authorization_endpoint.py | 4 +- 5 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/oidcendpoint/exception.py b/src/oidcendpoint/exception.py index dfe0114..8ab680d 100755 --- a/src/oidcendpoint/exception.py +++ b/src/oidcendpoint/exception.py @@ -66,6 +66,10 @@ class UnAuthorizedClient(OidcEndpointError): pass +class UnAuthorizedClientScope(OidcEndpointError): + pass + + class InvalidCookieSign(Exception): pass diff --git a/src/oidcendpoint/oauth2/authorization.py b/src/oidcendpoint/oauth2/authorization.py index 8e76ed9..8591238 100755 --- a/src/oidcendpoint/oauth2/authorization.py +++ b/src/oidcendpoint/oauth2/authorization.py @@ -33,6 +33,7 @@ from oidcendpoint.exception import ServiceError from oidcendpoint.exception import TamperAllert from oidcendpoint.exception import ToOld +from oidcendpoint.exception import UnAuthorizedClientScope from oidcendpoint.exception import UnknownClient from oidcendpoint.scopes import available_scopes from oidcendpoint.session import setup_session @@ -120,6 +121,21 @@ def re_authenticate(request, authn): return False +def check_unknown_scopes_policy(request_info, cinfo, endpoint_context): + op_capabilities = endpoint_context.conf['capabilities'] + client_allowed_scopes = cinfo.get('allowed_scopes') or \ + op_capabilities['scopes_supported'] + + # this prevents that authz would be released for unavailable scopes + for scope in request_info['scope']: + if op_capabilities.get('deny_unknown_scopes') and \ + scope not in client_allowed_scopes: + _msg = '{} requested an unauthorized scope ({})' + logger.warning(_msg.format(cinfo['client_id'], + scope)) + raise UnAuthorizedClientScope() + + class Authorization(Endpoint): request_cls = oauth2.AuthorizationRequest response_cls = oauth2.AuthorizationResponse @@ -591,6 +607,11 @@ def process_request(self, request_info=None, **kwargs): _cid = request_info["client_id"] cinfo = self.endpoint_context.cdb[_cid] + logger.debug("client {}: {}".format(_cid, cinfo)) + + # this apply the default optionally deny_unknown_scopes policy + check_unknown_scopes_policy(request_info, cinfo, self.endpoint_context) + cookie = kwargs.get("cookie", "") if cookie: del kwargs["cookie"] diff --git a/src/oidcendpoint/oidc/authorization.py b/src/oidcendpoint/oidc/authorization.py index 0332a93..5499915 100755 --- a/src/oidcendpoint/oidc/authorization.py +++ b/src/oidcendpoint/oidc/authorization.py @@ -37,6 +37,7 @@ from oidcendpoint.exception import TamperAllert from oidcendpoint.exception import ToOld from oidcendpoint.exception import UnknownClient +from oidcendpoint.oauth2.authorization import check_unknown_scopes_policy from oidcendpoint.session import setup_session from oidcendpoint.user_authn.authn_context import pick_auth @@ -683,6 +684,9 @@ def process_request(self, request_info=None, **kwargs): cinfo = self.endpoint_context.cdb[_cid] logger.debug("client {}: {}".format(_cid, cinfo)) + # this apply the default optionally deny_unknown_scopes policy + check_unknown_scopes_policy(request_info, cinfo, self.endpoint_context) + cookie = kwargs.get("cookie", "") if cookie: del kwargs["cookie"] diff --git a/tests/test_24_oauth2_authorization_endpoint.py b/tests/test_24_oauth2_authorization_endpoint.py index 25abecd..6f2e556 100755 --- a/tests/test_24_oauth2_authorization_endpoint.py +++ b/tests/test_24_oauth2_authorization_endpoint.py @@ -31,6 +31,7 @@ from oidcendpoint.exception import RedirectURIError from oidcendpoint.exception import ToOld from oidcendpoint.exception import UnknownClient +from oidcendpoint.exception import UnAuthorizedClientScope from oidcendpoint.id_token import IDToken from oidcendpoint.oauth2.authorization import Authorization from oidcendpoint.session import SessionInfo @@ -116,11 +117,11 @@ def get_cookie_value(cookie=None, cookie_name=None): clients: client_1: "client_secret": 'hemligtkodord' - "redirect_uris": + "redirect_uris": - ['https://example.com/cb', ''] "client_salt": "salted" 'token_endpoint_auth_method': 'client_secret_post' - 'response_types': + 'response_types': - 'code' - 'token' client2: @@ -463,6 +464,38 @@ def test_setup_auth_error(self): item["method"].file = "" + def test_setup_auth_invalid_scope(self): + request = AuthorizationRequest( + client_id="client_id", + redirect_uri="https://rp.example.com/cb", + response_type=["id_token"], + state="state", + nonce="nonce", + scope="openid THAT-BLOODY_SCOPE", + ) + redirect_uri = request["redirect_uri"] + cinfo = { + "client_id": "client_id", + "redirect_uris": [("https://rp.example.com/cb", {})], + "id_token_signed_response_alg": "RS256", + } + + _ec = self.endpoint.endpoint_context + _ec.cdb["client_id"] = cinfo + + kaka = self.endpoint.endpoint_context.cookie_dealer.create_cookie( + "value", "sso") + + # force to 400 Http Error message if the release scope policy is heavy! + self.endpoint.endpoint_context.conf['capabilities']['deny_unknown_scopes'] = True + excp = None + try: + res = self.endpoint.process_request(request) + except UnAuthorizedClientScope as e: + excp = e + assert excp + assert isinstance(excp, UnAuthorizedClientScope) + def test_setup_auth_user(self): request = AuthorizationRequest( client_id="client_id", diff --git a/tests/test_24_oidc_authorization_endpoint.py b/tests/test_24_oidc_authorization_endpoint.py index aea8244..80c0166 100755 --- a/tests/test_24_oidc_authorization_endpoint.py +++ b/tests/test_24_oidc_authorization_endpoint.py @@ -115,11 +115,11 @@ def full_path(local_file): oidc_clients: client_1: "client_secret": 'hemligtkodord' - "redirect_uris": + "redirect_uris": - ['https://example.com/cb', ''] "client_salt": "salted" 'token_endpoint_auth_method': 'client_secret_post' - 'response_types': + 'response_types': - 'code' - 'token' - 'code id_token'