summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--oauthlib/openid/connect/core/grant_types/base.py23
-rw-r--r--oauthlib/openid/connect/core/grant_types/hybrid.py25
-rw-r--r--oauthlib/openid/connect/core/grant_types/implicit.py23
-rw-r--r--tests/openid/connect/core/grant_types/test_authorization_code.py14
-rw-r--r--tests/openid/connect/core/grant_types/test_hybrid.py80
-rw-r--r--tests/openid/connect/core/grant_types/test_implicit.py63
6 files changed, 167 insertions, 61 deletions
diff --git a/oauthlib/openid/connect/core/grant_types/base.py b/oauthlib/openid/connect/core/grant_types/base.py
index 05cdd37..4f5c944 100644
--- a/oauthlib/openid/connect/core/grant_types/base.py
+++ b/oauthlib/openid/connect/core/grant_types/base.py
@@ -247,28 +247,5 @@ class GrantTypeBase(object):
return request_info
- def openid_implicit_authorization_validator(self, request):
- """Additional validation when following the implicit flow.
- """
- # Undefined in OpenID Connect, fall back to OAuth2 definition.
- if request.response_type == 'token':
- return {}
-
- # Treat it as normal OAuth 2 auth code request if openid is not present
- if not request.scopes or 'openid' not in request.scopes:
- return {}
-
- # REQUIRED. String value used to associate a Client session with an ID
- # Token, and to mitigate replay attacks. The value is passed through
- # unmodified from the Authentication Request to the ID Token.
- # Sufficient entropy MUST be present in the nonce values used to
- # prevent attackers from guessing values. For implementation notes, see
- # Section 15.5.2.
- if not request.nonce:
- desc = 'Request is missing mandatory nonce parameter.'
- raise InvalidRequestError(request=request, description=desc)
-
- return {}
-
OpenIDConnectBase = GrantTypeBase
diff --git a/oauthlib/openid/connect/core/grant_types/hybrid.py b/oauthlib/openid/connect/core/grant_types/hybrid.py
index 54669ae..685fa08 100644
--- a/oauthlib/openid/connect/core/grant_types/hybrid.py
+++ b/oauthlib/openid/connect/core/grant_types/hybrid.py
@@ -8,6 +8,7 @@ from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.oauth2.rfc6749.grant_types.authorization_code import AuthorizationCodeGrant as OAuth2AuthorizationCodeGrant
+from oauthlib.oauth2.rfc6749.errors import InvalidRequestError
from .base import GrantTypeBase
from ..request_validator import RequestValidator
@@ -34,3 +35,27 @@ class HybridGrant(GrantTypeBase):
self.register_code_modifier(self.add_token)
self.register_code_modifier(self.add_id_token)
self.register_token_modifier(self.add_id_token)
+
+ def openid_authorization_validator(self, request):
+ """Additional validation when following the Authorization Code flow.
+ """
+ request_info = super(HybridGrant, self).openid_authorization_validator(request)
+ if not request_info: # returns immediately if OAuth2.0
+ return request_info
+
+ # REQUIRED if the Response Type of the request is `code
+ # id_token` or `code id_token token` and OPTIONAL when the
+ # Response Type of the request is `code token`. It is a string
+ # value used to associate a Client session with an ID Token,
+ # and to mitigate replay attacks. The value is passed through
+ # unmodified from the Authentication Request to the ID
+ # Token. Sufficient entropy MUST be present in the `nonce`
+ # values used to prevent attackers from guessing values. For
+ # implementation notes, see Section 15.5.2.
+ if request.response_type in ["code id_token", "code id_token token"]:
+ if not request.nonce:
+ raise InvalidRequestError(
+ request=request,
+ description='Request is missing mandatory nonce parameter.'
+ )
+ return request_info
diff --git a/oauthlib/openid/connect/core/grant_types/implicit.py b/oauthlib/openid/connect/core/grant_types/implicit.py
index 0a6fcb7..d3797b2 100644
--- a/oauthlib/openid/connect/core/grant_types/implicit.py
+++ b/oauthlib/openid/connect/core/grant_types/implicit.py
@@ -10,6 +10,7 @@ import logging
from .base import GrantTypeBase
from oauthlib.oauth2.rfc6749.grant_types.implicit import ImplicitGrant as OAuth2ImplicitGrant
+from oauthlib.oauth2.rfc6749.errors import InvalidRequestError
log = logging.getLogger(__name__)
@@ -23,11 +24,29 @@ class ImplicitGrant(GrantTypeBase):
self.register_response_type('id_token token')
self.custom_validators.post_auth.append(
self.openid_authorization_validator)
- self.custom_validators.post_auth.append(
- self.openid_implicit_authorization_validator)
self.register_token_modifier(self.add_id_token)
def add_id_token(self, token, token_handler, request):
if 'state' not in token:
token['state'] = request.state
return super(ImplicitGrant, self).add_id_token(token, token_handler, request)
+
+ def openid_authorization_validator(self, request):
+ """Additional validation when following the implicit flow.
+ """
+ request_info = super(ImplicitGrant, self).openid_authorization_validator(request)
+ if not request_info: # returns immediately if OAuth2.0
+ return request_info
+
+ # REQUIRED. String value used to associate a Client session with an ID
+ # Token, and to mitigate replay attacks. The value is passed through
+ # unmodified from the Authentication Request to the ID Token.
+ # Sufficient entropy MUST be present in the nonce values used to
+ # prevent attackers from guessing values. For implementation notes, see
+ # Section 15.5.2.
+ if not request.nonce:
+ raise InvalidRequestError(
+ request=request,
+ description='Request is missing mandatory nonce parameter.'
+ )
+ return request_info
diff --git a/tests/openid/connect/core/grant_types/test_authorization_code.py b/tests/openid/connect/core/grant_types/test_authorization_code.py
index fbbd5ff..b721a19 100644
--- a/tests/openid/connect/core/grant_types/test_authorization_code.py
+++ b/tests/openid/connect/core/grant_types/test_authorization_code.py
@@ -40,6 +40,7 @@ class OpenIDAuthCodeTest(TestCase):
self.request.grant_type = 'authorization_code'
self.request.redirect_uri = 'https://a.b/cb'
self.request.state = 'abc'
+ self.request.nonce = None
self.mock_validator = mock.MagicMock()
self.mock_validator.authenticate_client.side_effect = self.set_client
@@ -147,3 +148,16 @@ class OpenIDAuthCodeTest(TestCase):
self.assertIn('scope', token)
self.assertNotIn('id_token', token)
self.assertNotIn('openid', token['scope'])
+
+ @mock.patch('oauthlib.common.generate_token')
+ def test_optional_nonce(self, generate_token):
+ generate_token.return_value = 'abc'
+ self.request.nonce = 'xyz'
+ scope, info = self.auth.validate_authorization_request(self.request)
+
+ bearer = BearerToken(self.mock_validator)
+ self.request.response_mode = 'query'
+ h, b, s = self.auth.create_authorization_response(self.request, bearer)
+ self.assertURLEqual(h['Location'], self.url_query)
+ self.assertEqual(b, None)
+ self.assertEqual(s, 302)
diff --git a/tests/openid/connect/core/grant_types/test_hybrid.py b/tests/openid/connect/core/grant_types/test_hybrid.py
index 6eb8037..0aa0add 100644
--- a/tests/openid/connect/core/grant_types/test_hybrid.py
+++ b/tests/openid/connect/core/grant_types/test_hybrid.py
@@ -1,9 +1,14 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
-from oauthlib.openid.connect.core.grant_types.hybrid import HybridGrant
+import mock
+
+from oauthlib.oauth2.rfc6749 import errors
+from oauthlib.oauth2.rfc6749.tokens import BearerToken
+from oauthlib.openid.connect.core.grant_types.hybrid import HybridGrant
from tests.oauth2.rfc6749.grant_types.test_authorization_code import \
AuthorizationCodeGrantTest
+from .test_authorization_code import OpenIDAuthCodeTest
class OpenIDHybridInterferenceTest(AuthorizationCodeGrantTest):
@@ -12,3 +17,76 @@ class OpenIDHybridInterferenceTest(AuthorizationCodeGrantTest):
def setUp(self):
super(OpenIDHybridInterferenceTest, self).setUp()
self.auth = HybridGrant(request_validator=self.mock_validator)
+
+
+class OpenIDHybridCodeTokenTest(OpenIDAuthCodeTest):
+
+ def setUp(self):
+ super(OpenIDHybridCodeTokenTest, self).setUp()
+ self.request.response_type = 'code token'
+ self.request.nonce = None
+ self.auth = HybridGrant(request_validator=self.mock_validator)
+ self.url_query = 'https://a.b/cb?code=abc&state=abc&token_type=Bearer&expires_in=3600&scope=hello+openid&access_token=abc'
+ self.url_fragment = 'https://a.b/cb#code=abc&state=abc&token_type=Bearer&expires_in=3600&scope=hello+openid&access_token=abc'
+
+ @mock.patch('oauthlib.common.generate_token')
+ def test_optional_nonce(self, generate_token):
+ generate_token.return_value = 'abc'
+ self.request.nonce = 'xyz'
+ scope, info = self.auth.validate_authorization_request(self.request)
+
+ bearer = BearerToken(self.mock_validator)
+ h, b, s = self.auth.create_authorization_response(self.request, bearer)
+ self.assertURLEqual(h['Location'], self.url_fragment, parse_fragment=True)
+ self.assertEqual(b, None)
+ self.assertEqual(s, 302)
+
+
+class OpenIDHybridCodeIdTokenTest(OpenIDAuthCodeTest):
+
+ def setUp(self):
+ super(OpenIDHybridCodeIdTokenTest, self).setUp()
+ self.mock_validator.get_code_challenge.return_value = None
+ self.request.response_type = 'code id_token'
+ self.request.nonce = 'zxc'
+ self.auth = HybridGrant(request_validator=self.mock_validator)
+ token = 'MOCKED_TOKEN'
+ self.url_query = 'https://a.b/cb?code=abc&state=abc&id_token=%s' % token
+ self.url_fragment = 'https://a.b/cb#code=abc&state=abc&id_token=%s' % token
+
+ @mock.patch('oauthlib.common.generate_token')
+ def test_required_nonce(self, generate_token):
+ generate_token.return_value = 'abc'
+ self.request.nonce = None
+ self.assertRaises(errors.InvalidRequestError, self.auth.validate_authorization_request, self.request)
+
+ bearer = BearerToken(self.mock_validator)
+ h, b, s = self.auth.create_authorization_response(self.request, bearer)
+ self.assertIn('error=invalid_request', h['Location'])
+ self.assertEqual(b, None)
+ self.assertEqual(s, 302)
+
+
+class OpenIDHybridCodeIdTokenTokenTest(OpenIDAuthCodeTest):
+
+ def setUp(self):
+ super(OpenIDHybridCodeIdTokenTokenTest, self).setUp()
+ self.mock_validator.get_code_challenge.return_value = None
+ self.request.response_type = 'code id_token token'
+ self.request.nonce = 'xyz'
+ self.auth = HybridGrant(request_validator=self.mock_validator)
+ token = 'MOCKED_TOKEN'
+ self.url_query = 'https://a.b/cb?code=abc&state=abc&token_type=Bearer&expires_in=3600&scope=hello+openid&access_token=abc&id_token=%s' % token
+ self.url_fragment = 'https://a.b/cb#code=abc&state=abc&token_type=Bearer&expires_in=3600&scope=hello+openid&access_token=abc&id_token=%s' % token
+
+ @mock.patch('oauthlib.common.generate_token')
+ def test_required_nonce(self, generate_token):
+ generate_token.return_value = 'abc'
+ self.request.nonce = None
+ self.assertRaises(errors.InvalidRequestError, self.auth.validate_authorization_request, self.request)
+
+ bearer = BearerToken(self.mock_validator)
+ h, b, s = self.auth.create_authorization_response(self.request, bearer)
+ self.assertIn('error=invalid_request', h['Location'])
+ self.assertEqual(b, None)
+ self.assertEqual(s, 302)
diff --git a/tests/openid/connect/core/grant_types/test_implicit.py b/tests/openid/connect/core/grant_types/test_implicit.py
index 7ab198a..1ee805c 100644
--- a/tests/openid/connect/core/grant_types/test_implicit.py
+++ b/tests/openid/connect/core/grant_types/test_implicit.py
@@ -4,13 +4,13 @@ from __future__ import absolute_import, unicode_literals
import mock
from oauthlib.common import Request
+from oauthlib.oauth2.rfc6749 import errors
from oauthlib.oauth2.rfc6749.tokens import BearerToken
from oauthlib.openid.connect.core.grant_types.exceptions import OIDCNoPrompt
-from oauthlib.openid.connect.core.grant_types.hybrid import HybridGrant
from oauthlib.openid.connect.core.grant_types.implicit import ImplicitGrant
from tests.oauth2.rfc6749.grant_types.test_implicit import ImplicitGrantTest
from tests.unittest import TestCase
-from .test_authorization_code import get_id_token_mock, OpenIDAuthCodeTest
+from .test_authorization_code import get_id_token_mock
class OpenIDImplicitInterferenceTest(ImplicitGrantTest):
@@ -30,8 +30,8 @@ class OpenIDImplicitTest(TestCase):
self.request.client_id = 'abcdef'
self.request.response_type = 'id_token token'
self.request.redirect_uri = 'https://a.b/cb'
- self.request.nonce = 'zxc'
self.request.state = 'abc'
+ self.request.nonce = 'xyz'
self.mock_validator = mock.MagicMock()
self.mock_validator.get_id_token.side_effect = get_id_token_mock
@@ -61,12 +61,6 @@ class OpenIDImplicitTest(TestCase):
self.assertEqual(b, None)
self.assertEqual(s, 302)
- self.request.nonce = None
- h, b, s = self.auth.create_authorization_response(self.request, bearer)
- self.assertIn('error=invalid_request', h['Location'])
- self.assertEqual(b, None)
- self.assertEqual(s, 302)
-
@mock.patch('oauthlib.common.generate_token')
def test_no_prompt_authorization(self, generate_token):
generate_token.return_value = 'abc'
@@ -105,36 +99,35 @@ class OpenIDImplicitTest(TestCase):
h, b, s = self.auth.create_authorization_response(self.request, bearer)
self.assertIn('error=login_required', h['Location'])
+ @mock.patch('oauthlib.common.generate_token')
+ def test_required_nonce(self, generate_token):
+ generate_token.return_value = 'abc'
+ self.request.nonce = None
+ self.assertRaises(errors.InvalidRequestError, self.auth.validate_authorization_request, self.request)
-class OpenIDHybridCodeTokenTest(OpenIDAuthCodeTest):
-
- def setUp(self):
- super(OpenIDHybridCodeTokenTest, self).setUp()
- self.request.response_type = 'code token'
- self.auth = HybridGrant(request_validator=self.mock_validator)
- self.url_query = 'https://a.b/cb?code=abc&state=abc&token_type=Bearer&expires_in=3600&scope=hello+openid&access_token=abc'
- self.url_fragment = 'https://a.b/cb#code=abc&state=abc&token_type=Bearer&expires_in=3600&scope=hello+openid&access_token=abc'
-
+ bearer = BearerToken(self.mock_validator)
+ h, b, s = self.auth.create_authorization_response(self.request, bearer)
+ self.assertIn('error=invalid_request', h['Location'])
+ self.assertEqual(b, None)
+ self.assertEqual(s, 302)
-class OpenIDHybridCodeIdTokenTest(OpenIDAuthCodeTest):
+class OpenIDImplicitNoAccessTokenTest(OpenIDImplicitTest):
def setUp(self):
- super(OpenIDHybridCodeIdTokenTest, self).setUp()
- self.mock_validator.get_code_challenge.return_value = None
- self.request.response_type = 'code id_token'
- self.auth = HybridGrant(request_validator=self.mock_validator)
+ super(OpenIDImplicitNoAccessTokenTest, self).setUp()
+ self.request.response_type = 'id_token'
token = 'MOCKED_TOKEN'
- self.url_query = 'https://a.b/cb?code=abc&state=abc&id_token=%s' % token
- self.url_fragment = 'https://a.b/cb#code=abc&state=abc&id_token=%s' % token
-
+ self.url_query = 'https://a.b/cb?state=abc&id_token=%s' % token
+ self.url_fragment = 'https://a.b/cb#state=abc&id_token=%s' % token
-class OpenIDHybridCodeIdTokenTokenTest(OpenIDAuthCodeTest):
+ @mock.patch('oauthlib.common.generate_token')
+ def test_required_nonce(self, generate_token):
+ generate_token.return_value = 'abc'
+ self.request.nonce = None
+ self.assertRaises(errors.InvalidRequestError, self.auth.validate_authorization_request, self.request)
- def setUp(self):
- super(OpenIDHybridCodeIdTokenTokenTest, self).setUp()
- self.mock_validator.get_code_challenge.return_value = None
- self.request.response_type = 'code id_token token'
- self.auth = HybridGrant(request_validator=self.mock_validator)
- token = 'MOCKED_TOKEN'
- self.url_query = 'https://a.b/cb?code=abc&state=abc&token_type=Bearer&expires_in=3600&scope=hello+openid&access_token=abc&id_token=%s' % token
- self.url_fragment = 'https://a.b/cb#code=abc&state=abc&token_type=Bearer&expires_in=3600&scope=hello+openid&access_token=abc&id_token=%s' % token
+ bearer = BearerToken(self.mock_validator)
+ h, b, s = self.auth.create_authorization_response(self.request, bearer)
+ self.assertIn('error=invalid_request', h['Location'])
+ self.assertEqual(b, None)
+ self.assertEqual(s, 302)