From f8dbfa5c049bb6d789cce2952437d52d7122679b Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Thu, 8 Sep 2016 16:21:48 -0300 Subject: [PATCH 01/28] Add Hybrid flow login in view and models. --- oidc_provider/lib/endpoints/authorize.py | 35 +++++++++++-------- .../migrations/0018_auto_20160908_1627.py | 20 +++++++++++ .../migrations/0019_auto_20160908_1519.py | 20 +++++++++++ oidc_provider/models.py | 3 ++ 4 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 oidc_provider/migrations/0018_auto_20160908_1627.py create mode 100644 oidc_provider/migrations/0019_auto_20160908_1519.py diff --git a/oidc_provider/lib/endpoints/authorize.py b/oidc_provider/lib/endpoints/authorize.py index ca4a584..2b2862d 100644 --- a/oidc_provider/lib/endpoints/authorize.py +++ b/oidc_provider/lib/endpoints/authorize.py @@ -44,6 +44,8 @@ class AuthorizeEndpoint(object): self.grant_type = 'authorization_code' elif self.params.response_type in ['id_token', 'id_token token', 'token']: self.grant_type = 'implicit' + elif self.params.response_type in ['code token', 'code id_token', 'code id_token token']: + self.grant_type = 'hybrid' else: self.grant_type = None @@ -53,7 +55,7 @@ class AuthorizeEndpoint(object): def _extract_params(self): """ Get all the params used by the Authorization Code Flow - (and also for the Implicit). + (and also for the Implicit and Hybrid). See: http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest """ @@ -118,7 +120,7 @@ class AuthorizeEndpoint(object): query_fragment = parse_qs(uri.fragment) try: - if self.grant_type == 'authorization_code': + if self.grant_type in ['authorization_code', 'hybrid']: code = create_code( user=self.request.user, client=self.client, @@ -127,37 +129,37 @@ class AuthorizeEndpoint(object): is_authentication=self.is_authentication, code_challenge=self.params.code_challenge, code_challenge_method=self.params.code_challenge_method) - code.save() + if self.grant_type == 'authorization_code': query_params['code'] = code.code query_params['state'] = self.params.state if self.params.state else '' - - elif self.grant_type == 'implicit': + elif self.grant_type in ['implicit', 'hybrid']: token = create_token( user=self.request.user, client=self.client, scope=self.params.scope) - # Check if response_type is an OpenID request with value 'id_token token' - # or it's an OAuth2 Implicit Flow request. - if self.params.response_type in ['id_token token', 'token']: + # Check if response_type must include access_token in the response. + if self.params.response_type in ['id_token token', 'token', 'code token', 'code id_token token']: query_fragment['access_token'] = token.access_token # We don't need id_token if it's an OAuth2 request. if self.is_authentication: kwargs = { - "user": self.request.user, - "aud": self.client.client_id, - "nonce": self.params.nonce, - "request": self.request + 'user': self.request.user, + 'aud': self.client.client_id, + 'nonce': self.params.nonce, + 'request': self.request, } # Include at_hash when access_token is being returned. if 'access_token' in query_fragment: kwargs['at_hash'] = token.at_hash id_token_dic = create_id_token(**kwargs) - query_fragment['id_token'] = encode_id_token(id_token_dic, self.client) - token.id_token = id_token_dic + + # Check if response_type must include id_token in the response. + if self.params.response_type in ['id_token', 'id_token token', 'code id_token', 'code id_token token']: + query_fragment['id_token'] = encode_id_token(id_token_dic, self.client) else: id_token_dic = {} @@ -165,7 +167,12 @@ class AuthorizeEndpoint(object): token.id_token = id_token_dic token.save() + # Code parameter must be present if it's Hybrid Flow. + if self.grant_type == 'hybrid': + query_fragment['code'] = code.code + query_fragment['token_type'] = 'bearer' + # TODO: Create setting 'OIDC_TOKEN_EXPIRE'. query_fragment['expires_in'] = 60 * 10 diff --git a/oidc_provider/migrations/0018_auto_20160908_1627.py b/oidc_provider/migrations/0018_auto_20160908_1627.py new file mode 100644 index 0000000..9d9cc7b --- /dev/null +++ b/oidc_provider/migrations/0018_auto_20160908_1627.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-09-08 16:27 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('oidc_provider', '0017_auto_20160811_1954'), + ] + + operations = [ + migrations.AlterField( + model_name='client', + name='response_type', + field=models.CharField(choices=[(b'code', b'code (Authorization Code Flow)'), (b'id_token', b'id_token (Implicit Flow)'), (b'id_token token', b'id_token token (Implicit Flow)'), (b'code token', b'code token (Hybrid Flow)'), (b'code id_token', b'code id_token (Hybrid Flow)'), (b'code id_token token', b'code id_token token (Hybrid Flow)')], max_length=30, verbose_name='Response Type'), + ), + ] diff --git a/oidc_provider/migrations/0019_auto_20160908_1519.py b/oidc_provider/migrations/0019_auto_20160908_1519.py new file mode 100644 index 0000000..320b30b --- /dev/null +++ b/oidc_provider/migrations/0019_auto_20160908_1519.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('oidc_provider', '0018_auto_20160908_1627'), + ] + + operations = [ + migrations.AlterField( + model_name='client', + name='response_type', + field=models.CharField(choices=[('code', 'code (Authorization Code Flow)'), ('id_token', 'id_token (Implicit Flow)'), ('id_token token', 'id_token token (Implicit Flow)'), ('code token', 'code token (Hybrid Flow)'), ('code id_token', 'code id_token (Hybrid Flow)'), ('code id_token token', 'code id_token token (Hybrid Flow)')], verbose_name='Response Type', max_length=30), + preserve_default=True, + ), + ] diff --git a/oidc_provider/models.py b/oidc_provider/models.py index 0adda40..3456e50 100644 --- a/oidc_provider/models.py +++ b/oidc_provider/models.py @@ -19,6 +19,9 @@ RESPONSE_TYPE_CHOICES = [ ('code', 'code (Authorization Code Flow)'), ('id_token', 'id_token (Implicit Flow)'), ('id_token token', 'id_token token (Implicit Flow)'), + ('code token', 'code token (Hybrid Flow)'), + ('code id_token', 'code id_token (Hybrid Flow)'), + ('code id_token token', 'code id_token token (Hybrid Flow)'), ] JWT_ALGS = [ From 439774aeeb2291381a4a475e5c113e004295a867 Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Thu, 8 Sep 2016 17:15:25 -0300 Subject: [PATCH 02/28] Add test for Hybrid flow. Plus refactoring. --- oidc_provider/tests/app/utils.py | 7 +- .../tests/test_authorize_endpoint.py | 128 ++++++++++-------- 2 files changed, 77 insertions(+), 58 deletions(-) diff --git a/oidc_provider/tests/app/utils.py b/oidc_provider/tests/app/utils.py index 4c58071..6308d1b 100644 --- a/oidc_provider/tests/app/utils.py +++ b/oidc_provider/tests/app/utils.py @@ -60,15 +60,14 @@ def create_fake_client(response_type, is_public=False): def is_code_valid(url, user, client): """ - Check if the code inside the url is valid. + Check if the code inside the url is valid. Supporting both query string and fragment. """ try: parsed = urlsplit(url) - params = parse_qs(parsed.query) + params = parse_qs(parsed.query or parsed.fragment) code = params['code'][0] code = Code.objects.get(code=code) - is_code_ok = (code.client == client) and \ - (code.user == user) + is_code_ok = (code.client == client) and (code.user == user) except: is_code_ok = False diff --git a/oidc_provider/tests/test_authorize_endpoint.py b/oidc_provider/tests/test_authorize_endpoint.py index f323d36..bd05163 100644 --- a/oidc_provider/tests/test_authorize_endpoint.py +++ b/oidc_provider/tests/test_authorize_endpoint.py @@ -25,19 +25,7 @@ from oidc_provider.tests.app.utils import ( from oidc_provider.views import AuthorizeView -class AuthorizationCodeFlowTestCase(TestCase): - """ - Test cases for Authorize Endpoint using Code Flow. - """ - - def setUp(self): - call_command('creatersakey') - self.factory = RequestFactory() - self.user = create_fake_user() - self.client = create_fake_client(response_type='code') - self.client_public = create_fake_client(response_type='code', is_public=True) - self.state = uuid.uuid4().hex - self.nonce = uuid.uuid4().hex +class AuthorizeEndpointMixin(object): def _auth_request(self, method, data={}, is_user_authenticated=False): url = reverse('oidc_provider:authorize') @@ -59,6 +47,21 @@ class AuthorizationCodeFlowTestCase(TestCase): return response + +class AuthorizationCodeFlowTestCase(TestCase, AuthorizeEndpointMixin): + """ + Test cases for Authorize Endpoint using Code Flow. + """ + + def setUp(self): + call_command('creatersakey') + self.factory = RequestFactory() + self.user = create_fake_user() + self.client = create_fake_client(response_type='code') + self.client_public = create_fake_client(response_type='code', is_public=True) + self.state = uuid.uuid4().hex + self.nonce = uuid.uuid4().hex + def test_missing_parameters(self): """ If the request fails due to a missing, invalid, or mismatching @@ -94,8 +97,7 @@ class AuthorizationCodeFlowTestCase(TestCase): self.assertEqual(response.has_header('Location'), True) # Should be an 'error' component in query. - query_exists = 'error=' in response['Location'] - self.assertEqual(query_exists, True) + self.assertIn('error=', response['Location']) def test_user_not_logged(self): """ @@ -115,8 +117,7 @@ class AuthorizationCodeFlowTestCase(TestCase): response = self._auth_request('get', data) # Check if user was redirected to the login view. - login_url_exists = settings.get('LOGIN_URL') in response['Location'] - self.assertEqual(login_url_exists, True) + self.assertIn(settings.get('LOGIN_URL'), response['Location']) def test_user_consent_inputs(self): """ @@ -183,10 +184,8 @@ class AuthorizationCodeFlowTestCase(TestCase): # Because user doesn't allow app, SHOULD exists an error parameter # in the query. - self.assertEqual('error=' in response['Location'], True, - msg='error param is missing in query.') - self.assertEqual('access_denied' in response['Location'], True, - msg='"access_denied" code is missing in query.') + self.assertIn('error=', response['Location'], msg='error param is missing in query.') + self.assertIn('access_denied', response['Location'], msg='"access_denied" code is missing in query.') # Simulate user authorization. data['allow'] = 'Accept' # Will be the value of the button. @@ -201,8 +200,7 @@ class AuthorizationCodeFlowTestCase(TestCase): # Check if the state is returned. state = (response['Location'].split('state='))[1].split('&')[0] - self.assertEqual(state == self.state, True, - msg='State change or is missing.') + self.assertEqual(state, self.state, msg='State change or is missing.') def test_user_consent_skipped(self): """ @@ -227,8 +225,7 @@ class AuthorizationCodeFlowTestCase(TestCase): with self.settings(OIDC_SKIP_CONSENT_ALWAYS=True): response = self._auth_request('post', data, is_user_authenticated=True) - self.assertEqual('code' in response['Location'], True, - msg='Code is missing in the returned url.') + self.assertIn('code', response['Location'], msg='Code is missing in the returned url.') response = self._auth_request('post', data, is_user_authenticated=True) @@ -274,7 +271,7 @@ class AuthorizationCodeFlowTestCase(TestCase): with self.settings(OIDC_SKIP_CONSENT_ALWAYS=True): response = self._auth_request('get', data, is_user_authenticated=True) - self.assertEqual('Request for Permission' in response.content.decode('utf-8'), True) + self.assertIn('Request for Permission', response.content.decode('utf-8')) def test_prompt_parameter(self): """ @@ -294,15 +291,15 @@ class AuthorizationCodeFlowTestCase(TestCase): response = self._auth_request('get', data) # An error is returned if an End-User is not already authenticated. - self.assertEqual('login_required' in response['Location'], True) + self.assertIn('login_required', response['Location']) response = self._auth_request('get', data, is_user_authenticated=True) # An error is returned if the Client does not have pre-configured consent for the requested Claims. - self.assertEqual('interaction_required' in response['Location'], True) + self.assertIn('interaction_required', response['Location']) -class AuthorizationImplicitFlowTestCase(TestCase): +class AuthorizationImplicitFlowTestCase(TestCase, AuthorizeEndpointMixin): """ Test cases for Authorization Endpoint using Implicit Flow. """ @@ -318,26 +315,6 @@ class AuthorizationImplicitFlowTestCase(TestCase): self.state = uuid.uuid4().hex self.nonce = uuid.uuid4().hex - def _auth_request(self, method, data={}, is_user_authenticated=False): - url = reverse('oidc_provider:authorize') - - if method.lower() == 'get': - query_str = urlencode(data).replace('+', '%20') - if query_str: - url += '?' + query_str - request = self.factory.get(url) - elif method.lower() == 'post': - request = self.factory.post(url, data=data) - else: - raise Exception('Method unsupported for an Authorization Request.') - - # Simulate that the user is logged. - request.user = self.user if is_user_authenticated else AnonymousUser() - - response = AuthorizeView.as_view()(request) - - return response - def test_missing_nonce(self): """ The `nonce` parameter is REQUIRED if you use the Implicit Flow. @@ -352,9 +329,9 @@ class AuthorizationImplicitFlowTestCase(TestCase): response = self._auth_request('get', data, is_user_authenticated=True) - self.assertEqual('#error=invalid_request' in response['Location'], True) + self.assertIn('#error=invalid_request', response['Location']) - def test_id_token_token_response(self): + def test_idtoken_token_response(self): """ Implicit client requesting `id_token token` receives both id token and access token as the result of the authorization request. @@ -384,7 +361,7 @@ class AuthorizationImplicitFlowTestCase(TestCase): self.assertIn('access_token', response['Location']) self.assertIn('id_token', response['Location']) - def test_id_token_response(self): + def test_idtoken_response(self): """ Implicit client requesting `id_token` receives only an id token as the result of the authorization request. @@ -414,7 +391,7 @@ class AuthorizationImplicitFlowTestCase(TestCase): self.assertNotIn('access_token', response['Location']) self.assertIn('id_token', response['Location']) - def test_id_token_token_at_hash(self): + def test_idtoken_token_at_hash(self): """ Implicit client requesting `id_token token` receives `at_hash` in `id_token`. @@ -440,7 +417,7 @@ class AuthorizationImplicitFlowTestCase(TestCase): self.assertIn('at_hash', id_token) - def test_id_token_at_hash(self): + def test_idtoken_at_hash(self): """ Implicit client requesting `id_token` should not receive `at_hash` in `id_token`. @@ -465,3 +442,46 @@ class AuthorizationImplicitFlowTestCase(TestCase): id_token = JWT().unpack(fragment["id_token"][0].encode('utf-8')).payload() self.assertNotIn('at_hash', id_token) + + +class AuthorizationHybridFlowTestCase(TestCase, AuthorizeEndpointMixin): + """ + Test cases for Authorization Endpoint using Hybrid Flow. + """ + + def setUp(self): + call_command('creatersakey') + self.factory = RequestFactory() + self.user = create_fake_user() + self.client_code_idtoken_token = create_fake_client(response_type='code id_token token', is_public=True) + self.state = uuid.uuid4().hex + self.nonce = uuid.uuid4().hex + + def test_code_idtoken_token_response(self): + """ + Implicit client requesting `id_token token` receives both id token + and access token as the result of the authorization request. + """ + data = { + 'client_id': self.client_code_idtoken_token.client_id, + 'redirect_uri': self.client_code_idtoken_token.default_redirect_uri, + 'response_type': self.client_code_idtoken_token.response_type, + 'scope': 'openid email', + 'state': self.state, + 'nonce': self.nonce, + 'allow': 'Accept', + } + + response = self._auth_request('post', data, is_user_authenticated=True) + + self.assertIn('#', response['Location']) + self.assertIn('access_token', response['Location']) + self.assertIn('id_token', response['Location']) + self.assertIn('state', response['Location']) + self.assertIn('code', response['Location']) + + # Validate code. + is_code_ok = is_code_valid(url=response['Location'], + user=self.user, + client=self.client_code_idtoken_token) + self.assertEqual(is_code_ok, True, msg='Code returned is invalid.') From 2c5019af3ef2b906fcd240aa82884465b511ee71 Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Thu, 8 Sep 2016 17:32:57 -0300 Subject: [PATCH 03/28] Improve docs. --- docs/index.rst | 3 +-- docs/sections/relyingparties.rst | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index e0f7ff2..afa58b4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,7 @@ Welcome to Django OIDC Provider Documentation! ============================================== -Django OIDC Provider can help you providing out of the box all the endpoints, data and logic needed to add OpenID Connect capabilities to your Django projects. And as a side effect a fair implementation of OAuth2.0 too. +This tiny (but powerful!) package can help you providing out of the box all the endpoints, data and logic needed to add OpenID Connect capabilities to your Django projects. And as a side effect a fair implementation of OAuth2.0 too. Covers Authorization Code, Implicit and Hybrid flows. Also implements the following specifications: @@ -13,7 +13,6 @@ Also implements the following specifications: Before getting started there are some important things that you should know: * Despite that implementation MUST support TLS. You can make request without using SSL. There is no control on that. -* This library covers **Authorization Code Flow** and **Implicit Flow**, NO support for **Hybrid Flow** at this moment. * Supports only for requesting Claims using Scope values. -------------------------------------------------------------------------------- diff --git a/docs/sections/relyingparties.rst b/docs/sections/relyingparties.rst index f9ed7d3..754094a 100644 --- a/docs/sections/relyingparties.rst +++ b/docs/sections/relyingparties.rst @@ -18,14 +18,14 @@ Properties * ``client_type``: Values are ``confidential`` and ``public``. * ``client_id``: Client unique identifier. * ``client_secret``: Client secret for confidential applications. -* ``response_type``: Values are ``code``, ``id_token`` and ``id_token token``. +* ``response_type``: Values depends of wich flow you want use. * ``jwt_alg``: Clients can choose wich algorithm will be used to sign id_tokens. Values are ``HS256`` and ``RS256``. * ``date_created``: Date automatically added when created. Using the admin =============== -We suggest you to use Django admin to easily manage your clients: +We suggest you to use Django admin to easily manage your clients: .. image:: ../images/client_creation.png :align: center From 4dc0faed0c182181468fb6e0fdd939e333b696ef Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Thu, 8 Sep 2016 17:34:46 -0300 Subject: [PATCH 04/28] Edit CHANGELOG. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 864877a..d0a80e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ### [Unreleased] ##### Added +- Support for Hybrid Flow. - Polish translations. - Examples section in documentation. From 5836774f6b33ff9ac3fb6388c8868ce9798ac6e4 Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Fri, 9 Sep 2016 11:43:28 -0300 Subject: [PATCH 05/28] Add user email into id_token. Fix missing OIDC_TOKEN_EXPIRE setting. --- oidc_provider/lib/endpoints/authorize.py | 3 +-- oidc_provider/lib/utils/token.py | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/oidc_provider/lib/endpoints/authorize.py b/oidc_provider/lib/endpoints/authorize.py index 2b2862d..54af1a7 100644 --- a/oidc_provider/lib/endpoints/authorize.py +++ b/oidc_provider/lib/endpoints/authorize.py @@ -173,8 +173,7 @@ class AuthorizeEndpoint(object): query_fragment['token_type'] = 'bearer' - # TODO: Create setting 'OIDC_TOKEN_EXPIRE'. - query_fragment['expires_in'] = 60 * 10 + query_fragment['expires_in'] = settings.get('OIDC_TOKEN_EXPIRE') query_fragment['state'] = self.params.state if self.params.state else '' diff --git a/oidc_provider/lib/utils/token.py b/oidc_provider/lib/utils/token.py index 680ee64..635feaf 100644 --- a/oidc_provider/lib/utils/token.py +++ b/oidc_provider/lib/utils/token.py @@ -19,8 +19,7 @@ from oidc_provider import settings def create_id_token(user, aud, nonce, at_hash=None, request=None): """ - Receives a user object and aud (audience). - Then creates the id_token dictionary. + Creates the id_token dictionary. See: http://openid.net/specs/openid-connect-core-1_0.html#IDToken Return a dic. @@ -51,6 +50,9 @@ def create_id_token(user, aud, nonce, at_hash=None, request=None): if at_hash: dic['at_hash'] = at_hash + if getattr(user, 'email', None): + dic['email'] = user.email + processing_hook = settings.get('OIDC_IDTOKEN_PROCESSING_HOOK') if isinstance(processing_hook, (list, tuple)): From 8a63c8351440b7e77be381372d4e6b14f0d1252f Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Fri, 9 Sep 2016 13:10:12 -0300 Subject: [PATCH 06/28] Refactoring create_id_token function. --- CHANGELOG.md | 1 + oidc_provider/lib/endpoints/authorize.py | 1 + oidc_provider/lib/endpoints/token.py | 2 ++ oidc_provider/lib/utils/token.py | 4 +-- .../tests/test_authorize_endpoint.py | 29 ++++++++++++++----- oidc_provider/tests/test_token_endpoint.py | 7 +++-- oidc_provider/tests/test_userinfo_endpoint.py | 12 ++++++-- 7 files changed, 41 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0a80e1..c67d47a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. ##### Fixed - CORS in discovery and userinfo endpoint. - Client type public bug when created using the admin. +- Missing OIDC_TOKEN_EXPIRE setting on implicit flow. ### [0.3.7] - 2016-08-31 diff --git a/oidc_provider/lib/endpoints/authorize.py b/oidc_provider/lib/endpoints/authorize.py index 54af1a7..f6161b5 100644 --- a/oidc_provider/lib/endpoints/authorize.py +++ b/oidc_provider/lib/endpoints/authorize.py @@ -151,6 +151,7 @@ class AuthorizeEndpoint(object): 'aud': self.client.client_id, 'nonce': self.params.nonce, 'request': self.request, + 'scope': self.params.scope, } # Include at_hash when access_token is being returned. if 'access_token' in query_fragment: diff --git a/oidc_provider/lib/endpoints/token.py b/oidc_provider/lib/endpoints/token.py index a10c508..77cd636 100644 --- a/oidc_provider/lib/endpoints/token.py +++ b/oidc_provider/lib/endpoints/token.py @@ -153,6 +153,7 @@ class TokenEndpoint(object): nonce=self.code.nonce, at_hash=token.at_hash, request=self.request, + scope=self.params.scope, ) else: id_token_dic = {} @@ -188,6 +189,7 @@ class TokenEndpoint(object): nonce=None, at_hash=token.at_hash, request=self.request, + scope=self.params.scope, ) else: id_token_dic = {} diff --git a/oidc_provider/lib/utils/token.py b/oidc_provider/lib/utils/token.py index 635feaf..f07ad34 100644 --- a/oidc_provider/lib/utils/token.py +++ b/oidc_provider/lib/utils/token.py @@ -17,7 +17,7 @@ from oidc_provider.models import ( from oidc_provider import settings -def create_id_token(user, aud, nonce, at_hash=None, request=None): +def create_id_token(user, aud, nonce, at_hash=None, request=None, scope=[]): """ Creates the id_token dictionary. See: http://openid.net/specs/openid-connect-core-1_0.html#IDToken @@ -50,7 +50,7 @@ def create_id_token(user, aud, nonce, at_hash=None, request=None): if at_hash: dic['at_hash'] = at_hash - if getattr(user, 'email', None): + if ('email' in scope) and getattr(user, 'email', None): dic['email'] = user.email processing_hook = settings.get('OIDC_IDTOKEN_PROCESSING_HOOK') diff --git a/oidc_provider/tests/test_authorize_endpoint.py b/oidc_provider/tests/test_authorize_endpoint.py index bd05163..030d50b 100644 --- a/oidc_provider/tests/test_authorize_endpoint.py +++ b/oidc_provider/tests/test_authorize_endpoint.py @@ -11,7 +11,10 @@ import uuid from django.contrib.auth.models import AnonymousUser from django.core.management import call_command from django.core.urlresolvers import reverse -from django.test import RequestFactory +from django.test import ( + RequestFactory, + override_settings, +) from django.test import TestCase from jwkest.jwt import JWT @@ -457,12 +460,8 @@ class AuthorizationHybridFlowTestCase(TestCase, AuthorizeEndpointMixin): self.state = uuid.uuid4().hex self.nonce = uuid.uuid4().hex - def test_code_idtoken_token_response(self): - """ - Implicit client requesting `id_token token` receives both id token - and access token as the result of the authorization request. - """ - data = { + # Base data for the auth request. + self.data = { 'client_id': self.client_code_idtoken_token.client_id, 'redirect_uri': self.client_code_idtoken_token.default_redirect_uri, 'response_type': self.client_code_idtoken_token.response_type, @@ -472,7 +471,12 @@ class AuthorizationHybridFlowTestCase(TestCase, AuthorizeEndpointMixin): 'allow': 'Accept', } - response = self._auth_request('post', data, is_user_authenticated=True) + def test_code_idtoken_token_response(self): + """ + Implicit client requesting `id_token token` receives both id token + and access token as the result of the authorization request. + """ + response = self._auth_request('post', self.data, is_user_authenticated=True) self.assertIn('#', response['Location']) self.assertIn('access_token', response['Location']) @@ -485,3 +489,12 @@ class AuthorizationHybridFlowTestCase(TestCase, AuthorizeEndpointMixin): user=self.user, client=self.client_code_idtoken_token) self.assertEqual(is_code_ok, True, msg='Code returned is invalid.') + + @override_settings(OIDC_TOKEN_EXPIRE=36000) + def test_access_token_expiration(self): + """ + Add ten hours of expiration to access_token. Check for the expires_in query in fragment. + """ + response = self._auth_request('post', self.data, is_user_authenticated=True) + + self.assertIn('expires_in=36000', response['Location']) diff --git a/oidc_provider/tests/test_token_endpoint.py b/oidc_provider/tests/test_token_endpoint.py index 7c43122..7f705d6 100644 --- a/oidc_provider/tests/test_token_endpoint.py +++ b/oidc_provider/tests/test_token_endpoint.py @@ -10,7 +10,10 @@ except ImportError: from django.core.management import call_command from django.core.urlresolvers import reverse -from django.test import RequestFactory, override_settings +from django.test import ( + RequestFactory, + override_settings, +) from django.test import TestCase from django.utils import timezone from jwkest.jwk import KEYS @@ -148,7 +151,7 @@ class TokenTestCase(TestCase): self.assertEqual(response_dic['token_type'], 'bearer') self.assertEqual(response_dic['expires_in'], 720) self.assertEqual(id_token['sub'], str(self.user.id)) - self.assertEqual(id_token['aud'], self.client.client_id) + self.assertEqual(id_token['aud'], self.client.client_id); def test_refresh_token(self): """ diff --git a/oidc_provider/tests/test_userinfo_endpoint.py b/oidc_provider/tests/test_userinfo_endpoint.py index 78dc8d4..de95cd8 100644 --- a/oidc_provider/tests/test_userinfo_endpoint.py +++ b/oidc_provider/tests/test_userinfo_endpoint.py @@ -34,14 +34,20 @@ class UserInfoTestCase(TestCase): """ Generate a valid token. """ - id_token_dic = create_id_token(self.user, - self.client.client_id, FAKE_NONCE) + scope = ['openid', 'email'] + extra_scope + + id_token_dic = create_id_token( + user=self.user, + aud=self.client.client_id, + nonce=FAKE_NONCE, + scope=scope, + ) token = create_token( user=self.user, client=self.client, id_token_dic=id_token_dic, - scope=['openid', 'email'] + extra_scope) + scope=scope) token.save() return token From c14d2f055a638e4244f48f5957beb6a01d010df4 Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Fri, 9 Sep 2016 14:49:41 -0300 Subject: [PATCH 07/28] Remove Params() object from endpoints classes. --- oidc_provider/lib/endpoints/authorize.py | 102 ++++++++++------------- oidc_provider/lib/endpoints/token.py | 66 +++++++-------- oidc_provider/lib/utils/params.py | 7 -- oidc_provider/views.py | 33 ++++---- 4 files changed, 94 insertions(+), 114 deletions(-) delete mode 100644 oidc_provider/lib/utils/params.py diff --git a/oidc_provider/lib/endpoints/authorize.py b/oidc_provider/lib/endpoints/authorize.py index f6161b5..1844eb6 100644 --- a/oidc_provider/lib/endpoints/authorize.py +++ b/oidc_provider/lib/endpoints/authorize.py @@ -14,7 +14,6 @@ from oidc_provider.lib.errors import ( ClientIdError, RedirectUriError, ) -from oidc_provider.lib.utils.params import Params from oidc_provider.lib.utils.token import ( create_code, create_id_token, @@ -35,22 +34,22 @@ class AuthorizeEndpoint(object): def __init__(self, request): self.request = request - self.params = Params() + self.params = {} self._extract_params() # Determine which flow to use. - if self.params.response_type in ['code']: + if self.params['response_type'] in ['code']: self.grant_type = 'authorization_code' - elif self.params.response_type in ['id_token', 'id_token token', 'token']: + elif self.params['response_type'] in ['id_token', 'id_token token', 'token']: self.grant_type = 'implicit' - elif self.params.response_type in ['code token', 'code id_token', 'code id_token token']: + elif self.params['response_type'] in ['code token', 'code id_token', 'code id_token token']: self.grant_type = 'hybrid' else: self.grant_type = None # Determine if it's an OpenID Authentication request (or OAuth2). - self.is_authentication = 'openid' in self.params.scope + self.is_authentication = 'openid' in self.params['scope'] def _extract_params(self): """ @@ -64,58 +63,54 @@ class AuthorizeEndpoint(object): query_dict = (self.request.POST if self.request.method == 'POST' else self.request.GET) - self.params.client_id = query_dict.get('client_id', '') - self.params.redirect_uri = query_dict.get('redirect_uri', '') - self.params.response_type = query_dict.get('response_type', '') - self.params.scope = query_dict.get('scope', '').split() - self.params.state = query_dict.get('state', '') - - self.params.nonce = query_dict.get('nonce', '') - self.params.prompt = query_dict.get('prompt', '') - self.params.code_challenge = query_dict.get('code_challenge', '') - self.params.code_challenge_method = query_dict.get('code_challenge_method', '') + self.params['client_id'] = query_dict.get('client_id', '') + self.params['redirect_uri'] = query_dict.get('redirect_uri', '') + self.params['response_type'] = query_dict.get('response_type', '') + self.params['scope'] = query_dict.get('scope', '').split() + self.params['state'] = query_dict.get('state', '') + self.params['nonce'] = query_dict.get('nonce', '') + self.params['prompt'] = query_dict.get('prompt', '') + self.params['code_challenge'] = query_dict.get('code_challenge', '') + self.params['code_challenge_method'] = query_dict.get('code_challenge_method', '') def validate_params(self): # Client validation. try: - self.client = Client.objects.get(client_id=self.params.client_id) + self.client = Client.objects.get(client_id=self.params['client_id']) except Client.DoesNotExist: - logger.debug('[Authorize] Invalid client identifier: %s', self.params.client_id) + logger.debug('[Authorize] Invalid client identifier: %s', self.params['client_id']) raise ClientIdError() # Redirect URI validation. - if self.is_authentication and not self.params.redirect_uri: + if self.is_authentication and not self.params['redirect_uri']: logger.debug('[Authorize] Missing redirect uri.') raise RedirectUriError() - clean_redirect_uri = urlsplit(self.params.redirect_uri) + clean_redirect_uri = urlsplit(self.params['redirect_uri']) clean_redirect_uri = urlunsplit(clean_redirect_uri._replace(query='')) if not (clean_redirect_uri in self.client.redirect_uris): - logger.debug('[Authorize] Invalid redirect uri: %s', self.params.redirect_uri) + logger.debug('[Authorize] Invalid redirect uri: %s', self.params['redirect_uri']) raise RedirectUriError() # Grant type validation. if not self.grant_type: - logger.debug('[Authorize] Invalid response type: %s', self.params.response_type) - raise AuthorizeError(self.params.redirect_uri, 'unsupported_response_type', - self.grant_type) + logger.debug('[Authorize] Invalid response type: %s', self.params['response_type']) + raise AuthorizeError(self.params['redirect_uri'], 'unsupported_response_type', self.grant_type) # Nonce parameter validation. - if self.is_authentication and self.grant_type == 'implicit' and not self.params.nonce: - raise AuthorizeError(self.params.redirect_uri, 'invalid_request', - self.grant_type) + if self.is_authentication and self.grant_type == 'implicit' and not self.params['nonce']: + raise AuthorizeError(self.params['redirect_uri'], 'invalid_request', self.grant_type) # Response type parameter validation. - if self.is_authentication and self.params.response_type != self.client.response_type: - raise AuthorizeError(self.params.redirect_uri, 'invalid_request', - self.grant_type) + if self.is_authentication and self.params['response_type'] != self.client.response_type: + raise AuthorizeError(self.params['redirect_uri'], 'invalid_request', self.grant_type) # PKCE validation of the transformation method. - if self.params.code_challenge: - if not (self.params.code_challenge_method in ['plain', 'S256']): - raise AuthorizeError(self.params.redirect_uri, 'invalid_request', self.grant_type) + if self.params['code_challenge']: + if not (self.params['code_challenge_method'] in ['plain', 'S256']): + raise AuthorizeError(self.params['redirect_uri'], 'invalid_request', self.grant_type) def create_response_uri(self): - uri = urlsplit(self.params.redirect_uri) + uri = urlsplit(self.params['redirect_uri']) query_params = parse_qs(uri.query) query_fragment = parse_qs(uri.fragment) @@ -124,24 +119,24 @@ class AuthorizeEndpoint(object): code = create_code( user=self.request.user, client=self.client, - scope=self.params.scope, - nonce=self.params.nonce, + scope=self.params['scope'], + nonce=self.params['nonce'], is_authentication=self.is_authentication, - code_challenge=self.params.code_challenge, - code_challenge_method=self.params.code_challenge_method) + code_challenge=self.params['code_challenge'], + code_challenge_method=self.params['code_challenge_method']) code.save() if self.grant_type == 'authorization_code': query_params['code'] = code.code - query_params['state'] = self.params.state if self.params.state else '' + query_params['state'] = self.params['state'] if self.params['state'] else '' elif self.grant_type in ['implicit', 'hybrid']: token = create_token( user=self.request.user, client=self.client, - scope=self.params.scope) + scope=self.params['scope']) # Check if response_type must include access_token in the response. - if self.params.response_type in ['id_token token', 'token', 'code token', 'code id_token token']: + if self.params['response_type'] in ['id_token token', 'token', 'code token', 'code id_token token']: query_fragment['access_token'] = token.access_token # We don't need id_token if it's an OAuth2 request. @@ -149,9 +144,9 @@ class AuthorizeEndpoint(object): kwargs = { 'user': self.request.user, 'aud': self.client.client_id, - 'nonce': self.params.nonce, + 'nonce': self.params['nonce'], 'request': self.request, - 'scope': self.params.scope, + 'scope': self.params['scope'], } # Include at_hash when access_token is being returned. if 'access_token' in query_fragment: @@ -159,7 +154,7 @@ class AuthorizeEndpoint(object): id_token_dic = create_id_token(**kwargs) # Check if response_type must include id_token in the response. - if self.params.response_type in ['id_token', 'id_token token', 'code id_token', 'code id_token token']: + if self.params['response_type'] in ['id_token', 'id_token token', 'code id_token', 'code id_token token']: query_fragment['id_token'] = encode_id_token(id_token_dic, self.client) else: id_token_dic = {} @@ -176,14 +171,11 @@ class AuthorizeEndpoint(object): query_fragment['expires_in'] = settings.get('OIDC_TOKEN_EXPIRE') - query_fragment['state'] = self.params.state if self.params.state else '' + query_fragment['state'] = self.params['state'] if self.params['state'] else '' except Exception as error: logger.debug('[Authorize] Error when trying to create response uri: %s', error) - raise AuthorizeError( - self.params.redirect_uri, - 'server_error', - self.grant_type) + raise AuthorizeError(self.params['redirect_uri'], 'server_error', self.grant_type) uri = uri._replace(query=urlencode(query_params, doseq=True)) uri = uri._replace(fragment=urlencode(query_fragment, doseq=True)) @@ -208,7 +200,7 @@ class AuthorizeEndpoint(object): 'date_given': date_given, } ) - uc.scope = self.params.scope + uc.scope = self.params['scope'] # Rewrite expires_at and date_given if object already exists. if not created: @@ -225,10 +217,8 @@ class AuthorizeEndpoint(object): """ value = False try: - uc = UserConsent.objects.get(user=self.request.user, - client=self.client) - if (set(self.params.scope).issubset(uc.scope)) and \ - not (uc.has_expired()): + uc = UserConsent.objects.get(user=self.request.user, client=self.client) + if (set(self.params['scope']).issubset(uc.scope)) and not (uc.has_expired()): value = True except UserConsent.DoesNotExist: pass @@ -239,9 +229,9 @@ class AuthorizeEndpoint(object): """ Return a list with the description of all the scopes requested. """ - scopes = StandardScopeClaims.get_scopes_info(self.params.scope) + scopes = StandardScopeClaims.get_scopes_info(self.params['scope']) if settings.get('OIDC_EXTRA_SCOPE_CLAIMS'): - scopes_extra = settings.get('OIDC_EXTRA_SCOPE_CLAIMS', import_str=True).get_scopes_info(self.params.scope) + scopes_extra = settings.get('OIDC_EXTRA_SCOPE_CLAIMS', import_str=True).get_scopes_info(self.params['scope']) for index_extra, scope_extra in enumerate(scopes_extra): for index, scope in enumerate(scopes[:]): if scope_extra['scope'] == scope['scope']: diff --git a/oidc_provider/lib/endpoints/token.py b/oidc_provider/lib/endpoints/token.py index 77cd636..707f403 100644 --- a/oidc_provider/lib/endpoints/token.py +++ b/oidc_provider/lib/endpoints/token.py @@ -12,7 +12,6 @@ from django.http import JsonResponse from oidc_provider.lib.errors import ( TokenError, ) -from oidc_provider.lib.utils.params import Params from oidc_provider.lib.utils.token import ( create_id_token, create_token, @@ -33,23 +32,22 @@ class TokenEndpoint(object): def __init__(self, request): self.request = request - self.params = Params() + self.params = {} self._extract_params() def _extract_params(self): client_id, client_secret = self._extract_client_auth() - self.params.client_id = client_id - self.params.client_secret = client_secret - self.params.redirect_uri = unquote(self.request.POST.get('redirect_uri', '')) - self.params.grant_type = self.request.POST.get('grant_type', '') - self.params.code = self.request.POST.get('code', '') - self.params.state = self.request.POST.get('state', '') - self.params.scope = self.request.POST.get('scope', '') - self.params.refresh_token = self.request.POST.get('refresh_token', '') - - # PKCE parameters. - self.params.code_verifier = self.request.POST.get('code_verifier') + self.params['client_id'] = client_id + self.params['client_secret'] = client_secret + self.params['redirect_uri'] = unquote(self.request.POST.get('redirect_uri', '')) + self.params['grant_type'] = self.request.POST.get('grant_type', '') + self.params['code'] = self.request.POST.get('code', '') + self.params['state'] = self.request.POST.get('state', '') + self.params['scope'] = self.request.POST.get('scope', '') + self.params['refresh_token'] = self.request.POST.get('refresh_token', '') + # PKCE parameter. + self.params['code_verifier'] = self.request.POST.get('code_verifier') def _extract_client_auth(self): """ @@ -76,68 +74,68 @@ class TokenEndpoint(object): def validate_params(self): try: - self.client = Client.objects.get(client_id=self.params.client_id) + self.client = Client.objects.get(client_id=self.params['client_id']) except Client.DoesNotExist: - logger.debug('[Token] Client does not exist: %s', self.params.client_id) + logger.debug('[Token] Client does not exist: %s', self.params['client_id']) raise TokenError('invalid_client') if self.client.client_type == 'confidential': - if not (self.client.client_secret == self.params.client_secret): + if not (self.client.client_secret == self.params['client_secret']): logger.debug('[Token] Invalid client secret: client %s do not have secret %s', self.client.client_id, self.client.client_secret) raise TokenError('invalid_client') - if self.params.grant_type == 'authorization_code': - if not (self.params.redirect_uri in self.client.redirect_uris): - logger.debug('[Token] Invalid redirect uri: %s', self.params.redirect_uri) + if self.params['grant_type'] == 'authorization_code': + if not (self.params['redirect_uri'] in self.client.redirect_uris): + logger.debug('[Token] Invalid redirect uri: %s', self.params['redirect_uri']) raise TokenError('invalid_client') try: - self.code = Code.objects.get(code=self.params.code) + self.code = Code.objects.get(code=self.params['code']) except Code.DoesNotExist: - logger.debug('[Token] Code does not exist: %s', self.params.code) + logger.debug('[Token] Code does not exist: %s', self.params['code']) raise TokenError('invalid_grant') if not (self.code.client == self.client) \ or self.code.has_expired(): logger.debug('[Token] Invalid code: invalid client or code has expired', - self.params.redirect_uri) + self.params['redirect_uri']) raise TokenError('invalid_grant') # Validate PKCE parameters. - if self.params.code_verifier: + if self.params['code_verifier']: if self.code.code_challenge_method == 'S256': new_code_challenge = urlsafe_b64encode( - hashlib.sha256(self.params.code_verifier.encode('ascii')).digest() + hashlib.sha256(self.params['code_verifier'].encode('ascii')).digest() ).decode('utf-8').replace('=', '') else: - new_code_challenge = self.params.code_verifier + new_code_challenge = self.params['code_verifier'] # TODO: We should explain the error. if not (new_code_challenge == self.code.code_challenge): raise TokenError('invalid_grant') - elif self.params.grant_type == 'refresh_token': - if not self.params.refresh_token: + elif self.params['grant_type'] == 'refresh_token': + if not self.params['refresh_token']: logger.debug('[Token] Missing refresh token') raise TokenError('invalid_grant') try: - self.token = Token.objects.get(refresh_token=self.params.refresh_token, + self.token = Token.objects.get(refresh_token=self.params['refresh_token'], client=self.client) except Token.DoesNotExist: - logger.debug('[Token] Refresh token does not exist: %s', self.params.refresh_token) + logger.debug('[Token] Refresh token does not exist: %s', self.params['refresh_token']) raise TokenError('invalid_grant') else: - logger.debug('[Token] Invalid grant type: %s', self.params.grant_type) + logger.debug('[Token] Invalid grant type: %s', self.params['grant_type']) raise TokenError('unsupported_grant_type') def create_response_dic(self): - if self.params.grant_type == 'authorization_code': + if self.params['grant_type'] == 'authorization_code': return self.create_code_response_dic() - elif self.params.grant_type == 'refresh_token': + elif self.params['grant_type'] == 'refresh_token': return self.create_refresh_response_dic() def create_code_response_dic(self): @@ -153,7 +151,7 @@ class TokenEndpoint(object): nonce=self.code.nonce, at_hash=token.at_hash, request=self.request, - scope=self.params.scope, + scope=self.params['scope'], ) else: id_token_dic = {} @@ -189,7 +187,7 @@ class TokenEndpoint(object): nonce=None, at_hash=token.at_hash, request=self.request, - scope=self.params.scope, + scope=self.params['scope'], ) else: id_token_dic = {} diff --git a/oidc_provider/lib/utils/params.py b/oidc_provider/lib/utils/params.py deleted file mode 100644 index 961424a..0000000 --- a/oidc_provider/lib/utils/params.py +++ /dev/null @@ -1,7 +0,0 @@ - - -class Params(object): - """ - The purpose of this class is for accesing params via dot notation. - """ - pass \ No newline at end of file diff --git a/oidc_provider/views.py b/oidc_provider/views.py index 912c8fc..ec3fd65 100644 --- a/oidc_provider/views.py +++ b/oidc_provider/views.py @@ -46,24 +46,24 @@ class AuthorizeView(View): return hook_resp if settings.get('OIDC_SKIP_CONSENT_ALWAYS') and not (authorize.client.client_type == 'public') \ - and not (authorize.params.prompt == 'consent'): + and not (authorize.params['prompt'] == 'consent'): return redirect(authorize.create_response_uri()) if settings.get('OIDC_SKIP_CONSENT_ENABLE'): # Check if user previously give consent. if authorize.client_has_user_consent() and not (authorize.client.client_type == 'public') \ - and not (authorize.params.prompt == 'consent'): + and not (authorize.params['prompt'] == 'consent'): return redirect(authorize.create_response_uri()) - if authorize.params.prompt == 'none': - raise AuthorizeError(authorize.params.redirect_uri, 'interaction_required', authorize.grant_type) + if authorize.params['prompt'] == 'none': + raise AuthorizeError(authorize.params['redirect_uri'], 'interaction_required', authorize.grant_type) - if authorize.params.prompt == 'login': + if authorize.params['prompt'] == 'login': return redirect_to_login(request.get_full_path()) - if authorize.params.prompt == 'select_account': + if authorize.params['prompt'] == 'select_account': # TODO: see how we can support multiple accounts for the end-user. - raise AuthorizeError(authorize.params.redirect_uri, 'account_selection_required', authorize.grant_type) + raise AuthorizeError(authorize.params['redirect_uri'], 'account_selection_required', authorize.grant_type) # Generate hidden inputs for the form. context = { @@ -73,8 +73,8 @@ class AuthorizeView(View): # Remove `openid` from scope list # since we don't need to print it. - if 'openid' in authorize.params.scope: - authorize.params.scope.remove('openid') + if 'openid' in authorize.params['scope']: + authorize.params['scope'].remove('openid') context = { 'client': authorize.client, @@ -85,8 +85,8 @@ class AuthorizeView(View): return render(request, 'oidc_provider/authorize.html', context) else: - if authorize.params.prompt == 'none': - raise AuthorizeError(authorize.params.redirect_uri, 'login_required', authorize.grant_type) + if authorize.params['prompt'] == 'none': + raise AuthorizeError(authorize.params['redirect_uri'], 'login_required', authorize.grant_type) return redirect_to_login(request.get_full_path()) @@ -100,8 +100,8 @@ class AuthorizeView(View): except (AuthorizeError) as error: uri = error.create_uri( - authorize.params.redirect_uri, - authorize.params.state) + authorize.params['redirect_uri'], + authorize.params['state']) return redirect(uri) @@ -112,7 +112,7 @@ class AuthorizeView(View): authorize.validate_params() if not request.POST.get('allow'): - raise AuthorizeError(authorize.params.redirect_uri, + raise AuthorizeError(authorize.params['redirect_uri'], 'access_denied', authorize.grant_type) @@ -125,8 +125,8 @@ class AuthorizeView(View): except (AuthorizeError) as error: uri = error.create_uri( - authorize.params.redirect_uri, - authorize.params.state) + authorize.params['redirect_uri'], + authorize.params['state']) return redirect(uri) @@ -134,7 +134,6 @@ class AuthorizeView(View): class TokenView(View): def post(self, request, *args, **kwargs): - token = TokenEndpoint(request) try: From 1973ce1108bd53e87eb931c03254aeeb1da8d785 Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Fri, 9 Sep 2016 15:57:25 -0300 Subject: [PATCH 08/28] Add more attr to Client object. --- oidc_provider/admin.py | 12 +++++++ .../migrations/0020_auto_20160909_1836.py | 35 +++++++++++++++++++ .../migrations/0021_auto_20160909_1855.py | 20 +++++++++++ oidc_provider/models.py | 6 +++- 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 oidc_provider/migrations/0020_auto_20160909_1836.py create mode 100644 oidc_provider/migrations/0021_auto_20160909_1855.py diff --git a/oidc_provider/admin.py b/oidc_provider/admin.py index 71ee638..1a05dbd 100644 --- a/oidc_provider/admin.py +++ b/oidc_provider/admin.py @@ -4,6 +4,7 @@ from uuid import uuid4 from django.forms import ModelForm from django.contrib import admin +from django.utils.translation import ugettext_lazy as _ from oidc_provider.models import Client, Code, Token, RSAKey @@ -48,6 +49,17 @@ class ClientForm(ModelForm): @admin.register(Client) class ClientAdmin(admin.ModelAdmin): + fieldsets = [ + [_(u''), { + 'fields': ('name', 'client_type', 'response_type','_redirect_uris', 'jwt_alg'), + }], + [_(u'Credentials'), { + 'fields': ('client_id', 'client_secret'), + }], + [_(u'Information'), { + 'fields': ('contact_email', 'website_url', 'terms_url', 'logo', 'date_created'), + }], + ] form = ClientForm list_display = ['name', 'client_id', 'response_type', 'date_created'] readonly_fields = ['date_created'] diff --git a/oidc_provider/migrations/0020_auto_20160909_1836.py b/oidc_provider/migrations/0020_auto_20160909_1836.py new file mode 100644 index 0000000..d2eb663 --- /dev/null +++ b/oidc_provider/migrations/0020_auto_20160909_1836.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-09-09 18:36 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('oidc_provider', '0019_auto_20160908_1519'), + ] + + operations = [ + migrations.AddField( + model_name='client', + name='contact_email', + field=models.CharField(blank=True, default=b'', max_length=255, verbose_name='Contact Email'), + ), + migrations.AddField( + model_name='client', + name='logo', + field=models.FileField(blank=True, default=b'', upload_to=b'oidc_provider/clients', verbose_name='Logo Image'), + ), + migrations.AddField( + model_name='client', + name='terms_url', + field=models.CharField(blank=True, default=b'', help_text='External reference to the privacy policy of the client.', max_length=255, verbose_name='Terms URL'), + ), + migrations.AddField( + model_name='client', + name='website_url', + field=models.CharField(blank=True, default=b'', max_length=255, verbose_name='Website URL'), + ), + ] diff --git a/oidc_provider/migrations/0021_auto_20160909_1855.py b/oidc_provider/migrations/0021_auto_20160909_1855.py new file mode 100644 index 0000000..2115edd --- /dev/null +++ b/oidc_provider/migrations/0021_auto_20160909_1855.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-09-09 18:55 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('oidc_provider', '0020_auto_20160909_1836'), + ] + + operations = [ + migrations.AlterField( + model_name='client', + name='jwt_alg', + field=models.CharField(choices=[(b'HS256', b'HS256'), (b'RS256', b'RS256')], default=b'RS256', help_text='Algorithm used to encode ID Tokens.', max_length=10, verbose_name='JWT Algorithm'), + ), + ] diff --git a/oidc_provider/models.py b/oidc_provider/models.py index 3456e50..4167b96 100644 --- a/oidc_provider/models.py +++ b/oidc_provider/models.py @@ -37,8 +37,12 @@ class Client(models.Model): client_id = models.CharField(max_length=255, unique=True, verbose_name=_(u'Client ID')) client_secret = models.CharField(max_length=255, blank=True, default='', verbose_name=_(u'Client SECRET')) response_type = models.CharField(max_length=30, choices=RESPONSE_TYPE_CHOICES, verbose_name=_(u'Response Type')) - jwt_alg = models.CharField(max_length=10, choices=JWT_ALGS, default='RS256', verbose_name=_(u'JWT Algorithm')) + jwt_alg = models.CharField(max_length=10, choices=JWT_ALGS, default='RS256', verbose_name=_(u'JWT Algorithm'), help_text=_(u'Algorithm used to encode ID Tokens.')) date_created = models.DateField(auto_now_add=True, verbose_name=_(u'Date Created')) + website_url = models.CharField(max_length=255, blank=True, default='', verbose_name=_(u'Website URL')) + terms_url = models.CharField(max_length=255, blank=True, default='', verbose_name=_(u'Terms URL'), help_text=_(u'External reference to the privacy policy of the client.')) + contact_email = models.CharField(max_length=255, blank=True, default='', verbose_name=_(u'Contact Email')) + logo = models.FileField(blank=True, default='', upload_to='oidc_provider/clients', verbose_name=_(u'Logo Image')) _redirect_uris = models.TextField(default='', verbose_name=_(u'Redirect URIs'), help_text=_(u'Enter each URI on a new line.')) From e83a0199c0d543e40ea2d7b6eb39019453b6629f Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Fri, 9 Sep 2016 15:59:17 -0300 Subject: [PATCH 09/28] Edit CHANGELOG. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c67d47a..ab9ebd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ##### Added - Support for Hybrid Flow. +- New attributes for Clients: Website url, logo, contact email, terms url. - Polish translations. - Examples section in documentation. From 4971449684a1bc69aebf7fc2e22322f9c3922ff0 Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Mon, 12 Sep 2016 11:09:21 -0300 Subject: [PATCH 10/28] Create migration again. --- .../migrations/0018_auto_20160908_1627.py | 20 --------- .../0018_hybridflow_and_clientattrs.py | 45 +++++++++++++++++++ .../migrations/0019_auto_20160908_1519.py | 20 --------- .../migrations/0020_auto_20160909_1836.py | 35 --------------- .../migrations/0021_auto_20160909_1855.py | 20 --------- 5 files changed, 45 insertions(+), 95 deletions(-) delete mode 100644 oidc_provider/migrations/0018_auto_20160908_1627.py create mode 100644 oidc_provider/migrations/0018_hybridflow_and_clientattrs.py delete mode 100644 oidc_provider/migrations/0019_auto_20160908_1519.py delete mode 100644 oidc_provider/migrations/0020_auto_20160909_1836.py delete mode 100644 oidc_provider/migrations/0021_auto_20160909_1855.py diff --git a/oidc_provider/migrations/0018_auto_20160908_1627.py b/oidc_provider/migrations/0018_auto_20160908_1627.py deleted file mode 100644 index 9d9cc7b..0000000 --- a/oidc_provider/migrations/0018_auto_20160908_1627.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2016-09-08 16:27 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('oidc_provider', '0017_auto_20160811_1954'), - ] - - operations = [ - migrations.AlterField( - model_name='client', - name='response_type', - field=models.CharField(choices=[(b'code', b'code (Authorization Code Flow)'), (b'id_token', b'id_token (Implicit Flow)'), (b'id_token token', b'id_token token (Implicit Flow)'), (b'code token', b'code token (Hybrid Flow)'), (b'code id_token', b'code id_token (Hybrid Flow)'), (b'code id_token token', b'code id_token token (Hybrid Flow)')], max_length=30, verbose_name='Response Type'), - ), - ] diff --git a/oidc_provider/migrations/0018_hybridflow_and_clientattrs.py b/oidc_provider/migrations/0018_hybridflow_and_clientattrs.py new file mode 100644 index 0000000..c915cf8 --- /dev/null +++ b/oidc_provider/migrations/0018_hybridflow_and_clientattrs.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-09-12 14:08 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('oidc_provider', '0017_auto_20160811_1954'), + ] + + operations = [ + migrations.AddField( + model_name='client', + name='contact_email', + field=models.CharField(blank=True, default='', max_length=255, verbose_name='Contact Email'), + ), + migrations.AddField( + model_name='client', + name='logo', + field=models.FileField(blank=True, default='', upload_to='oidc_provider/clients', verbose_name='Logo Image'), + ), + migrations.AddField( + model_name='client', + name='terms_url', + field=models.CharField(blank=True, default='', help_text='External reference to the privacy policy of the client.', max_length=255, verbose_name='Terms URL'), + ), + migrations.AddField( + model_name='client', + name='website_url', + field=models.CharField(blank=True, default='', max_length=255, verbose_name='Website URL'), + ), + migrations.AlterField( + model_name='client', + name='jwt_alg', + field=models.CharField(choices=[('HS256', 'HS256'), ('RS256', 'RS256')], default='RS256', help_text='Algorithm used to encode ID Tokens.', max_length=10, verbose_name='JWT Algorithm'), + ), + migrations.AlterField( + model_name='client', + name='response_type', + field=models.CharField(choices=[('code', 'code (Authorization Code Flow)'), ('id_token', 'id_token (Implicit Flow)'), ('id_token token', 'id_token token (Implicit Flow)'), ('code token', 'code token (Hybrid Flow)'), ('code id_token', 'code id_token (Hybrid Flow)'), ('code id_token token', 'code id_token token (Hybrid Flow)')], max_length=30, verbose_name='Response Type'), + ), + ] diff --git a/oidc_provider/migrations/0019_auto_20160908_1519.py b/oidc_provider/migrations/0019_auto_20160908_1519.py deleted file mode 100644 index 320b30b..0000000 --- a/oidc_provider/migrations/0019_auto_20160908_1519.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('oidc_provider', '0018_auto_20160908_1627'), - ] - - operations = [ - migrations.AlterField( - model_name='client', - name='response_type', - field=models.CharField(choices=[('code', 'code (Authorization Code Flow)'), ('id_token', 'id_token (Implicit Flow)'), ('id_token token', 'id_token token (Implicit Flow)'), ('code token', 'code token (Hybrid Flow)'), ('code id_token', 'code id_token (Hybrid Flow)'), ('code id_token token', 'code id_token token (Hybrid Flow)')], verbose_name='Response Type', max_length=30), - preserve_default=True, - ), - ] diff --git a/oidc_provider/migrations/0020_auto_20160909_1836.py b/oidc_provider/migrations/0020_auto_20160909_1836.py deleted file mode 100644 index d2eb663..0000000 --- a/oidc_provider/migrations/0020_auto_20160909_1836.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2016-09-09 18:36 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('oidc_provider', '0019_auto_20160908_1519'), - ] - - operations = [ - migrations.AddField( - model_name='client', - name='contact_email', - field=models.CharField(blank=True, default=b'', max_length=255, verbose_name='Contact Email'), - ), - migrations.AddField( - model_name='client', - name='logo', - field=models.FileField(blank=True, default=b'', upload_to=b'oidc_provider/clients', verbose_name='Logo Image'), - ), - migrations.AddField( - model_name='client', - name='terms_url', - field=models.CharField(blank=True, default=b'', help_text='External reference to the privacy policy of the client.', max_length=255, verbose_name='Terms URL'), - ), - migrations.AddField( - model_name='client', - name='website_url', - field=models.CharField(blank=True, default=b'', max_length=255, verbose_name='Website URL'), - ), - ] diff --git a/oidc_provider/migrations/0021_auto_20160909_1855.py b/oidc_provider/migrations/0021_auto_20160909_1855.py deleted file mode 100644 index 2115edd..0000000 --- a/oidc_provider/migrations/0021_auto_20160909_1855.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9 on 2016-09-09 18:55 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('oidc_provider', '0020_auto_20160909_1836'), - ] - - operations = [ - migrations.AlterField( - model_name='client', - name='jwt_alg', - field=models.CharField(choices=[(b'HS256', b'HS256'), (b'RS256', b'RS256')], default=b'RS256', help_text='Algorithm used to encode ID Tokens.', max_length=10, verbose_name='JWT Algorithm'), - ), - ] From d125ed3955d15dbd71fd7f04dbc014ff49717bb1 Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Mon, 12 Sep 2016 12:38:44 -0300 Subject: [PATCH 11/28] Edit docs. --- docs/images/client_creation.png | Bin 51150 -> 119183 bytes docs/sections/relyingparties.rst | 8 ++++++++ 2 files changed, 8 insertions(+) diff --git a/docs/images/client_creation.png b/docs/images/client_creation.png index fac2105cbe1e94b73a3117d4899a1ab0bc453808..2b7fc8b9a66102c1fbd6262ef042708ed2c517cf 100644 GIT binary patch literal 119183 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%0DqAgNJ3t*RYY@g_t2F=XWcL)GtJ5v!q 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 LfJ$Gy{qR2kK_qV5 diff --git a/docs/sections/relyingparties.rst b/docs/sections/relyingparties.rst index 754094a..d55c58c 100644 --- a/docs/sections/relyingparties.rst +++ b/docs/sections/relyingparties.rst @@ -21,6 +21,14 @@ Properties * ``response_type``: Values depends of wich flow you want use. * ``jwt_alg``: Clients can choose wich algorithm will be used to sign id_tokens. Values are ``HS256`` and ``RS256``. * ``date_created``: Date automatically added when created. +* ``redirect_uris``: List of redirect URIs. + +Optional information: + +* ``website_url``: Website URL of your client. +* ``terms_url``: External reference to the privacy policy of the client. +* ``contact_email``: Contact email. +* ``logo``: Logo image. Using the admin =============== From a0f95446bbb39c004b126031ae6abda2352a5a45 Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Mon, 12 Sep 2016 13:07:12 -0300 Subject: [PATCH 12/28] Fix docs. --- docs/sections/settings.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sections/settings.rst b/docs/sections/settings.rst index e51194e..4679a95 100644 --- a/docs/sections/settings.rst +++ b/docs/sections/settings.rst @@ -95,7 +95,7 @@ Check out an example of how to implement it:: OIDC_IDTOKEN_EXPIRE =================== -OPTIONAL. ``int``. Token object expiration after been delivered. +OPTIONAL. ``int``. ID Token expiration after been delivered. Expressed in seconds. Default is ``60*10``. @@ -155,7 +155,7 @@ Expressed in days. Default is ``30*3``. OIDC_TOKEN_EXPIRE ================= -OPTIONAL. ``int``. Token object expiration after been created. +OPTIONAL. ``int``. Token object (access token) expiration after been created. Expressed in seconds. Default is ``60*60``. From f882efbad30f1e07d1478aeadd946f80e61e67ed Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Mon, 12 Sep 2016 13:19:35 -0300 Subject: [PATCH 13/28] Bump version v0.4.0. --- CHANGELOG.md | 2 ++ example_project/requirements.txt | 4 ++-- setup.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab9ebd2..b8a4d81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ### [Unreleased] +### [0.4.0] - 2016-09-12 + ##### Added - Support for Hybrid Flow. - New attributes for Clients: Website url, logo, contact email, terms url. diff --git a/example_project/requirements.txt b/example_project/requirements.txt index 7f5a1f8..412a8f5 100644 --- a/example_project/requirements.txt +++ b/example_project/requirements.txt @@ -1,2 +1,2 @@ -django==1.9 -https://github.com/juanifioren/django-oidc-provider/archive/v0.3.x.zip +django==1.10 +https://github.com/juanifioren/django-oidc-provider/archive/v0.4.x.zip diff --git a/setup.py b/setup.py index 7183e06..033da02 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) setup( name='django-oidc-provider', - version='0.3.7', + version='0.4.0', packages=[ 'oidc_provider', 'oidc_provider/lib', 'oidc_provider/lib/endpoints', 'oidc_provider/lib/utils', 'oidc_provider/tests', 'oidc_provider/tests/app', From 746dfbb04de8c20232459946f250898829b734d9 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Fiorentino Date: Mon, 12 Sep 2016 13:26:24 -0300 Subject: [PATCH 14/28] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 79a04c3..7a497fb 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![PyPI Versions](https://img.shields.io/pypi/v/django-oidc-provider.svg)](https://pypi.python.org/pypi/django-oidc-provider) [![Travis](https://travis-ci.org/juanifioren/django-oidc-provider.svg?branch=develop)](https://travis-ci.org/juanifioren/django-oidc-provider) [![PyPI Downloads](https://img.shields.io/pypi/dm/django-oidc-provider.svg)](https://pypi.python.org/pypi/django-oidc-provider) +[![Documentation Status](https://readthedocs.org/projects/django-oidc-provider/badge/?version=v0.4.x)](http://django-oidc-provider.readthedocs.io/en/v0.4.x/?badge=v0.4.x) ## About OpenID From d8bbb69407fa276bc02612faca7ea7ae2e0e4411 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Fiorentino Date: Mon, 12 Sep 2016 13:28:48 -0300 Subject: [PATCH 15/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a497fb..bad2d26 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Python Versions](https://img.shields.io/pypi/pyversions/django-oidc-provider.svg)](https://pypi.python.org/pypi/django-oidc-provider) [![PyPI Versions](https://img.shields.io/pypi/v/django-oidc-provider.svg)](https://pypi.python.org/pypi/django-oidc-provider) -[![Travis](https://travis-ci.org/juanifioren/django-oidc-provider.svg?branch=develop)](https://travis-ci.org/juanifioren/django-oidc-provider) +[![Travis](https://travis-ci.org/juanifioren/django-oidc-provider.svg?branch=v0.4.x)](https://travis-ci.org/juanifioren/django-oidc-provider) [![PyPI Downloads](https://img.shields.io/pypi/dm/django-oidc-provider.svg)](https://pypi.python.org/pypi/django-oidc-provider) [![Documentation Status](https://readthedocs.org/projects/django-oidc-provider/badge/?version=v0.4.x)](http://django-oidc-provider.readthedocs.io/en/v0.4.x/?badge=v0.4.x) From 1873a882d65d6a2e2acf4ad74bc806678062c7f1 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Fiorentino Date: Mon, 12 Sep 2016 14:34:08 -0300 Subject: [PATCH 16/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bad2d26..036d04a 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [![Python Versions](https://img.shields.io/pypi/pyversions/django-oidc-provider.svg)](https://pypi.python.org/pypi/django-oidc-provider) [![PyPI Versions](https://img.shields.io/pypi/v/django-oidc-provider.svg)](https://pypi.python.org/pypi/django-oidc-provider) +[![Documentation Status](https://readthedocs.org/projects/django-oidc-provider/badge/?version=v0.4.x)](http://django-oidc-provider.readthedocs.io/en/v0.4.x/?badge=v0.4.x) [![Travis](https://travis-ci.org/juanifioren/django-oidc-provider.svg?branch=v0.4.x)](https://travis-ci.org/juanifioren/django-oidc-provider) [![PyPI Downloads](https://img.shields.io/pypi/dm/django-oidc-provider.svg)](https://pypi.python.org/pypi/django-oidc-provider) -[![Documentation Status](https://readthedocs.org/projects/django-oidc-provider/badge/?version=v0.4.x)](http://django-oidc-provider.readthedocs.io/en/v0.4.x/?badge=v0.4.x) ## About OpenID From 68c88d21751cdb696d7d5caf110852b629806258 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Fiorentino Date: Mon, 12 Sep 2016 15:29:11 -0300 Subject: [PATCH 17/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 036d04a..a1bada4 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ OpenID Connect is a simple identity layer on top of the OAuth 2.0 protocol, whic Support for Python 3 and 2. Also latest versions of django. -[Read docs for more info](http://django-oidc-provider.readthedocs.org/) or [see the changelog here](https://github.com/juanifioren/django-oidc-provider/blob/master/CHANGELOG.md). +[Read docs for more info](http://django-oidc-provider.readthedocs.org/). ## Contributing From 20c63fe133077d7c53ba9b89ccc029bd8aa0cc80 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Fiorentino Date: Tue, 13 Sep 2016 14:50:29 -0300 Subject: [PATCH 18/28] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index a1bada4..fb2f8b1 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ [![PyPI Versions](https://img.shields.io/pypi/v/django-oidc-provider.svg)](https://pypi.python.org/pypi/django-oidc-provider) [![Documentation Status](https://readthedocs.org/projects/django-oidc-provider/badge/?version=v0.4.x)](http://django-oidc-provider.readthedocs.io/en/v0.4.x/?badge=v0.4.x) [![Travis](https://travis-ci.org/juanifioren/django-oidc-provider.svg?branch=v0.4.x)](https://travis-ci.org/juanifioren/django-oidc-provider) -[![PyPI Downloads](https://img.shields.io/pypi/dm/django-oidc-provider.svg)](https://pypi.python.org/pypi/django-oidc-provider) ## About OpenID From 976f254524759cda02956a74d9888f99b74823e2 Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Mon, 19 Sep 2016 17:38:17 -0300 Subject: [PATCH 19/28] Improve Docs. --- docs/sections/claims.rst | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/sections/claims.rst b/docs/sections/claims.rst index 78160f7..d4eb38f 100644 --- a/docs/sections/claims.rst +++ b/docs/sections/claims.rst @@ -5,9 +5,9 @@ Standard Claims This subset of OpenID Connect defines a set of standard Claims. They are returned in the UserInfo Response. -The package comes with a setting called ``OIDC_USERINFO``, basically it refers to a class that MUST have a class-method named ``get_by_user``, this will be called with a Django ``User`` instance and returns an object with all the claims of the user as attributes. +The package comes with a setting called ``OIDC_USERINFO``, basically it refers to a function that will be called with ``claims`` (dict) and ``user`` (user instance). It returns the ``claims`` dict with all the claims populated. -List of all the attributes grouped by scopes: +List of all the ``claims`` keys grouped by scopes: +--------------------+----------------+-----------------------+------------------------+ | profile | email | phone | address | @@ -41,15 +41,18 @@ List of all the attributes grouped by scopes: | updated_at | | | | +--------------------+----------------+-----------------------+------------------------+ +How to populate userinfo response +================================= + Somewhere in your Django ``settings.py``:: OIDC_USERINFO = 'myproject.oidc_provider_settings.userinfo' -Then create the function for the ``OIDC_USERINFO`` setting:: +Then inside your ``oidc_provider_settings.py`` file create the function for the ``OIDC_USERINFO`` setting:: def userinfo(claims, user): - + # Populate claims dict. claims['name'] = '{0} {1}'.format(user.first_name, user.last_name) claims['given_name'] = user.first_name claims['family_name'] = user.last_name @@ -58,5 +61,7 @@ Then create the function for the ``OIDC_USERINFO`` setting:: return claims +Now test an Authorization Request using these scopes ``openid profile email`` and see how user attributes are returned. + .. note:: Please **DO NOT** add extra keys or delete the existing ones in the ``claims`` dict. If you want to add extra claims to some scopes you can use the ``OIDC_EXTRA_SCOPE_CLAIMS`` setting. From 72bdeb0b3cb07ba8286827ae66b3553717cc3fb3 Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Mon, 19 Sep 2016 18:05:29 -0300 Subject: [PATCH 20/28] Update docs. --- docs/index.rst | 2 +- .../sections/{claims.rst => scopesclaims.rst} | 55 +++++++++++++++++-- docs/sections/settings.rst | 43 +-------------- 3 files changed, 53 insertions(+), 47 deletions(-) rename docs/sections/{claims.rst => scopesclaims.rst} (66%) diff --git a/docs/index.rst b/docs/index.rst index afa58b4..e30bd3b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -26,7 +26,7 @@ Contents: sections/relyingparties sections/serverkeys sections/templates - sections/claims + sections/scopesclaims sections/userconsent sections/oauth2 sections/settings diff --git a/docs/sections/claims.rst b/docs/sections/scopesclaims.rst similarity index 66% rename from docs/sections/claims.rst rename to docs/sections/scopesclaims.rst index d4eb38f..9a3309d 100644 --- a/docs/sections/claims.rst +++ b/docs/sections/scopesclaims.rst @@ -1,7 +1,7 @@ -.. _claims: +.. _scopesclaims: -Standard Claims -############### +Scopes and Claims +################# This subset of OpenID Connect defines a set of standard Claims. They are returned in the UserInfo Response. @@ -41,8 +41,8 @@ List of all the ``claims`` keys grouped by scopes: | updated_at | | | | +--------------------+----------------+-----------------------+------------------------+ -How to populate userinfo response -================================= +How to populate standard claims +=============================== Somewhere in your Django ``settings.py``:: @@ -65,3 +65,48 @@ Now test an Authorization Request using these scopes ``openid profile email`` an .. note:: Please **DO NOT** add extra keys or delete the existing ones in the ``claims`` dict. If you want to add extra claims to some scopes you can use the ``OIDC_EXTRA_SCOPE_CLAIMS`` setting. + +How to add custom scopes and claims +=================================== + +The ``OIDC_EXTRA_SCOPE_CLAIMS`` setting is used to add extra scopes specific for your app. Is just a class that inherit from ``oidc_provider.lib.claims.ScopeClaims``. You can create or modify scopes by adding this methods into it: + +* ``info_scopename`` class property for setting the verbose name and description. +* ``scope_scopename`` method for returning some information related. + +Let's say that you want add your custom ``foo`` scope for your OAuth2/OpenID provider. So when a client (RP) makes an Authorization Request containing ``foo`` in the list of scopes, it will be listed in the consent page (``templates/oidc_provider/authorize.html``) and then some specific claims like ``bar`` will be returned from the ``/userinfo`` response. + +Somewhere in your Django ``settings.py``:: + + OIDC_USERINFO = 'yourproject.oidc_provider_settings.CustomScopeClaims' + +Inside your oidc_provider_settings.py file add the following class:: + + from django.utils.translation import ugettext as _ + from oidc_provider.lib.claims import ScopeClaims + + class CustomScopeClaims(ScopeClaims): + + info_foo = ( + _(u'Foo'), + _(u'Some description for the scope.'), + ) + + def scope_foo(self): + # self.user - Django user instance. + # self.userinfo - Dict returned by OIDC_USERINFO function. + # self.scopes - List of scopes requested. + dic = { + 'bar': 'Something dynamic here', + } + + return dic + + # If you want to change the description of the profile scope, you can redefine it. + info_profile = ( + _(u'Profile'), + _(u'Another description.'), + ) + +.. note:: + If a field is empty or ``None`` inside the dictionary your return on ``scope_scopename`` method, it will be cleaned from the response. diff --git a/docs/sections/settings.rst b/docs/sections/settings.rst index 4679a95..b4afc90 100644 --- a/docs/sections/settings.rst +++ b/docs/sections/settings.rst @@ -48,49 +48,10 @@ OIDC_EXTRA_SCOPE_CLAIMS OPTIONAL. ``str``. A string with the location of your class. Default is ``oidc_provider.lib.claims.ScopeClaims``. -Used to add extra scopes specific for your app. This class MUST inherit ``ScopeClaims``. +Used to add extra scopes specific for your app. OpenID Connect RP's will use scope values to specify what access privileges are being requested for Access Tokens. -OpenID Connect Clients will use scope values to specify what access privileges are being requested for Access Tokens. +Read more about how to implement it in :ref:`scopesclaims` section. -`Here `_ you have the standard scopes defined by the protocol. - -You can create or modify scopes using: - -* ``info_scopename`` class property for setting the verbose name and description. -* ``scope_scopename`` method for returning some information related. - -Check out an example of how to implement it:: - - from django.utils.translation import ugettext as _ - from oidc_provider.lib.claims import ScopeClaims - - class MyAppScopeClaims(ScopeClaims): - - info_books = ( - _(u'Books'), # Verbose name of the scope. - _(u'Access to your books.'), # Description of the scope. - ) - - def scope_books(self): - # Here, for example, you can search books for this user. - # self.user - Django user instance. - # self.userinfo - Instance of your custom OIDC_USERINFO class. - # self.scopes - List of scopes requested. - - dic = { - 'books_readed': books_readed_count, - } - - return dic - - # If you want to change the description of the profile scope, you can redefine it. - info_profile = ( - _(u'Profile'), - _(u'Another description.'), - ) - -.. note:: - If a field is empty or ``None`` inside the dictionary your return on ``scope_scopename`` method, it will be cleaned from the response. OIDC_IDTOKEN_EXPIRE =================== From ea4609c08ced6890f944d555500a7d4e891086f0 Mon Sep 17 00:00:00 2001 From: Juan Ignacio Fiorentino Date: Tue, 20 Sep 2016 15:29:35 -0300 Subject: [PATCH 21/28] Fix typo in docs. --- docs/sections/scopesclaims.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sections/scopesclaims.rst b/docs/sections/scopesclaims.rst index 9a3309d..1973f74 100644 --- a/docs/sections/scopesclaims.rst +++ b/docs/sections/scopesclaims.rst @@ -109,4 +109,4 @@ Inside your oidc_provider_settings.py file add the following class:: ) .. note:: - If a field is empty or ``None`` inside the dictionary your return on ``scope_scopename`` method, it will be cleaned from the response. + If a field is empty or ``None`` inside the dictionary you return on the ``scope_scopename`` method, it will be cleaned from the response. From 1c3bd7579b35b70275716bc9b3f61e3bd227f021 Mon Sep 17 00:00:00 2001 From: yasserisa Date: Wed, 21 Sep 2016 10:20:03 -0300 Subject: [PATCH 22/28] FIX id_token, field iss set to type str --- oidc_provider/lib/utils/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oidc_provider/lib/utils/common.py b/oidc_provider/lib/utils/common.py index 6b625e3..236c12a 100644 --- a/oidc_provider/lib/utils/common.py +++ b/oidc_provider/lib/utils/common.py @@ -42,7 +42,7 @@ def get_issuer(site_url=None, request=None): .split('/.well-known/openid-configuration')[0] issuer = site_url + path - return issuer + return str(issuer) def default_userinfo(claims, user): From ac6fc66770e1f7f290bb06a2bb71821ed483463c Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Mon, 26 Sep 2016 17:15:32 -0300 Subject: [PATCH 23/28] Improve docs. --- docs/index.rst | 1 + docs/sections/accesstokens.rst | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 docs/sections/accesstokens.rst diff --git a/docs/index.rst b/docs/index.rst index e30bd3b..2c6a3b3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -29,6 +29,7 @@ Contents: sections/scopesclaims sections/userconsent sections/oauth2 + sections/accesstokens sections/settings sections/examples sections/contribute diff --git a/docs/sections/accesstokens.rst b/docs/sections/accesstokens.rst new file mode 100644 index 0000000..1aff36f --- /dev/null +++ b/docs/sections/accesstokens.rst @@ -0,0 +1,48 @@ +.. _accesstokens: + +Access Tokens +############# + +At the end of the login process, an access token is generated. This access token is the thing that's passed along with every API call (e.g. userinfo endpoint) as proof that the call was made by a specific person from a specific app. + +Access tokens generally have a lifetime of only a couple of hours, you can use ``OIDC_TOKEN_EXPIRE`` to set custom expiration that suit your needs. + +Obtaining an Access token +========================= + +Go to the admin site and create a public client with a response_type ``id_token token`` and a redirect_uri ``http://example.org/``. + +Open your browser and accept consent at:: + + http://localhost:8000/authorize?client_id=651462&redirect_uri=http://example.org/&response_type=code&scope=openid email profile&state=123123 + +In the redirected URL you should have a ``code`` parameter included as query string:: + + http://example.org/?code=b9cedb346ee04f15ab1d3ac13da92002&state=123123 + +We use ``code`` value to obtain ``access_token`` and ``refresh_token``:: + + curl -X POST -H "Authorization: Basic NjUxNDYyOjM3YjFjNGZmODI2ZjhkNzhiZDQ1ZTI1YmFkNzVhMmMw" -H "Cache-Control: no-cache" -H "Content-Type: multipart/form-data" -F "code=b9cedb346ee04f15ab1d3ac13da92002" -F "redirect_uri=http://example.org/" -F "grant_type=authorization_code" "http://localhost:8000/token/" + +Example response:: + + { + "access_token": "82b35f3d810f4cf49dd7a52d4b22a594", + "token_type": "bearer", + "expires_in": 3600, + "refresh_token": "0bac2d80d75d46658b0b31d3778039bb", + "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6..." + } + +Then you can grab the access token and ask user data by doing a GET request to the ``/userinfo`` endpoint:: + + http://localhost:8000/userinfo/?access_token=82b35f3d810f4cf49dd7a52d4b22a594 + +Expiration and Refresh of Access Tokens +======================================= + +If you receive a ``401 Unauthorized`` status when issuing access token probably means that has expired. + +The RP application obtains a new access token by sending a POST request to the ``/token`` endpoint with the following request parameters:: + + curl -X POST -H "Cache-Control: no-cache" -H "Content-Type: multipart/form-data" -F "client_id=651462" -F "grant_type=refresh_token" -F "refresh_token=0bac2d80d75d46658b0b31d3778039bb" "http://localhost:8000/token/" From 510a54a0d577a4e7614d1e236e8ac3cc59922e6d Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Tue, 27 Sep 2016 12:56:26 -0300 Subject: [PATCH 24/28] Fix docs. --- docs/sections/accesstokens.rst | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/sections/accesstokens.rst b/docs/sections/accesstokens.rst index 1aff36f..4e0876a 100644 --- a/docs/sections/accesstokens.rst +++ b/docs/sections/accesstokens.rst @@ -10,7 +10,7 @@ Access tokens generally have a lifetime of only a couple of hours, you can use ` Obtaining an Access token ========================= -Go to the admin site and create a public client with a response_type ``id_token token`` and a redirect_uri ``http://example.org/``. +Go to the admin site and create a public client with ``response_type = code`` and ``redirect_uri = http://example.org/``. Open your browser and accept consent at:: @@ -22,7 +22,15 @@ In the redirected URL you should have a ``code`` parameter included as query str We use ``code`` value to obtain ``access_token`` and ``refresh_token``:: - curl -X POST -H "Authorization: Basic NjUxNDYyOjM3YjFjNGZmODI2ZjhkNzhiZDQ1ZTI1YmFkNzVhMmMw" -H "Cache-Control: no-cache" -H "Content-Type: multipart/form-data" -F "code=b9cedb346ee04f15ab1d3ac13da92002" -F "redirect_uri=http://example.org/" -F "grant_type=authorization_code" "http://localhost:8000/token/" + curl -X POST \ + -H "Cache-Control: no-cache" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + "http://localhost:8000/token/" \ + -d "client_id=651462" \ + -d "client_secret=37b1c4ff826f8d78bd45e25bad75a2c0" \ + -d "code=b9cedb346ee04f15ab1d3ac13da92002" \ + -d "redirect_uri=http://example.org/" \ + -d "grant_type=authorization_code" Example response:: @@ -36,7 +44,9 @@ Example response:: Then you can grab the access token and ask user data by doing a GET request to the ``/userinfo`` endpoint:: - http://localhost:8000/userinfo/?access_token=82b35f3d810f4cf49dd7a52d4b22a594 + curl -X GET \ + -H "Cache-Control: no-cache" \ + "http://localhost:8000/userinfo/?access_token=82b35f3d810f4cf49dd7a52d4b22a594" Expiration and Refresh of Access Tokens ======================================= @@ -45,4 +55,10 @@ If you receive a ``401 Unauthorized`` status when issuing access token probably The RP application obtains a new access token by sending a POST request to the ``/token`` endpoint with the following request parameters:: - curl -X POST -H "Cache-Control: no-cache" -H "Content-Type: multipart/form-data" -F "client_id=651462" -F "grant_type=refresh_token" -F "refresh_token=0bac2d80d75d46658b0b31d3778039bb" "http://localhost:8000/token/" + curl -X POST \ + -H "Cache-Control: no-cache" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + "http://localhost:8000/token/" \ + -d "client_id=651462" \ + -d "grant_type=refresh_token" \ + -d "refresh_token=0bac2d80d75d46658b0b31d3778039bb" From ddb8015987051dfdba2023763290a0aee9e0d898 Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Tue, 27 Sep 2016 13:07:39 -0300 Subject: [PATCH 25/28] Fix docs. --- docs/sections/accesstokens.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sections/accesstokens.rst b/docs/sections/accesstokens.rst index 4e0876a..1a0c33d 100644 --- a/docs/sections/accesstokens.rst +++ b/docs/sections/accesstokens.rst @@ -10,7 +10,7 @@ Access tokens generally have a lifetime of only a couple of hours, you can use ` Obtaining an Access token ========================= -Go to the admin site and create a public client with ``response_type = code`` and ``redirect_uri = http://example.org/``. +Go to the admin site and create a confidential client with ``response_type = code`` and ``redirect_uri = http://example.org/``. Open your browser and accept consent at:: From 18de0b7b2212fba22289c5a5f1699f5ae8d5a890 Mon Sep 17 00:00:00 2001 From: Erick Navarro Date: Tue, 27 Sep 2016 19:57:24 -0500 Subject: [PATCH 26/28] Fix import django.conf.urls.patterns is removed in Django 1.10 --- example_project/myapp/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example_project/myapp/urls.py b/example_project/myapp/urls.py index 12e6abf..91d31fa 100644 --- a/example_project/myapp/urls.py +++ b/example_project/myapp/urls.py @@ -1,5 +1,5 @@ from django.contrib.auth import views as auth_views -from django.conf.urls import patterns, include, url +from django.conf.urls import include, url from django.contrib import admin from django.views.generic import TemplateView From 6ed2c200a7a9b2e064a3f68de0b76025ca04b8e6 Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Mon, 3 Oct 2016 12:54:54 -0300 Subject: [PATCH 27/28] Update pyjwkest to version 1.3.0. --- oidc_provider/lib/utils/token.py | 2 +- oidc_provider/management/commands/creatersakey.py | 2 +- oidc_provider/views.py | 2 +- setup.py | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/oidc_provider/lib/utils/token.py b/oidc_provider/lib/utils/token.py index f07ad34..781dac0 100644 --- a/oidc_provider/lib/utils/token.py +++ b/oidc_provider/lib/utils/token.py @@ -2,7 +2,7 @@ from datetime import timedelta import time import uuid -from Crypto.PublicKey.RSA import importKey +from Cryptodome.PublicKey.RSA import importKey from django.utils import timezone from jwkest.jwk import RSAKey as jwk_RSAKey from jwkest.jwk import SYMKey diff --git a/oidc_provider/management/commands/creatersakey.py b/oidc_provider/management/commands/creatersakey.py index 1768f5e..1dc1a2c 100644 --- a/oidc_provider/management/commands/creatersakey.py +++ b/oidc_provider/management/commands/creatersakey.py @@ -1,6 +1,6 @@ import os -from Crypto.PublicKey import RSA +from Cryptodome.PublicKey import RSA from django.core.management.base import BaseCommand from oidc_provider import settings diff --git a/oidc_provider/views.py b/oidc_provider/views.py index ec3fd65..427a1e5 100644 --- a/oidc_provider/views.py +++ b/oidc_provider/views.py @@ -1,6 +1,6 @@ import logging -from Crypto.PublicKey import RSA +from Cryptodome.PublicKey import RSA from django.contrib.auth.views import redirect_to_login, logout from django.core.urlresolvers import reverse from django.http import JsonResponse diff --git a/setup.py b/setup.py index 033da02..d4efa1a 100644 --- a/setup.py +++ b/setup.py @@ -36,11 +36,11 @@ setup( ], test_suite='runtests.runtests', tests_require=[ - 'pyjwkest==1.1.0', + 'pyjwkest==1.3.0', 'mock==2.0.0', ], install_requires=[ - 'pyjwkest==1.1.0', + 'pyjwkest==1.3.0', ], ) From aceb9d6c24433a15ef65ce2cfcfc0eb23fc1af9f Mon Sep 17 00:00:00 2001 From: Ignacio Fiorentino Date: Mon, 3 Oct 2016 12:57:39 -0300 Subject: [PATCH 28/28] Bump version v0.4.1. --- CHANGELOG.md | 6 ++++++ setup.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8a4d81..eeb57bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ### [Unreleased] +### [0.4.1] - 2016-10-03 + +##### Changed +- Update pyjwkest to version 1.3.0. +- Use Cryptodome instead of Crypto lib. + ### [0.4.0] - 2016-09-12 ##### Added diff --git a/setup.py b/setup.py index d4efa1a..851de13 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) setup( name='django-oidc-provider', - version='0.4.0', + version='0.4.1', packages=[ 'oidc_provider', 'oidc_provider/lib', 'oidc_provider/lib/endpoints', 'oidc_provider/lib/utils', 'oidc_provider/tests', 'oidc_provider/tests/app',