Skip to content

Commit c98fa0b

Browse files
Qup42dopry
authored andcommitted
Split IDToken loading and claim validation into two functions
1 parent 2ffbc5b commit c98fa0b

File tree

2 files changed

+47
-24
lines changed

2 files changed

+47
-24
lines changed

oauth2_provider/views/oidc.py

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -144,19 +144,18 @@ def _create_userinfo_response(self, request):
144144
return response
145145

146146

147-
def _load_id_token(request, token):
147+
def _load_id_token(token):
148148
"""
149149
Loads an IDToken given its string representation for use with RP-Initiated Logout.
150-
This method differs from `oauth2_validators._load_id_token` in two ways.
151-
This method validates the `iss` claim and might accept expired but otherwise valid tokens
152-
depending on the configuration.
150+
A tuple (IDToken, claims) is returned. Depending on the configuration expired tokens may be loaded.
151+
If loading failed (None, None) is returned.
153152
"""
154153
IDToken = get_id_token_model()
155154
validator = oauth2_settings.OAUTH2_VALIDATOR_CLASS()
156155

157156
key = validator._get_key_for_token(token)
158157
if not key:
159-
return None
158+
return None, None
160159

161160
try:
162161
if oauth2_settings.OIDC_RP_INITIATED_LOGOUT_ACCEPT_EXPIRED_TOKENS:
@@ -171,20 +170,29 @@ def _load_id_token(request, token):
171170
jwt_token = jwt.JWT(key=key, jwt=token, check_claims=check_claims)
172171
claims = json.loads(jwt_token.claims)
173172

174-
# Verification of `iss` claim is mandated by OIDC RP-Initiated Logout specs.
175-
if claims["iss"] != validator.get_oidc_issuer_endpoint(request):
176-
# IDToken was not issued by this OP.
177-
return None
178-
179173
# Assumption: the `sub` claim and `user` property of the corresponding IDToken Object point to the
180174
# same user.
181175
# To verify that the IDToken was intended for the user it is therefore sufficient to check the `user`
182176
# attribute on the IDToken Object later on.
183177

184-
return IDToken.objects.get(jti=claims["jti"])
178+
return IDToken.objects.get(jti=claims["jti"]), claims
185179

186180
except (JWException, JWTExpired, IDToken.DoesNotExist):
187-
return None
181+
return None, None
182+
183+
184+
def _validate_claims(request, claims):
185+
"""
186+
Validates the claims of an IDToken for use with OIDC RP-Initiated Logout.
187+
"""
188+
validator = oauth2_settings.OAUTH2_VALIDATOR_CLASS()
189+
190+
# Verification of `iss` claim is mandated by OIDC RP-Initiated Logout specs.
191+
if "iss" not in claims or claims["iss"] != validator.get_oidc_issuer_endpoint(request):
192+
# IDToken was not issued by this OP, or it can not be verified.
193+
return False
194+
195+
return True
188196

189197

190198
def validate_logout_request(request, id_token_hint, client_id, post_logout_redirect_uri):
@@ -205,11 +213,10 @@ def validate_logout_request(request, id_token_hint, client_id, post_logout_redir
205213
id_token = None
206214
must_prompt_logout = True
207215
if id_token_hint:
208-
# Note: The standard states that expired tokens should still be accepted.
209-
# This implementation only accepts tokens that are still valid.
210-
id_token = _load_id_token(request, id_token_hint)
216+
# Only basic validation has been done on the IDToken at this point.
217+
id_token, claims = _load_id_token(id_token_hint)
211218

212-
if not id_token:
219+
if not id_token or not _validate_claims(request, claims):
213220
raise InvalidIDTokenError()
214221

215222
if id_token.user == request.user:

tests/test_oidc_views.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from oauth2_provider.models import get_id_token_model
99
from oauth2_provider.oauth2_validators import OAuth2Validator
1010
from oauth2_provider.settings import oauth2_settings
11-
from oauth2_provider.views.oidc import _load_id_token, validate_logout_request
11+
from oauth2_provider.views.oidc import _load_id_token, _validate_claims, validate_logout_request
1212

1313
from . import presets
1414

@@ -411,25 +411,41 @@ def test_rp_initiated_logout_expired_tokens_deny(loggend_in_client, application,
411411
@pytest.mark.django_db
412412
@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RP_LOGOUT)
413413
def test_load_id_token_accept_expired(expired_id_token):
414-
assert isinstance(_load_id_token(mock_request(), expired_id_token), get_id_token_model())
414+
id_token, _ = _load_id_token(expired_id_token)
415+
assert isinstance(id_token, get_id_token_model())
415416

416417

417418
@pytest.mark.django_db
418419
@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RP_LOGOUT)
419420
def test_load_id_token_wrong_aud(id_token_wrong_aud):
420-
assert _load_id_token(mock_request(), id_token_wrong_aud) is None
421+
id_token, claims = _load_id_token(id_token_wrong_aud)
422+
assert id_token is None
423+
assert claims is None
424+
425+
426+
@pytest.mark.django_db
427+
@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RP_LOGOUT_DENY_EXPIRED)
428+
def test_load_id_token_deny_expired(expired_id_token):
429+
id_token, claims = _load_id_token(expired_id_token)
430+
assert id_token is None
431+
assert claims is None
421432

422433

423434
@pytest.mark.django_db
424435
@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RP_LOGOUT)
425-
def test_load_id_token_wrong_iss(id_token_wrong_iss):
426-
assert _load_id_token(mock_request(), id_token_wrong_iss) is None
436+
def test_validate_claims_wrong_iss(id_token_wrong_iss):
437+
id_token, claims = _load_id_token(id_token_wrong_iss)
438+
assert id_token is not None
439+
assert claims is not None
440+
assert not _validate_claims(mock_request(), claims)
427441

428442

429443
@pytest.mark.django_db
430-
@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RP_LOGOUT_DENY_EXPIRED)
431-
def test_load_id_token_deny_expired(expired_id_token):
432-
assert _load_id_token(mock_request(), expired_id_token) is None
444+
@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RP_LOGOUT)
445+
def test_validate_claims(oidc_tokens):
446+
id_token, claims = _load_id_token(oidc_tokens.id_token)
447+
assert claims is not None
448+
assert _validate_claims(mock_request_for(oidc_tokens.user), claims)
433449

434450

435451
@pytest.mark.django_db

0 commit comments

Comments
 (0)