diff --git a/oidc_provider/lib/endpoints/token.py b/oidc_provider/lib/endpoints/token.py index 6566f03..191a3f4 100644 --- a/oidc_provider/lib/endpoints/token.py +++ b/oidc_provider/lib/endpoints/token.py @@ -33,6 +33,7 @@ class TokenEndpoint(object): self.params.grant_type = query_dict.get('grant_type', '') self.params.code = query_dict.get('code', '') self.params.state = query_dict.get('state', '') + self.params.nonce = query_dict.get('nonce', '') def validate_params(self): if not (self.params.grant_type == 'authorization_code'): @@ -70,7 +71,9 @@ class TokenEndpoint(object): def create_response_dic(self): id_token_dic = create_id_token( user=self.code.user, - aud=self.client.client_id) + aud=self.client.client_id, + nonce=self.params.nonce, + ) token = create_token( user=self.code.user, diff --git a/oidc_provider/lib/utils/token.py b/oidc_provider/lib/utils/token.py index de93ea2..1264471 100644 --- a/oidc_provider/lib/utils/token.py +++ b/oidc_provider/lib/utils/token.py @@ -10,7 +10,7 @@ from oidc_provider.models import * from oidc_provider import settings -def create_id_token(user, aud): +def create_id_token(user, aud, nonce=None): """ Receives a user object and aud (audience). Then creates the id_token dictionary. @@ -40,6 +40,9 @@ def create_id_token(user, aud): 'auth_time': auth_time, } + if nonce: + dic['nonce'] = nonce + return dic @@ -89,4 +92,4 @@ def create_code(user, client, scope): seconds=settings.get('OIDC_CODE_EXPIRE')) code.scope = scope - return code \ No newline at end of file + return code diff --git a/oidc_provider/tests/test_token_endpoint.py b/oidc_provider/tests/test_token_endpoint.py index 1bd6568..19d2983 100644 --- a/oidc_provider/tests/test_token_endpoint.py +++ b/oidc_provider/tests/test_token_endpoint.py @@ -9,6 +9,8 @@ from django.core.urlresolvers import reverse from django.test import RequestFactory from django.test import TestCase +import jwt + from oidc_provider.lib.utils.token import * from oidc_provider.tests.utils import * from oidc_provider.views import * @@ -123,3 +125,32 @@ class TokenTestCase(TestCase): msg='"error" key should exists in response.') self.assertEqual(response_dic.get('error') == 'invalid_client', True, msg='"error" key value should be "invalid_client".') + + def test_token_contains_nonce_if_provided(self): + """ + If present in the Authentication Request, Authorization Servers MUST + include a nonce Claim in the ID Token with the Claim Value being the + nonce value sent in the Authentication Request. + + See http://openid.net/specs/openid-connect-core-1_0.html#IDToken + """ + + code = self._create_code() + + post_data = { + 'client_id': self.client.client_id, + 'client_secret': self.client.client_secret, + 'redirect_uri': self.client.default_redirect_uri, + 'grant_type': 'authorization_code', + 'code': code.code, + 'state': self.state, + 'nonce': 'thisisanonce' + } + + response = self._post_request(post_data) + + response_dic = json.loads(response.content.decode('utf-8')) + id_token = jwt.decode(response_dic['id_token'], + options={'verify_signature': False, 'verify_aud': False}) + + self.assertEqual(id_token['nonce'], 'thisisanonce')