commit
45717a82f2
8 changed files with 340 additions and 106 deletions
|
@ -78,7 +78,7 @@ Let's say that you want add your custom ``foo`` scope for your OAuth2/OpenID pro
|
||||||
|
|
||||||
Somewhere in your Django ``settings.py``::
|
Somewhere in your Django ``settings.py``::
|
||||||
|
|
||||||
OIDC_USERINFO = 'yourproject.oidc_provider_settings.CustomScopeClaims'
|
OIDC_EXTRA_SCOPE_CLAIMS = 'yourproject.oidc_provider_settings.CustomScopeClaims'
|
||||||
|
|
||||||
Inside your oidc_provider_settings.py file add the following class::
|
Inside your oidc_provider_settings.py file add the following class::
|
||||||
|
|
||||||
|
|
|
@ -30,12 +30,13 @@ from oidc_provider.models import (
|
||||||
UserConsent,
|
UserConsent,
|
||||||
)
|
)
|
||||||
from oidc_provider import settings
|
from oidc_provider import settings
|
||||||
from oidc_provider.lib.utils.common import cleanup_url_from_query_string, get_browser_state_or_default
|
from oidc_provider.lib.utils.common import get_browser_state_or_default
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AuthorizeEndpoint(object):
|
class AuthorizeEndpoint(object):
|
||||||
|
_allowed_prompt_params = {'none', 'login', 'consent', 'select_account'}
|
||||||
|
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
self.request = request
|
self.request = request
|
||||||
|
@ -74,7 +75,9 @@ class AuthorizeEndpoint(object):
|
||||||
self.params['scope'] = query_dict.get('scope', '').split()
|
self.params['scope'] = query_dict.get('scope', '').split()
|
||||||
self.params['state'] = query_dict.get('state', '')
|
self.params['state'] = query_dict.get('state', '')
|
||||||
self.params['nonce'] = query_dict.get('nonce', '')
|
self.params['nonce'] = query_dict.get('nonce', '')
|
||||||
self.params['prompt'] = query_dict.get('prompt', '')
|
|
||||||
|
self.params['prompt'] = self._allowed_prompt_params.intersection(set(query_dict.get('prompt', '').split()))
|
||||||
|
|
||||||
self.params['code_challenge'] = query_dict.get('code_challenge', '')
|
self.params['code_challenge'] = query_dict.get('code_challenge', '')
|
||||||
self.params['code_challenge_method'] = query_dict.get('code_challenge_method', '')
|
self.params['code_challenge_method'] = query_dict.get('code_challenge_method', '')
|
||||||
|
|
||||||
|
@ -90,8 +93,7 @@ class AuthorizeEndpoint(object):
|
||||||
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.')
|
logger.debug('[Authorize] Missing redirect uri.')
|
||||||
raise RedirectUriError()
|
raise RedirectUriError()
|
||||||
clean_redirect_uri = cleanup_url_from_query_string(self.params['redirect_uri'])
|
if not (self.params['redirect_uri'] in self.client.redirect_uris):
|
||||||
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()
|
raise RedirectUriError()
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ import hashlib
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from oidc_provider.lib.utils.common import cleanup_url_from_query_string
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
@ -43,8 +42,7 @@ class TokenEndpoint(object):
|
||||||
|
|
||||||
self.params['client_id'] = client_id
|
self.params['client_id'] = client_id
|
||||||
self.params['client_secret'] = client_secret
|
self.params['client_secret'] = client_secret
|
||||||
self.params['redirect_uri'] = unquote(
|
self.params['redirect_uri'] = self.request.POST.get('redirect_uri', '')
|
||||||
self.request.POST.get('redirect_uri', '').split('?', 1)[0])
|
|
||||||
self.params['grant_type'] = self.request.POST.get('grant_type', '')
|
self.params['grant_type'] = self.request.POST.get('grant_type', '')
|
||||||
self.params['code'] = self.request.POST.get('code', '')
|
self.params['code'] = self.request.POST.get('code', '')
|
||||||
self.params['state'] = self.request.POST.get('state', '')
|
self.params['state'] = self.request.POST.get('state', '')
|
||||||
|
@ -93,8 +91,7 @@ class TokenEndpoint(object):
|
||||||
raise TokenError('invalid_client')
|
raise TokenError('invalid_client')
|
||||||
|
|
||||||
if self.params['grant_type'] == 'authorization_code':
|
if self.params['grant_type'] == 'authorization_code':
|
||||||
clean_redirect_uri = cleanup_url_from_query_string(self.params['redirect_uri'])
|
if not (self.params['redirect_uri'] in self.client.redirect_uris):
|
||||||
if not (clean_redirect_uri in self.client.redirect_uris):
|
|
||||||
logger.debug('[Token] Invalid redirect uri: %s', self.params['redirect_uri'])
|
logger.debug('[Token] Invalid redirect uri: %s', self.params['redirect_uri'])
|
||||||
raise TokenError('invalid_client')
|
raise TokenError('invalid_client')
|
||||||
|
|
||||||
|
@ -162,6 +159,8 @@ class TokenEndpoint(object):
|
||||||
return self.create_access_token_response_dic()
|
return self.create_access_token_response_dic()
|
||||||
|
|
||||||
def create_access_token_response_dic(self):
|
def create_access_token_response_dic(self):
|
||||||
|
# See https://tools.ietf.org/html/rfc6749#section-4.3
|
||||||
|
|
||||||
token = create_token(
|
token = create_token(
|
||||||
self.user,
|
self.user,
|
||||||
self.client,
|
self.client,
|
||||||
|
@ -173,7 +172,7 @@ class TokenEndpoint(object):
|
||||||
nonce='self.code.nonce',
|
nonce='self.code.nonce',
|
||||||
at_hash=token.at_hash,
|
at_hash=token.at_hash,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
scope=self.params['scope'],
|
scope=token.scope,
|
||||||
)
|
)
|
||||||
|
|
||||||
token.id_token = id_token_dic
|
token.id_token = id_token_dic
|
||||||
|
@ -188,6 +187,8 @@ class TokenEndpoint(object):
|
||||||
}
|
}
|
||||||
|
|
||||||
def create_code_response_dic(self):
|
def create_code_response_dic(self):
|
||||||
|
# See https://tools.ietf.org/html/rfc6749#section-4.1
|
||||||
|
|
||||||
token = create_token(
|
token = create_token(
|
||||||
user=self.code.user,
|
user=self.code.user,
|
||||||
client=self.code.client,
|
client=self.code.client,
|
||||||
|
@ -200,7 +201,7 @@ class TokenEndpoint(object):
|
||||||
nonce=self.code.nonce,
|
nonce=self.code.nonce,
|
||||||
at_hash=token.at_hash,
|
at_hash=token.at_hash,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
scope=self.params['scope'],
|
scope=token.scope,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
id_token_dic = {}
|
id_token_dic = {}
|
||||||
|
@ -223,10 +224,18 @@ class TokenEndpoint(object):
|
||||||
return dic
|
return dic
|
||||||
|
|
||||||
def create_refresh_response_dic(self):
|
def create_refresh_response_dic(self):
|
||||||
|
# See https://tools.ietf.org/html/rfc6749#section-6
|
||||||
|
|
||||||
|
scope_param = self.params['scope']
|
||||||
|
scope = (scope_param.split(' ') if scope_param else self.token.scope)
|
||||||
|
unauthorized_scopes = set(scope) - set(self.token.scope)
|
||||||
|
if unauthorized_scopes:
|
||||||
|
raise TokenError('invalid_scope')
|
||||||
|
|
||||||
token = create_token(
|
token = create_token(
|
||||||
user=self.token.user,
|
user=self.token.user,
|
||||||
client=self.token.client,
|
client=self.token.client,
|
||||||
scope=self.token.scope)
|
scope=scope)
|
||||||
|
|
||||||
# If the Token has an id_token it's an Authentication request.
|
# If the Token has an id_token it's an Authentication request.
|
||||||
if self.token.id_token:
|
if self.token.id_token:
|
||||||
|
@ -236,7 +245,7 @@ class TokenEndpoint(object):
|
||||||
nonce=None,
|
nonce=None,
|
||||||
at_hash=token.at_hash,
|
at_hash=token.at_hash,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
scope=self.params['scope'],
|
scope=token.scope,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
id_token_dic = {}
|
id_token_dic = {}
|
||||||
|
|
|
@ -11,19 +11,6 @@ except ImportError:
|
||||||
from urllib.parse import urlsplit, urlunsplit
|
from urllib.parse import urlsplit, urlunsplit
|
||||||
|
|
||||||
|
|
||||||
def cleanup_url_from_query_string(uri):
|
|
||||||
"""
|
|
||||||
Function used to clean up the uri from any query string, used i.e. by endpoints to validate redirect_uri
|
|
||||||
|
|
||||||
:param uri: URI to clean from query string
|
|
||||||
:type uri: str
|
|
||||||
:return: cleaned URI without query string
|
|
||||||
"""
|
|
||||||
clean_uri = urlsplit(uri)
|
|
||||||
clean_uri = urlunsplit(clean_uri._replace(query=''))
|
|
||||||
return clean_uri
|
|
||||||
|
|
||||||
|
|
||||||
def redirect(uri):
|
def redirect(uri):
|
||||||
"""
|
"""
|
||||||
Custom Response object for redirecting to a Non-HTTP url scheme.
|
Custom Response object for redirecting to a Non-HTTP url scheme.
|
||||||
|
|
|
@ -29,6 +29,8 @@ def create_fake_user():
|
||||||
user = User()
|
user = User()
|
||||||
user.username = 'johndoe'
|
user.username = 'johndoe'
|
||||||
user.email = 'johndoe@example.com'
|
user.email = 'johndoe@example.com'
|
||||||
|
user.first_name = 'John'
|
||||||
|
user.last_name = 'Doe'
|
||||||
user.set_password('1234')
|
user.set_password('1234')
|
||||||
|
|
||||||
user.save()
|
user.save()
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from oidc_provider.lib.errors import RedirectUriError
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -249,9 +251,13 @@ class AuthorizationCodeFlowTestCase(TestCase, AuthorizeEndpointMixin):
|
||||||
self.assertEqual(is_code_ok, True, msg='Code returned is invalid or missing.')
|
self.assertEqual(is_code_ok, True, msg='Code returned is invalid or missing.')
|
||||||
|
|
||||||
def test_response_uri_is_properly_constructed(self):
|
def test_response_uri_is_properly_constructed(self):
|
||||||
|
"""
|
||||||
|
Check that the redirect_uri matches the one configured for the client.
|
||||||
|
Only 'state' and 'code' should be appended.
|
||||||
|
"""
|
||||||
data = {
|
data = {
|
||||||
'client_id': self.client.client_id,
|
'client_id': self.client.client_id,
|
||||||
'redirect_uri': self.client.default_redirect_uri + "?redirect_state=xyz",
|
'redirect_uri': self.client.default_redirect_uri,
|
||||||
'response_type': 'code',
|
'response_type': 'code',
|
||||||
'scope': 'openid email',
|
'scope': 'openid email',
|
||||||
'state': self.state,
|
'state': self.state,
|
||||||
|
@ -260,11 +266,55 @@ class AuthorizationCodeFlowTestCase(TestCase, AuthorizeEndpointMixin):
|
||||||
|
|
||||||
response = self._auth_request('post', data, is_user_authenticated=True)
|
response = self._auth_request('post', data, is_user_authenticated=True)
|
||||||
|
|
||||||
# TODO
|
parsed = urlsplit(response['Location'])
|
||||||
|
params = parse_qs(parsed.query or parsed.fragment)
|
||||||
|
state = params['state'][0]
|
||||||
|
self.assertEquals(self.state, state, msg="State returned is invalid or missing")
|
||||||
|
|
||||||
|
is_code_ok = is_code_valid(url=response['Location'],
|
||||||
|
user=self.user,
|
||||||
|
client=self.client)
|
||||||
|
self.assertTrue(is_code_ok, msg='Code returned is invalid or missing')
|
||||||
|
|
||||||
|
self.assertEquals(set(params.keys()), set(['state', 'code']), msg='More than state or code appended as query params')
|
||||||
|
|
||||||
|
self.assertTrue(response['Location'].startswith(self.client.default_redirect_uri), msg='Different redirect_uri returned')
|
||||||
|
|
||||||
|
def test_unknown_redirect_uris_are_rejected(self):
|
||||||
|
"""
|
||||||
|
If a redirect_uri is not registered with the client the request must be rejected.
|
||||||
|
See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest.
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'client_id': self.client.client_id,
|
||||||
|
'response_type': 'code',
|
||||||
|
'redirect_uri': 'http://neverseenthis.com',
|
||||||
|
'scope': 'openid email',
|
||||||
|
'state': self.state,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self._auth_request('get', data)
|
||||||
|
self.assertIn(RedirectUriError.error, response.content.decode('utf-8'), msg='No redirect_uri error')
|
||||||
|
|
||||||
|
def test_manipulated_redirect_uris_are_rejected(self):
|
||||||
|
"""
|
||||||
|
If a redirect_uri does not exactly match the registered uri it must be rejected.
|
||||||
|
See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest.
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'client_id': self.client.client_id,
|
||||||
|
'response_type': 'code',
|
||||||
|
'redirect_uri': self.client.default_redirect_uri + "?some=query",
|
||||||
|
'scope': 'openid email',
|
||||||
|
'state': self.state,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self._auth_request('get', data)
|
||||||
|
self.assertIn(RedirectUriError.error, response.content.decode('utf-8'), msg='No redirect_uri error')
|
||||||
|
|
||||||
def test_public_client_auto_approval(self):
|
def test_public_client_auto_approval(self):
|
||||||
"""
|
"""
|
||||||
It's recommended not auto-approving requests for non-confidential clients.
|
It's recommended not auto-approving requests for non-confidential clients using Authorization Code.
|
||||||
"""
|
"""
|
||||||
data = {
|
data = {
|
||||||
'client_id': self.client_public_with_no_consent.client_id,
|
'client_id': self.client_public_with_no_consent.client_id,
|
||||||
|
@ -278,7 +328,7 @@ class AuthorizationCodeFlowTestCase(TestCase, AuthorizeEndpointMixin):
|
||||||
|
|
||||||
self.assertIn('Request for Permission', response.content.decode('utf-8'))
|
self.assertIn('Request for Permission', response.content.decode('utf-8'))
|
||||||
|
|
||||||
def test_prompt_parameter(self):
|
def test_prompt_none_parameter(self):
|
||||||
"""
|
"""
|
||||||
Specifies whether the Authorization Server prompts the End-User for reauthentication and consent.
|
Specifies whether the Authorization Server prompts the End-User for reauthentication and consent.
|
||||||
See: http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
See: http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
|
@ -289,10 +339,9 @@ class AuthorizationCodeFlowTestCase(TestCase, AuthorizeEndpointMixin):
|
||||||
'redirect_uri': self.client.default_redirect_uri,
|
'redirect_uri': self.client.default_redirect_uri,
|
||||||
'scope': 'openid email',
|
'scope': 'openid email',
|
||||||
'state': self.state,
|
'state': self.state,
|
||||||
|
'prompt': 'none'
|
||||||
}
|
}
|
||||||
|
|
||||||
data['prompt'] = 'none'
|
|
||||||
|
|
||||||
response = self._auth_request('get', data)
|
response = self._auth_request('get', data)
|
||||||
|
|
||||||
# An error is returned if an End-User is not already authenticated.
|
# An error is returned if an End-User is not already authenticated.
|
||||||
|
@ -301,7 +350,92 @@ class AuthorizationCodeFlowTestCase(TestCase, AuthorizeEndpointMixin):
|
||||||
response = self._auth_request('get', data, is_user_authenticated=True)
|
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.
|
# An error is returned if the Client does not have pre-configured consent for the requested Claims.
|
||||||
self.assertIn('interaction_required', response['Location'])
|
self.assertIn('consent_required', response['Location'])
|
||||||
|
|
||||||
|
@patch('oidc_provider.views.django_user_logout')
|
||||||
|
def test_prompt_login_parameter(self, logout_function):
|
||||||
|
"""
|
||||||
|
Specifies whether the Authorization Server prompts the End-User for reauthentication and consent.
|
||||||
|
See: http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'client_id': self.client.client_id,
|
||||||
|
'response_type': self.client.response_type,
|
||||||
|
'redirect_uri': self.client.default_redirect_uri,
|
||||||
|
'scope': 'openid email',
|
||||||
|
'state': self.state,
|
||||||
|
'prompt': 'login'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self._auth_request('get', data)
|
||||||
|
self.assertIn(settings.get('OIDC_LOGIN_URL'), response['Location'])
|
||||||
|
|
||||||
|
response = self._auth_request('get', data, is_user_authenticated=True)
|
||||||
|
self.assertIn(settings.get('OIDC_LOGIN_URL'), response['Location'])
|
||||||
|
self.assertTrue(logout_function.called_once())
|
||||||
|
|
||||||
|
def test_prompt_login_none_parameter(self):
|
||||||
|
"""
|
||||||
|
Specifies whether the Authorization Server prompts the End-User for reauthentication and consent.
|
||||||
|
See: http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'client_id': self.client.client_id,
|
||||||
|
'response_type': self.client.response_type,
|
||||||
|
'redirect_uri': self.client.default_redirect_uri,
|
||||||
|
'scope': 'openid email',
|
||||||
|
'state': self.state,
|
||||||
|
'prompt': 'login none'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self._auth_request('get', data)
|
||||||
|
self.assertIn('login_required', response['Location'])
|
||||||
|
|
||||||
|
response = self._auth_request('get', data, is_user_authenticated=True)
|
||||||
|
self.assertIn('login_required', response['Location'])
|
||||||
|
|
||||||
|
@patch('oidc_provider.views.render')
|
||||||
|
def test_prompt_consent_parameter(self, render_patched):
|
||||||
|
"""
|
||||||
|
Specifies whether the Authorization Server prompts the End-User for reauthentication and consent.
|
||||||
|
See: http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'client_id': self.client.client_id,
|
||||||
|
'response_type': self.client.response_type,
|
||||||
|
'redirect_uri': self.client.default_redirect_uri,
|
||||||
|
'scope': 'openid email',
|
||||||
|
'state': self.state,
|
||||||
|
'prompt': 'consent'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self._auth_request('get', data)
|
||||||
|
self.assertIn(settings.get('OIDC_LOGIN_URL'), response['Location'])
|
||||||
|
|
||||||
|
response = self._auth_request('get', data, is_user_authenticated=True)
|
||||||
|
render_patched.assert_called_once()
|
||||||
|
self.assertTrue(render_patched.call_args[0][1], settings.get('OIDC_TEMPLATES')['authorize'])
|
||||||
|
|
||||||
|
def test_prompt_consent_none_parameter(self):
|
||||||
|
"""
|
||||||
|
Specifies whether the Authorization Server prompts the End-User for reauthentication and consent.
|
||||||
|
See: http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'client_id': self.client.client_id,
|
||||||
|
'response_type': self.client.response_type,
|
||||||
|
'redirect_uri': self.client.default_redirect_uri,
|
||||||
|
'scope': 'openid email',
|
||||||
|
'state': self.state,
|
||||||
|
'prompt': 'consent none'
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self._auth_request('get', data)
|
||||||
|
self.assertIn('login_required', response['Location'])
|
||||||
|
|
||||||
|
response = self._auth_request('get', data, is_user_authenticated=True)
|
||||||
|
self.assertIn('consent_required', response['Location'])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationImplicitFlowTestCase(TestCase, AuthorizeEndpointMixin):
|
class AuthorizationImplicitFlowTestCase(TestCase, AuthorizeEndpointMixin):
|
||||||
|
@ -315,6 +449,9 @@ class AuthorizationImplicitFlowTestCase(TestCase, AuthorizeEndpointMixin):
|
||||||
self.user = create_fake_user()
|
self.user = create_fake_user()
|
||||||
self.client = create_fake_client(response_type='id_token token')
|
self.client = create_fake_client(response_type='id_token token')
|
||||||
self.client_public = create_fake_client(response_type='id_token token', is_public=True)
|
self.client_public = create_fake_client(response_type='id_token token', is_public=True)
|
||||||
|
self.client_public_no_consent = create_fake_client(
|
||||||
|
response_type='id_token token', is_public=True,
|
||||||
|
require_consent=False)
|
||||||
self.client_no_access = create_fake_client(response_type='id_token')
|
self.client_no_access = create_fake_client(response_type='id_token')
|
||||||
self.client_public_no_access = create_fake_client(response_type='id_token', is_public=True)
|
self.client_public_no_access = create_fake_client(response_type='id_token', is_public=True)
|
||||||
self.state = uuid.uuid4().hex
|
self.state = uuid.uuid4().hex
|
||||||
|
@ -448,6 +585,28 @@ class AuthorizationImplicitFlowTestCase(TestCase, AuthorizeEndpointMixin):
|
||||||
|
|
||||||
self.assertNotIn('at_hash', id_token)
|
self.assertNotIn('at_hash', id_token)
|
||||||
|
|
||||||
|
def test_public_client_implicit_auto_approval(self):
|
||||||
|
"""
|
||||||
|
Public clients using Implicit Flow should be able to reuse consent.
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'client_id': self.client_public_no_consent.client_id,
|
||||||
|
'response_type': self.client_public_no_consent.response_type,
|
||||||
|
'redirect_uri': self.client_public_no_consent.default_redirect_uri,
|
||||||
|
'scope': 'openid email',
|
||||||
|
'state': self.state,
|
||||||
|
'nonce': self.nonce,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self._auth_request('get', data, is_user_authenticated=True)
|
||||||
|
response_text = response.content.decode('utf-8')
|
||||||
|
self.assertEquals(response_text, '')
|
||||||
|
components = urlsplit(response['Location'])
|
||||||
|
fragment = parse_qs(components[4])
|
||||||
|
self.assertIn('access_token', fragment)
|
||||||
|
self.assertIn('id_token', fragment)
|
||||||
|
self.assertIn('expires_in', fragment)
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationHybridFlowTestCase(TestCase, AuthorizeEndpointMixin):
|
class AuthorizationHybridFlowTestCase(TestCase, AuthorizeEndpointMixin):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -50,15 +50,18 @@ class TokenTestCase(TestCase):
|
||||||
self.user = create_fake_user()
|
self.user = create_fake_user()
|
||||||
self.client = create_fake_client(response_type='code')
|
self.client = create_fake_client(response_type='code')
|
||||||
|
|
||||||
def _password_grant_post_data(self):
|
def _password_grant_post_data(self, scope=None):
|
||||||
return {
|
result = {
|
||||||
'username': 'johndoe',
|
'username': 'johndoe',
|
||||||
'password': '1234',
|
'password': '1234',
|
||||||
'grant_type': 'password',
|
'grant_type': 'password',
|
||||||
'scope': 'openid email',
|
'scope': 'openid email',
|
||||||
}
|
}
|
||||||
|
if scope is not None:
|
||||||
|
result['scope'] = ' '.join(scope)
|
||||||
|
return result
|
||||||
|
|
||||||
def _auth_code_post_data(self, code):
|
def _auth_code_post_data(self, code, scope=None):
|
||||||
"""
|
"""
|
||||||
All the data that will be POSTed to the Token Endpoint.
|
All the data that will be POSTed to the Token Endpoint.
|
||||||
"""
|
"""
|
||||||
|
@ -70,10 +73,12 @@ class TokenTestCase(TestCase):
|
||||||
'code': code,
|
'code': code,
|
||||||
'state': uuid.uuid4().hex,
|
'state': uuid.uuid4().hex,
|
||||||
}
|
}
|
||||||
|
if scope is not None:
|
||||||
|
post_data['scope'] = ' '.join(scope)
|
||||||
|
|
||||||
return post_data
|
return post_data
|
||||||
|
|
||||||
def _refresh_token_post_data(self, refresh_token):
|
def _refresh_token_post_data(self, refresh_token, scope=None):
|
||||||
"""
|
"""
|
||||||
All the data that will be POSTed to the Token Endpoint.
|
All the data that will be POSTed to the Token Endpoint.
|
||||||
"""
|
"""
|
||||||
|
@ -83,6 +88,8 @@ class TokenTestCase(TestCase):
|
||||||
'grant_type': 'refresh_token',
|
'grant_type': 'refresh_token',
|
||||||
'refresh_token': refresh_token,
|
'refresh_token': refresh_token,
|
||||||
}
|
}
|
||||||
|
if scope is not None:
|
||||||
|
post_data['scope'] = ' '.join(scope)
|
||||||
|
|
||||||
return post_data
|
return post_data
|
||||||
|
|
||||||
|
@ -103,14 +110,14 @@ class TokenTestCase(TestCase):
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def _create_code(self):
|
def _create_code(self, scope=None):
|
||||||
"""
|
"""
|
||||||
Generate a valid grant code.
|
Generate a valid grant code.
|
||||||
"""
|
"""
|
||||||
code = create_code(
|
code = create_code(
|
||||||
user=self.user,
|
user=self.user,
|
||||||
client=self.client,
|
client=self.client,
|
||||||
scope=['openid', 'email'],
|
scope=(scope if scope else ['openid', 'email']),
|
||||||
nonce=FAKE_NONCE,
|
nonce=FAKE_NONCE,
|
||||||
is_authentication=True)
|
is_authentication=True)
|
||||||
code.save()
|
code.save()
|
||||||
|
@ -228,30 +235,41 @@ class TokenTestCase(TestCase):
|
||||||
self.assertEqual(400, response.status_code)
|
self.assertEqual(400, response.status_code)
|
||||||
self.assertEqual('invalid_client', response_dict['error'])
|
self.assertEqual('invalid_client', response_dict['error'])
|
||||||
|
|
||||||
@patch('oidc_provider.lib.utils.token.uuid')
|
def test_password_grant_full_response(self):
|
||||||
|
self.check_password_grant(scope=['openid', 'email'])
|
||||||
|
|
||||||
|
def test_password_grant_scope(self):
|
||||||
|
self.check_password_grant(scope=['openid', 'profile'])
|
||||||
|
|
||||||
@override_settings(OIDC_TOKEN_EXPIRE=120,
|
@override_settings(OIDC_TOKEN_EXPIRE=120,
|
||||||
OIDC_GRANT_TYPE_PASSWORD_ENABLE=True)
|
OIDC_GRANT_TYPE_PASSWORD_ENABLE=True)
|
||||||
def test_password_grant_full_response(self, mock_uuid):
|
def check_password_grant(self, scope):
|
||||||
test_hex = 'fake_token'
|
|
||||||
mock_uuid4 = Mock(spec=uuid.uuid4)
|
|
||||||
mock_uuid4.hex = test_hex
|
|
||||||
mock_uuid.uuid4.return_value = mock_uuid4
|
|
||||||
|
|
||||||
response = self._post_request(
|
response = self._post_request(
|
||||||
post_data=self._password_grant_post_data(),
|
post_data=self._password_grant_post_data(scope),
|
||||||
extras=self._password_grant_auth_header()
|
extras=self._password_grant_auth_header()
|
||||||
)
|
)
|
||||||
|
|
||||||
response_dict = json.loads(response.content.decode('utf-8'))
|
response_dict = json.loads(response.content.decode('utf-8'))
|
||||||
id_token = JWS().verify_compact(response_dict['id_token'].encode('utf-8'), self._get_keys())
|
id_token = JWS().verify_compact(response_dict['id_token'].encode('utf-8'), self._get_keys())
|
||||||
|
|
||||||
self.assertEqual(response_dict['access_token'], 'fake_token')
|
token = Token.objects.get(user=self.user)
|
||||||
self.assertEqual(response_dict['refresh_token'], 'fake_token')
|
self.assertEqual(response_dict['access_token'], token.access_token)
|
||||||
|
self.assertEqual(response_dict['refresh_token'], token.refresh_token)
|
||||||
self.assertEqual(response_dict['expires_in'], 120)
|
self.assertEqual(response_dict['expires_in'], 120)
|
||||||
self.assertEqual(response_dict['token_type'], 'bearer')
|
self.assertEqual(response_dict['token_type'], 'bearer')
|
||||||
self.assertEqual(id_token['sub'], str(self.user.id))
|
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)
|
||||||
|
|
||||||
|
# Check the scope is honored by checking the claims in the userinfo
|
||||||
|
userinfo_response = self._get_userinfo(response_dict['access_token'])
|
||||||
|
userinfo = json.loads(userinfo_response.content.decode('utf-8'))
|
||||||
|
|
||||||
|
for (scope_param, claim) in [('email', 'email'), ('profile', 'name')]:
|
||||||
|
if scope_param in scope:
|
||||||
|
self.assertIn(claim, userinfo)
|
||||||
|
else:
|
||||||
|
self.assertNotIn(claim, userinfo)
|
||||||
|
|
||||||
@override_settings(OIDC_TOKEN_EXPIRE=720)
|
@override_settings(OIDC_TOKEN_EXPIRE=720)
|
||||||
def test_authorization_code(self):
|
def test_authorization_code(self):
|
||||||
"""
|
"""
|
||||||
|
@ -277,16 +295,64 @@ class TokenTestCase(TestCase):
|
||||||
self.assertEqual(id_token['sub'], str(self.user.id))
|
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)
|
||||||
|
|
||||||
|
@override_settings(OIDC_TOKEN_EXPIRE=720)
|
||||||
|
def test_scope_is_ignored_for_auth_code(self):
|
||||||
|
"""
|
||||||
|
Scope is ignored for token respones to auth code grant type.
|
||||||
|
"""
|
||||||
|
SIGKEYS = self._get_keys()
|
||||||
|
for code_scope in [['openid'], ['openid', 'email']]:
|
||||||
|
code = self._create_code(code_scope)
|
||||||
|
|
||||||
|
post_data = self._auth_code_post_data(
|
||||||
|
code=code.code, scope=['openid', 'profile'])
|
||||||
|
|
||||||
|
response = self._post_request(post_data)
|
||||||
|
response_dic = json.loads(response.content.decode('utf-8'))
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
id_token = JWS().verify_compact(response_dic['id_token'].encode('utf-8'), SIGKEYS)
|
||||||
|
|
||||||
|
if 'email' in code_scope:
|
||||||
|
self.assertIn('email', id_token)
|
||||||
|
else:
|
||||||
|
self.assertNotIn('email', id_token)
|
||||||
|
|
||||||
def test_refresh_token(self):
|
def test_refresh_token(self):
|
||||||
"""
|
"""
|
||||||
A request to the Token Endpoint can also use a Refresh Token
|
A request to the Token Endpoint can also use a Refresh Token
|
||||||
by using the grant_type value refresh_token, as described in
|
by using the grant_type value refresh_token, as described in
|
||||||
Section 6 of OAuth 2.0 [RFC6749].
|
Section 6 of OAuth 2.0 [RFC6749].
|
||||||
"""
|
"""
|
||||||
|
self.do_refresh_token_check()
|
||||||
|
|
||||||
|
def test_refresh_token_invalid_scope(self):
|
||||||
|
"""
|
||||||
|
Extending scope in refresh token is not allowed.
|
||||||
|
|
||||||
|
Try to get a refresh token with "profile" in the scope even
|
||||||
|
though the original authorized scope in the authorization code
|
||||||
|
request is only ['openid', 'email'].
|
||||||
|
"""
|
||||||
|
self.do_refresh_token_check(scope=['openid', 'profile'])
|
||||||
|
|
||||||
|
def test_refresh_token_narrowed_scope(self):
|
||||||
|
"""
|
||||||
|
Narrowing scope in refresh token is allowed.
|
||||||
|
|
||||||
|
Try to get a refresh token with just "openid" in the scope even
|
||||||
|
though the original authorized scope in the authorization code
|
||||||
|
request is ['openid', 'email'].
|
||||||
|
"""
|
||||||
|
self.do_refresh_token_check(scope=['openid'])
|
||||||
|
|
||||||
|
def do_refresh_token_check(self, scope=None):
|
||||||
SIGKEYS = self._get_keys()
|
SIGKEYS = self._get_keys()
|
||||||
|
|
||||||
# Retrieve refresh token
|
# Retrieve refresh token
|
||||||
code = self._create_code()
|
code = self._create_code()
|
||||||
|
self.assertEqual(code.scope, ['openid', 'email'])
|
||||||
post_data = self._auth_code_post_data(code=code.code)
|
post_data = self._auth_code_post_data(code=code.code)
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
with patch('oidc_provider.lib.utils.token.time.time') as time_func:
|
with patch('oidc_provider.lib.utils.token.time.time') as time_func:
|
||||||
|
@ -297,14 +363,29 @@ class TokenTestCase(TestCase):
|
||||||
id_token1 = JWS().verify_compact(response_dic1['id_token'].encode('utf-8'), SIGKEYS)
|
id_token1 = JWS().verify_compact(response_dic1['id_token'].encode('utf-8'), SIGKEYS)
|
||||||
|
|
||||||
# Use refresh token to obtain new token
|
# Use refresh token to obtain new token
|
||||||
post_data = self._refresh_token_post_data(response_dic1['refresh_token'])
|
post_data = self._refresh_token_post_data(
|
||||||
|
response_dic1['refresh_token'], scope)
|
||||||
with patch('oidc_provider.lib.utils.token.time.time') as time_func:
|
with patch('oidc_provider.lib.utils.token.time.time') as time_func:
|
||||||
time_func.return_value = start_time + 600
|
time_func.return_value = start_time + 600
|
||||||
response = self._post_request(post_data)
|
response = self._post_request(post_data)
|
||||||
|
|
||||||
response_dic2 = json.loads(response.content.decode('utf-8'))
|
response_dic2 = json.loads(response.content.decode('utf-8'))
|
||||||
|
|
||||||
|
if scope and set(scope) - set(code.scope): # too broad scope
|
||||||
|
self.assertEqual(response.status_code, 400) # Bad Request
|
||||||
|
self.assertIn('error', response_dic2)
|
||||||
|
self.assertEqual(response_dic2['error'], 'invalid_scope')
|
||||||
|
return # No more checks
|
||||||
|
|
||||||
id_token2 = JWS().verify_compact(response_dic2['id_token'].encode('utf-8'), SIGKEYS)
|
id_token2 = JWS().verify_compact(response_dic2['id_token'].encode('utf-8'), SIGKEYS)
|
||||||
|
|
||||||
|
if scope and 'email' not in scope: # narrowed scope The auth
|
||||||
|
# The auth code request had email in scope, so it should be
|
||||||
|
# in the first id token
|
||||||
|
self.assertIn('email', id_token1)
|
||||||
|
# but the refresh request had no email in scope
|
||||||
|
self.assertNotIn('email', id_token2, 'email was not requested')
|
||||||
|
|
||||||
self.assertNotEqual(response_dic1['id_token'], response_dic2['id_token'])
|
self.assertNotEqual(response_dic1['id_token'], response_dic2['id_token'])
|
||||||
self.assertNotEqual(response_dic1['access_token'], response_dic2['access_token'])
|
self.assertNotEqual(response_dic1['access_token'], response_dic2['access_token'])
|
||||||
self.assertNotEqual(response_dic1['refresh_token'], response_dic2['refresh_token'])
|
self.assertNotEqual(response_dic1['refresh_token'], response_dic2['refresh_token'])
|
||||||
|
@ -339,12 +420,12 @@ class TokenTestCase(TestCase):
|
||||||
response = self._post_request(post_data)
|
response = self._post_request(post_data)
|
||||||
self.assertIn('invalid_grant', response.content.decode('utf-8'))
|
self.assertIn('invalid_grant', response.content.decode('utf-8'))
|
||||||
|
|
||||||
def test_client_redirect_url(self):
|
def test_client_redirect_uri(self):
|
||||||
"""
|
"""
|
||||||
Validate that client redirect URIs with query strings match registered
|
Validate that client redirect URIs exactly match registered
|
||||||
URIs, and that unregistered URIs are rejected.
|
URIs, and that unregistered URIs or URIs with query parameters are rejected.
|
||||||
|
See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest and
|
||||||
source: https://github.com/jerrykan/django-oidc-provider/blob/2f54e537666c689dd8448f8bbc6a3a0244b01a97/oidc_provider/tests/test_token_endpoint.py
|
http://openid.net/specs/openid-connect-core-1_0.html#HybridTokenRequest.
|
||||||
"""
|
"""
|
||||||
SIGKEYS = self._get_keys()
|
SIGKEYS = self._get_keys()
|
||||||
code = self._create_code()
|
code = self._create_code()
|
||||||
|
@ -354,15 +435,19 @@ class TokenTestCase(TestCase):
|
||||||
post_data['redirect_uri'] = 'http://invalid.example.org'
|
post_data['redirect_uri'] = 'http://invalid.example.org'
|
||||||
|
|
||||||
response = self._post_request(post_data)
|
response = self._post_request(post_data)
|
||||||
|
self.assertIn('invalid_client', response.content.decode('utf-8'))
|
||||||
|
|
||||||
self.assertIn('invalid_client', response.content.decode('utf-8')),
|
# Registered URI, but with query string appended
|
||||||
|
post_data['redirect_uri'] = self.client.default_redirect_uri + '?foo=bar'
|
||||||
# Registered URI contained a query string
|
|
||||||
post_data['redirect_uri'] = 'http://example.com/?client=OidcClient'
|
|
||||||
|
|
||||||
response = self._post_request(post_data)
|
response = self._post_request(post_data)
|
||||||
|
self.assertIn('invalid_client', response.content.decode('utf-8'))
|
||||||
|
|
||||||
self.assertNotIn('invalid_client', response.content.decode('utf-8')),
|
# Registered URI
|
||||||
|
post_data['redirect_uri'] = self.client.default_redirect_uri
|
||||||
|
|
||||||
|
response = self._post_request(post_data)
|
||||||
|
self.assertNotIn('invalid_client', response.content.decode('utf-8'))
|
||||||
|
|
||||||
def test_request_methods(self):
|
def test_request_methods(self):
|
||||||
"""
|
"""
|
||||||
|
@ -440,29 +525,6 @@ class TokenTestCase(TestCase):
|
||||||
False,
|
False,
|
||||||
msg='Client authentication fails using HTTP Basic Auth.')
|
msg='Client authentication fails using HTTP Basic Auth.')
|
||||||
|
|
||||||
def test_client_redirect_url(self):
|
|
||||||
"""
|
|
||||||
Validate that client redirect URIs with query strings match registered
|
|
||||||
URIs, and that unregistered URIs are rejected.
|
|
||||||
"""
|
|
||||||
SIGKEYS = self._get_keys()
|
|
||||||
code = self._create_code()
|
|
||||||
post_data = self._auth_code_post_data(code=code.code)
|
|
||||||
|
|
||||||
# Unregistered URI
|
|
||||||
post_data['redirect_uri'] = 'http://invalid.example.org'
|
|
||||||
|
|
||||||
response = self._post_request(post_data)
|
|
||||||
|
|
||||||
self.assertIn('invalid_client', response.content.decode('utf-8')),
|
|
||||||
|
|
||||||
# Registered URI contained a query string
|
|
||||||
post_data['redirect_uri'] = 'http://example.com/?client=OidcClient'
|
|
||||||
|
|
||||||
response = self._post_request(post_data)
|
|
||||||
|
|
||||||
self.assertNotIn('invalid_client', response.content.decode('utf-8')),
|
|
||||||
|
|
||||||
def test_access_token_contains_nonce(self):
|
def test_access_token_contains_nonce(self):
|
||||||
"""
|
"""
|
||||||
If present in the Authentication Request, Authorization Servers MUST
|
If present in the Authentication Request, Authorization Servers MUST
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from urlparse import urlsplit, parse_qs, urlunsplit
|
from urlparse import urlsplit, parse_qs, urlunsplit
|
||||||
|
@ -10,6 +11,7 @@ from django.contrib.auth.views import (
|
||||||
redirect_to_login,
|
redirect_to_login,
|
||||||
logout,
|
logout,
|
||||||
)
|
)
|
||||||
|
from django.contrib.auth import logout as django_user_logout
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
@ -44,14 +46,12 @@ from oidc_provider.models import (
|
||||||
from oidc_provider import settings
|
from oidc_provider import settings
|
||||||
from oidc_provider import signals
|
from oidc_provider import signals
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
OIDC_TEMPLATES = settings.get('OIDC_TEMPLATES')
|
OIDC_TEMPLATES = settings.get('OIDC_TEMPLATES')
|
||||||
|
|
||||||
|
|
||||||
class AuthorizeView(View):
|
class AuthorizeView(View):
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
|
||||||
authorize = AuthorizeEndpoint(request)
|
authorize = AuthorizeEndpoint(request)
|
||||||
|
@ -67,25 +67,43 @@ class AuthorizeView(View):
|
||||||
if hook_resp:
|
if hook_resp:
|
||||||
return hook_resp
|
return hook_resp
|
||||||
|
|
||||||
if not authorize.client.require_consent and not (authorize.client.client_type == 'public') \
|
if 'login' in authorize.params['prompt']:
|
||||||
and not (authorize.params['prompt'] == 'consent'):
|
if 'none' in authorize.params['prompt']:
|
||||||
|
raise AuthorizeError(authorize.params['redirect_uri'], 'login_required', authorize.grant_type)
|
||||||
|
else:
|
||||||
|
django_user_logout(request)
|
||||||
|
return redirect_to_login(request.get_full_path(), settings.get('OIDC_LOGIN_URL'))
|
||||||
|
|
||||||
|
if 'select_account' in authorize.params['prompt']:
|
||||||
|
# TODO: see how we can support multiple accounts for the end-user.
|
||||||
|
if 'none' in authorize.params['prompt']:
|
||||||
|
raise AuthorizeError(authorize.params['redirect_uri'], 'account_selection_required', authorize.grant_type)
|
||||||
|
else:
|
||||||
|
django_user_logout(request)
|
||||||
|
return redirect_to_login(request.get_full_path(), settings.get('OIDC_LOGIN_URL'))
|
||||||
|
|
||||||
|
if {'none', 'consent'}.issubset(authorize.params['prompt']):
|
||||||
|
raise AuthorizeError(authorize.params['redirect_uri'], 'consent_required', authorize.grant_type)
|
||||||
|
|
||||||
|
implicit_flow_resp_types = set(['id_token', 'id_token token'])
|
||||||
|
allow_skipping_consent = (
|
||||||
|
authorize.client.client_type != 'public' or
|
||||||
|
authorize.client.response_type in implicit_flow_resp_types)
|
||||||
|
|
||||||
|
if not authorize.client.require_consent and (
|
||||||
|
allow_skipping_consent and
|
||||||
|
'consent' not in authorize.params['prompt']):
|
||||||
return redirect(authorize.create_response_uri())
|
return redirect(authorize.create_response_uri())
|
||||||
|
|
||||||
if authorize.client.reuse_consent:
|
if authorize.client.reuse_consent:
|
||||||
# Check if user previously give consent.
|
# Check if user previously give consent.
|
||||||
if authorize.client_has_user_consent() and not (authorize.client.client_type == 'public') \
|
if authorize.client_has_user_consent() and (
|
||||||
and not (authorize.params['prompt'] == 'consent'):
|
allow_skipping_consent and
|
||||||
|
'consent' not in authorize.params['prompt']):
|
||||||
return redirect(authorize.create_response_uri())
|
return redirect(authorize.create_response_uri())
|
||||||
|
|
||||||
if authorize.params['prompt'] == 'none':
|
if 'none' in authorize.params['prompt']:
|
||||||
raise AuthorizeError(authorize.params['redirect_uri'], 'interaction_required', authorize.grant_type)
|
raise AuthorizeError(authorize.params['redirect_uri'], 'consent_required', authorize.grant_type)
|
||||||
|
|
||||||
if authorize.params['prompt'] == 'login':
|
|
||||||
return redirect_to_login(request.get_full_path(), settings.get('OIDC_LOGIN_URL'))
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
# Generate hidden inputs for the form.
|
# Generate hidden inputs for the form.
|
||||||
context = {
|
context = {
|
||||||
|
@ -107,7 +125,7 @@ class AuthorizeView(View):
|
||||||
|
|
||||||
return render(request, OIDC_TEMPLATES['authorize'], context)
|
return render(request, OIDC_TEMPLATES['authorize'], context)
|
||||||
else:
|
else:
|
||||||
if authorize.params['prompt'] == 'none':
|
if 'none' in authorize.params['prompt']:
|
||||||
raise AuthorizeError(authorize.params['redirect_uri'], 'login_required', authorize.grant_type)
|
raise AuthorizeError(authorize.params['redirect_uri'], 'login_required', authorize.grant_type)
|
||||||
|
|
||||||
return redirect_to_login(request.get_full_path(), settings.get('OIDC_LOGIN_URL'))
|
return redirect_to_login(request.get_full_path(), settings.get('OIDC_LOGIN_URL'))
|
||||||
|
@ -120,7 +138,7 @@ class AuthorizeView(View):
|
||||||
|
|
||||||
return render(request, OIDC_TEMPLATES['error'], context)
|
return render(request, OIDC_TEMPLATES['error'], context)
|
||||||
|
|
||||||
except (AuthorizeError) as error:
|
except AuthorizeError as error:
|
||||||
uri = error.create_uri(
|
uri = error.create_uri(
|
||||||
authorize.params['redirect_uri'],
|
authorize.params['redirect_uri'],
|
||||||
authorize.params['state'])
|
authorize.params['state'])
|
||||||
|
@ -158,7 +176,6 @@ class AuthorizeView(View):
|
||||||
|
|
||||||
|
|
||||||
class TokenView(View):
|
class TokenView(View):
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
token = TokenEndpoint(request)
|
token = TokenEndpoint(request)
|
||||||
|
|
||||||
|
@ -206,7 +223,6 @@ def userinfo(request, *args, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
class ProviderInfoView(View):
|
class ProviderInfoView(View):
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
dic = dict()
|
dic = dict()
|
||||||
|
|
||||||
|
@ -241,7 +257,6 @@ class ProviderInfoView(View):
|
||||||
|
|
||||||
|
|
||||||
class JwksView(View):
|
class JwksView(View):
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
dic = dict(keys=[])
|
dic = dict(keys=[])
|
||||||
|
|
||||||
|
@ -263,7 +278,6 @@ class JwksView(View):
|
||||||
|
|
||||||
|
|
||||||
class EndSessionView(View):
|
class EndSessionView(View):
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
id_token_hint = request.GET.get('id_token_hint', '')
|
id_token_hint = request.GET.get('id_token_hint', '')
|
||||||
post_logout_redirect_uri = request.GET.get('post_logout_redirect_uri', '')
|
post_logout_redirect_uri = request.GET.get('post_logout_redirect_uri', '')
|
||||||
|
@ -302,7 +316,6 @@ class EndSessionView(View):
|
||||||
|
|
||||||
|
|
||||||
class CheckSessionIframeView(View):
|
class CheckSessionIframeView(View):
|
||||||
|
|
||||||
@method_decorator(xframe_options_exempt)
|
@method_decorator(xframe_options_exempt)
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
return super(CheckSessionIframeView, self).dispatch(request, *args, **kwargs)
|
return super(CheckSessionIframeView, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
Loading…
Reference in a new issue