Conflicts:
	example_project/.gitignore
This commit is contained in:
juanifioren 2015-07-17 11:32:14 -03:00
commit a08dbdb7d2
11 changed files with 82 additions and 44 deletions

View file

@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
### [Unreleased]
##### Fixed
- Nonce support for both Code and Implicit flow.
### [0.0.7] - 2015-07-06
##### Added

View file

@ -1,2 +1,2 @@
*.sqlite3
*.pem
*.pem

View file

@ -36,7 +36,6 @@ class AuthorizeEndpoint(object):
self.grant_type = 'authorization_code'
elif self.params.response_type in ['id_token', 'id_token token']:
self.grant_type = 'implicit'
self._extract_implicit_params()
else:
self.grant_type = None
@ -52,13 +51,6 @@ class AuthorizeEndpoint(object):
self.params.response_type = self.query_dict.get('response_type', '')
self.params.scope = self.query_dict.get('scope', '').split()
self.params.state = self.query_dict.get('state', '')
def _extract_implicit_params(self):
"""
Get specific params used by the Implicit Flow.
See: http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthRequest
"""
self.params.nonce = self.query_dict.get('nonce', '')
def validate_params(self):
@ -104,7 +96,8 @@ class AuthorizeEndpoint(object):
code = create_code(
user=self.request.user,
client=self.client,
scope=self.params.scope)
scope=self.params.scope,
nonce=self.params.nonce)
code.save()
@ -114,7 +107,8 @@ class AuthorizeEndpoint(object):
elif self.grant_type == 'implicit':
id_token_dic = create_id_token(
user=self.request.user,
aud=self.client.client_id)
aud=self.client.client_id,
nonce=self.params.nonce)
token = create_token(
user=self.request.user,

View file

@ -33,7 +33,6 @@ 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'):
@ -72,7 +71,7 @@ class TokenEndpoint(object):
id_token_dic = create_id_token(
user=self.code.user,
aud=self.client.client_id,
nonce=self.params.nonce,
nonce=self.code.nonce,
)
token = create_token(

View file

@ -10,7 +10,7 @@ from oidc_provider.models import *
from oidc_provider import settings
def create_id_token(user, aud, nonce=None):
def create_id_token(user, aud, nonce):
"""
Receives a user object and aud (audience).
Then creates the id_token dictionary.
@ -22,13 +22,12 @@ def create_id_token(user, aud, nonce=None):
expires_in = settings.get('OIDC_IDTOKEN_EXPIRE')
now = timezone.now()
# Convert datetimes into timestamps.
iat_time = time.mktime(now.timetuple())
exp_time = time.mktime((now + timedelta(seconds=expires_in)).timetuple())
now = timezone.now()
iat_time = int(time.mktime(now.timetuple()))
exp_time = int(time.mktime((now + timedelta(seconds=expires_in)).timetuple()))
user_auth_time = user.last_login or user.date_joined
auth_time = time.mktime(user_auth_time.timetuple())
auth_time = int(time.mktime(user_auth_time.timetuple()))
dic = {
'iss': get_issuer(),
@ -75,7 +74,7 @@ def create_token(user, client, id_token_dic, scope):
return token
def create_code(user, client, scope):
def create_code(user, client, scope, nonce):
"""
Create and populate a Code object.
@ -88,5 +87,6 @@ def create_code(user, client, scope):
code.expires_at = timezone.now() + timedelta(
seconds=settings.get('OIDC_CODE_EXPIRE'))
code.scope = scope
code.nonce = nonce
return code

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('oidc_provider', '0002_userconsent'),
]
operations = [
migrations.AddField(
model_name='code',
name='nonce',
field=models.CharField(default=b'', max_length=255, blank=True),
),
]

View file

@ -71,6 +71,7 @@ class BaseCodeTokenModel(models.Model):
class Code(BaseCodeTokenModel):
code = models.CharField(max_length=255, unique=True)
nonce = models.CharField(max_length=255, blank=True, default='')
class Token(BaseCodeTokenModel):

View file

@ -60,7 +60,7 @@ class DefaultSettings(object):
which is intended to be consumed by the Client.
"""
def default_sub_generator(user):
return user.id
return str(user.id)
return default_sub_generator

View file

@ -6,6 +6,8 @@ except ImportError:
from oidc_provider.models import *
FAKE_NONCE = 'cb584e44c43ed6bd0bc2d9c7e242837d'
def create_fake_user():
"""
Create a test user.

View file

@ -27,7 +27,21 @@ class TokenTestCase(TestCase):
self.factory = RequestFactory()
self.user = create_fake_user()
self.client = create_fake_client(response_type='code')
self.state = uuid.uuid4().hex
def _post_data(self, code):
"""
All the data that will be POSTed to the Token Endpoint.
"""
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,
'state': uuid.uuid4().hex,
}
return post_data
def _post_request(self, post_data):
"""
@ -52,7 +66,8 @@ class TokenTestCase(TestCase):
code = create_code(
user=self.user,
client=self.client,
scope=['openid', 'email'])
scope=['openid', 'email'],
nonce=FAKE_NONCE)
code.save()
return code
@ -94,14 +109,8 @@ class TokenTestCase(TestCase):
code = self._create_code()
# Test a valid request to the token endpoint.
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,
}
post_data = self._post_data(code=code.code)
response = self._post_request(post_data)
response_dic = json.loads(response.content.decode('utf-8'))
@ -126,7 +135,7 @@ class TokenTestCase(TestCase):
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):
def test_access_token_contains_nonce(self):
"""
If present in the Authentication Request, Authorization Servers MUST
include a nonce Claim in the ID Token with the Claim Value being the
@ -134,18 +143,9 @@ class TokenTestCase(TestCase):
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'
}
post_data = self._post_data(code=code.code)
response = self._post_request(post_data)
@ -153,4 +153,23 @@ class TokenTestCase(TestCase):
id_token = jwt.decode(response_dic['id_token'],
options={'verify_signature': False, 'verify_aud': False})
self.assertEqual(id_token['nonce'], 'thisisanonce')
self.assertEqual(id_token['nonce'], FAKE_NONCE)
def test_access_token_not_contains_nonce(self):
"""
If the client does not supply a nonce parameter, it SHOULD not be
included in the `id_token`.
"""
code = self._create_code()
code.nonce = ''
code.save()
post_data = self._post_data(code=code.code)
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.get('nonce'), None)

View file

@ -22,7 +22,8 @@ class UserInfoTestCase(TestCase):
"""
Generate a valid token.
"""
id_token_dic = create_id_token(self.user, self.client.client_id)
id_token_dic = create_id_token(self.user,
self.client.client_id, FAKE_NONCE)
token = create_token(
user=self.user,