summaryrefslogtreecommitdiff
path: root/tests/oauth2
diff options
context:
space:
mode:
Diffstat (limited to 'tests/oauth2')
-rw-r--r--tests/oauth2/rfc6749/endpoints/test_client_authentication.py2
-rw-r--r--tests/oauth2/rfc6749/endpoints/test_credentials_preservation.py1
-rw-r--r--tests/oauth2/rfc6749/endpoints/test_error_responses.py1
-rw-r--r--tests/oauth2/rfc6749/endpoints/test_metadata.py88
-rw-r--r--tests/oauth2/rfc6749/endpoints/test_resource_owner_association.py1
-rw-r--r--tests/oauth2/rfc6749/endpoints/test_scope_handling.py1
-rw-r--r--tests/oauth2/rfc6749/grant_types/test_authorization_code.py124
-rw-r--r--tests/oauth2/rfc6749/test_server.py3
8 files changed, 221 insertions, 0 deletions
diff --git a/tests/oauth2/rfc6749/endpoints/test_client_authentication.py b/tests/oauth2/rfc6749/endpoints/test_client_authentication.py
index e9a0673..48c5f5a 100644
--- a/tests/oauth2/rfc6749/endpoints/test_client_authentication.py
+++ b/tests/oauth2/rfc6749/endpoints/test_client_authentication.py
@@ -32,6 +32,8 @@ class ClientAuthenticationTest(TestCase):
def setUp(self):
self.validator = mock.MagicMock(spec=RequestValidator)
+ self.validator.is_pkce_required.return_value = False
+ self.validator.get_code_challenge.return_value = None
self.validator.get_default_redirect_uri.return_value = 'http://i.b./path'
self.web = WebApplicationServer(self.validator,
token_generator=self.inspect_client)
diff --git a/tests/oauth2/rfc6749/endpoints/test_credentials_preservation.py b/tests/oauth2/rfc6749/endpoints/test_credentials_preservation.py
index 50c2956..1a2f66b 100644
--- a/tests/oauth2/rfc6749/endpoints/test_credentials_preservation.py
+++ b/tests/oauth2/rfc6749/endpoints/test_credentials_preservation.py
@@ -24,6 +24,7 @@ class PreservationTest(TestCase):
def setUp(self):
self.validator = mock.MagicMock(spec=RequestValidator)
self.validator.get_default_redirect_uri.return_value = self.DEFAULT_REDIRECT_URI
+ self.validator.get_code_challenge.return_value = None
self.validator.authenticate_client.side_effect = self.set_client
self.web = WebApplicationServer(self.validator)
self.mobile = MobileApplicationServer(self.validator)
diff --git a/tests/oauth2/rfc6749/endpoints/test_error_responses.py b/tests/oauth2/rfc6749/endpoints/test_error_responses.py
index ef05c4d..a249cb1 100644
--- a/tests/oauth2/rfc6749/endpoints/test_error_responses.py
+++ b/tests/oauth2/rfc6749/endpoints/test_error_responses.py
@@ -24,6 +24,7 @@ class ErrorResponseTest(TestCase):
def setUp(self):
self.validator = mock.MagicMock(spec=RequestValidator)
self.validator.get_default_redirect_uri.return_value = None
+ self.validator.get_code_challenge.return_value = None
self.web = WebApplicationServer(self.validator)
self.mobile = MobileApplicationServer(self.validator)
self.legacy = LegacyApplicationServer(self.validator)
diff --git a/tests/oauth2/rfc6749/endpoints/test_metadata.py b/tests/oauth2/rfc6749/endpoints/test_metadata.py
index 301e846..4813b46 100644
--- a/tests/oauth2/rfc6749/endpoints/test_metadata.py
+++ b/tests/oauth2/rfc6749/endpoints/test_metadata.py
@@ -3,6 +3,7 @@ from __future__ import absolute_import, unicode_literals
from oauthlib.oauth2 import MetadataEndpoint
from oauthlib.oauth2 import TokenEndpoint
+from oauthlib.oauth2 import Server
from ....unittest import TestCase
@@ -13,6 +14,33 @@ class MetadataEndpointTest(TestCase):
"issuer": 'https://foo.bar'
}
+ def test_openid_oauth2_preconfigured(self):
+ default_claims = {
+ "issuer": 'https://foo.bar',
+ "authorization_endpoint": "https://foo.bar/authorize",
+ "revocation_endpoint": "https://foo.bar/revoke",
+ "introspection_endpoint": "https://foo.bar/introspect",
+ "token_endpoint": "https://foo.bar/token"
+ }
+ from oauthlib.oauth2 import Server as OAuth2Server
+ from oauthlib.openid import Server as OpenIDServer
+
+ endpoint = OAuth2Server(None)
+ metadata = MetadataEndpoint([endpoint], default_claims)
+ oauth2_claims = metadata.claims
+
+ endpoint = OpenIDServer(None)
+ metadata = MetadataEndpoint([endpoint], default_claims)
+ openid_claims = metadata.claims
+
+ # Pure OAuth2 Authorization Metadata are similar with OpenID but
+ # response_type not! (OIDC contains "id_token" and hybrid flows)
+ del oauth2_claims['response_types_supported']
+ del openid_claims['response_types_supported']
+
+ self.maxDiff = None
+ self.assertEqual(openid_claims, oauth2_claims)
+
def test_token_endpoint(self):
endpoint = TokenEndpoint(None, None, grant_types={"password": None})
metadata = MetadataEndpoint([endpoint], {
@@ -36,3 +64,63 @@ class MetadataEndpointTest(TestCase):
metadata = MetadataEndpoint([], self.metadata)
self.assertIn("issuer", metadata.claims)
self.assertEqual(metadata.claims["issuer"], 'https://foo.bar')
+
+ def test_server_metadata(self):
+ endpoint = Server(None)
+ metadata = MetadataEndpoint([endpoint], {
+ "issuer": 'https://foo.bar',
+ "authorization_endpoint": "https://foo.bar/authorize",
+ "introspection_endpoint": "https://foo.bar/introspect",
+ "revocation_endpoint": "https://foo.bar/revoke",
+ "token_endpoint": "https://foo.bar/token",
+ "jwks_uri": "https://foo.bar/certs",
+ "scopes_supported": ["email", "profile"]
+ })
+ expected_claims = {
+ "issuer": "https://foo.bar",
+ "authorization_endpoint": "https://foo.bar/authorize",
+ "introspection_endpoint": "https://foo.bar/introspect",
+ "revocation_endpoint": "https://foo.bar/revoke",
+ "token_endpoint": "https://foo.bar/token",
+ "jwks_uri": "https://foo.bar/certs",
+ "scopes_supported": ["email", "profile"],
+ "grant_types_supported": [
+ "authorization_code",
+ "password",
+ "client_credentials",
+ "refresh_token",
+ "implicit"
+ ],
+ "token_endpoint_auth_methods_supported": [
+ "client_secret_post",
+ "client_secret_basic"
+ ],
+ "response_types_supported": [
+ "code",
+ "token"
+ ],
+ "response_modes_supported": [
+ "query",
+ "fragment"
+ ],
+ "code_challenge_methods_supported": [
+ "plain",
+ "S256"
+ ],
+ "revocation_endpoint_auth_methods_supported": [
+ "client_secret_post",
+ "client_secret_basic"
+ ],
+ "introspection_endpoint_auth_methods_supported": [
+ "client_secret_post",
+ "client_secret_basic"
+ ]
+ }
+
+ def sort_list(claims):
+ for k in claims.keys():
+ claims[k] = sorted(claims[k])
+
+ sort_list(metadata.claims)
+ sort_list(expected_claims)
+ self.assertEqual(sorted(metadata.claims.items()), sorted(expected_claims.items()))
diff --git a/tests/oauth2/rfc6749/endpoints/test_resource_owner_association.py b/tests/oauth2/rfc6749/endpoints/test_resource_owner_association.py
index d30ec9d..e823286 100644
--- a/tests/oauth2/rfc6749/endpoints/test_resource_owner_association.py
+++ b/tests/oauth2/rfc6749/endpoints/test_resource_owner_association.py
@@ -46,6 +46,7 @@ class ResourceOwnerAssociationTest(TestCase):
def setUp(self):
self.validator = mock.MagicMock(spec=RequestValidator)
self.validator.get_default_redirect_uri.return_value = 'http://i.b./path'
+ self.validator.get_code_challenge.return_value = None
self.validator.authenticate_client.side_effect = self.set_client
self.web = WebApplicationServer(self.validator,
token_generator=self.inspect_client)
diff --git a/tests/oauth2/rfc6749/endpoints/test_scope_handling.py b/tests/oauth2/rfc6749/endpoints/test_scope_handling.py
index 8490c03..4f27963 100644
--- a/tests/oauth2/rfc6749/endpoints/test_scope_handling.py
+++ b/tests/oauth2/rfc6749/endpoints/test_scope_handling.py
@@ -42,6 +42,7 @@ class TestScopeHandling(TestCase):
def setUp(self):
self.validator = mock.MagicMock(spec=RequestValidator)
self.validator.get_default_redirect_uri.return_value = TestScopeHandling.DEFAULT_REDIRECT_URI
+ self.validator.get_code_challenge.return_value = None
self.validator.authenticate_client.side_effect = self.set_client
self.server = Server(self.validator)
self.web = WebApplicationServer(self.validator)
diff --git a/tests/oauth2/rfc6749/grant_types/test_authorization_code.py b/tests/oauth2/rfc6749/grant_types/test_authorization_code.py
index acb23ac..00e2b6d 100644
--- a/tests/oauth2/rfc6749/grant_types/test_authorization_code.py
+++ b/tests/oauth2/rfc6749/grant_types/test_authorization_code.py
@@ -8,6 +8,7 @@ import mock
from oauthlib.common import Request
from oauthlib.oauth2.rfc6749 import errors
from oauthlib.oauth2.rfc6749.grant_types import AuthorizationCodeGrant
+from oauthlib.oauth2.rfc6749.grant_types import authorization_code
from oauthlib.oauth2.rfc6749.tokens import BearerToken
from ....unittest import TestCase
@@ -27,6 +28,8 @@ class AuthorizationCodeGrantTest(TestCase):
self.request.redirect_uri = 'https://a.b/cb'
self.mock_validator = mock.MagicMock()
+ self.mock_validator.is_pkce_required.return_value = False
+ self.mock_validator.get_code_challenge.return_value = None
self.mock_validator.authenticate_client.side_effect = self.set_client
self.auth = AuthorizationCodeGrant(request_validator=self.mock_validator)
@@ -200,3 +203,124 @@ class AuthorizationCodeGrantTest(TestCase):
self.mock_validator.confirm_redirect_uri.return_value = False
self.assertRaises(errors.MismatchingRedirectURIError,
self.auth.validate_token_request, self.request)
+
+ # PKCE validate_authorization_request
+ def test_pkce_challenge_missing(self):
+ self.mock_validator.is_pkce_required.return_value = True
+ self.assertRaises(errors.MissingCodeChallengeError,
+ self.auth.validate_authorization_request, self.request)
+
+ def test_pkce_default_method(self):
+ for required in [True, False]:
+ self.mock_validator.is_pkce_required.return_value = required
+ self.request.code_challenge = "present"
+ _, ri = self.auth.validate_authorization_request(self.request)
+ self.assertIsNotNone(ri["request"].code_challenge_method)
+ self.assertEqual(ri["request"].code_challenge_method, "plain")
+
+ def test_pkce_wrong_method(self):
+ for required in [True, False]:
+ self.mock_validator.is_pkce_required.return_value = required
+ self.request.code_challenge = "present"
+ self.request.code_challenge_method = "foobar"
+ self.assertRaises(errors.UnsupportedCodeChallengeMethodError,
+ self.auth.validate_authorization_request, self.request)
+
+ # PKCE validate_token_request
+ def test_pkce_verifier_missing(self):
+ self.mock_validator.is_pkce_required.return_value = True
+ self.assertRaises(errors.MissingCodeVerifierError,
+ self.auth.validate_token_request, self.request)
+
+ # PKCE validate_token_request
+ def test_pkce_required_verifier_missing_challenge_missing(self):
+ self.mock_validator.is_pkce_required.return_value = True
+ self.request.code_verifier = None
+ self.mock_validator.get_code_challenge.return_value = None
+ self.assertRaises(errors.MissingCodeVerifierError,
+ self.auth.validate_token_request, self.request)
+
+ def test_pkce_required_verifier_missing_challenge_valid(self):
+ self.mock_validator.is_pkce_required.return_value = True
+ self.request.code_verifier = None
+ self.mock_validator.get_code_challenge.return_value = "foo"
+ self.assertRaises(errors.MissingCodeVerifierError,
+ self.auth.validate_token_request, self.request)
+
+ def test_pkce_required_verifier_valid_challenge_missing(self):
+ self.mock_validator.is_pkce_required.return_value = True
+ self.request.code_verifier = "foobar"
+ self.mock_validator.get_code_challenge.return_value = None
+ self.assertRaises(errors.InvalidGrantError,
+ self.auth.validate_token_request, self.request)
+
+ def test_pkce_required_verifier_valid_challenge_valid_method_valid(self):
+ self.mock_validator.is_pkce_required.return_value = True
+ self.request.code_verifier = "foobar"
+ self.mock_validator.get_code_challenge.return_value = "foobar"
+ self.mock_validator.get_code_challenge_method.return_value = "plain"
+ self.auth.validate_token_request(self.request)
+
+ def test_pkce_required_verifier_invalid_challenge_valid_method_valid(self):
+ self.mock_validator.is_pkce_required.return_value = True
+ self.request.code_verifier = "foobar"
+ self.mock_validator.get_code_challenge.return_value = "raboof"
+ self.mock_validator.get_code_challenge_method.return_value = "plain"
+ self.assertRaises(errors.InvalidGrantError,
+ self.auth.validate_token_request, self.request)
+
+ def test_pkce_required_verifier_valid_challenge_valid_method_wrong(self):
+ self.mock_validator.is_pkce_required.return_value = True
+ self.request.code_verifier = "present"
+ self.mock_validator.get_code_challenge.return_value = "foobar"
+ self.mock_validator.get_code_challenge_method.return_value = "cryptic_method"
+ self.assertRaises(errors.ServerError,
+ self.auth.validate_token_request, self.request)
+
+ def test_pkce_verifier_valid_challenge_valid_method_missing(self):
+ self.mock_validator.is_pkce_required.return_value = True
+ self.request.code_verifier = "present"
+ self.mock_validator.get_code_challenge.return_value = "foobar"
+ self.mock_validator.get_code_challenge_method.return_value = None
+ self.assertRaises(errors.InvalidGrantError,
+ self.auth.validate_token_request, self.request)
+
+ def test_pkce_optional_verifier_valid_challenge_missing(self):
+ self.mock_validator.is_pkce_required.return_value = False
+ self.request.code_verifier = "present"
+ self.mock_validator.get_code_challenge.return_value = None
+ self.auth.validate_token_request(self.request)
+
+ def test_pkce_optional_verifier_missing_challenge_valid(self):
+ self.mock_validator.is_pkce_required.return_value = False
+ self.request.code_verifier = None
+ self.mock_validator.get_code_challenge.return_value = "foobar"
+ self.assertRaises(errors.MissingCodeVerifierError,
+ self.auth.validate_token_request, self.request)
+
+ # PKCE functions
+ def test_wrong_code_challenge_method_plain(self):
+ self.assertFalse(authorization_code.code_challenge_method_plain("foo", "bar"))
+
+ def test_correct_code_challenge_method_plain(self):
+ self.assertTrue(authorization_code.code_challenge_method_plain("foo", "foo"))
+
+ def test_wrong_code_challenge_method_s256(self):
+ self.assertFalse(authorization_code.code_challenge_method_s256("foo", "bar"))
+
+ def test_correct_code_challenge_method_s256(self):
+ # "abcd" as verifier gives a '+' to base64
+ self.assertTrue(
+ authorization_code.code_challenge_method_s256("abcd",
+ "iNQmb9TmM40TuEX88olXnSCciXgjuSF9o-Fhk28DFYk")
+ )
+ # "/" as verifier gives a '/' and '+' to base64
+ self.assertTrue(
+ authorization_code.code_challenge_method_s256("/",
+ "il7asoJjJEMhngUeSt4tHVu8Zxx4EFG_FDeJfL3-oPE")
+ )
+ # Example from PKCE RFCE
+ self.assertTrue(
+ authorization_code.code_challenge_method_s256("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
+ "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM")
+ )
diff --git a/tests/oauth2/rfc6749/test_server.py b/tests/oauth2/rfc6749/test_server.py
index bc7a2b7..b623a9b 100644
--- a/tests/oauth2/rfc6749/test_server.py
+++ b/tests/oauth2/rfc6749/test_server.py
@@ -23,6 +23,7 @@ class AuthorizationEndpointTest(TestCase):
def setUp(self):
self.mock_validator = mock.MagicMock()
+ self.mock_validator.get_code_challenge.return_value = None
self.addCleanup(setattr, self, 'mock_validator', mock.MagicMock())
auth_code = AuthorizationCodeGrant(
request_validator=self.mock_validator)
@@ -117,6 +118,7 @@ class TokenEndpointTest(TestCase):
self.mock_validator = mock.MagicMock()
self.mock_validator.authenticate_client.side_effect = set_user
+ self.mock_validator.get_code_challenge.return_value = None
self.addCleanup(setattr, self, 'mock_validator', mock.MagicMock())
auth_code = AuthorizationCodeGrant(
request_validator=self.mock_validator)
@@ -218,6 +220,7 @@ class SignedTokenEndpointTest(TestCase):
return True
self.mock_validator = mock.MagicMock()
+ self.mock_validator.get_code_challenge.return_value = None
self.mock_validator.authenticate_client.side_effect = set_user
self.addCleanup(setattr, self, 'mock_validator', mock.MagicMock())