diff options
-rw-r--r-- | docs/oauth2/endpoints.rst | 23 | ||||
-rw-r--r-- | docs/oauth2/server.rst | 34 | ||||
-rw-r--r-- | oauthlib/oauth2/rfc6749/__init__.py | 4 | ||||
-rw-r--r-- | oauthlib/oauth2/rfc6749/endpoints/base.py | 4 | ||||
-rw-r--r-- | oauthlib/oauth2/rfc6749/grant_types/authorization_code.py | 10 | ||||
-rw-r--r-- | oauthlib/oauth2/rfc6749/grant_types/client_credentials.py | 4 | ||||
-rw-r--r-- | oauthlib/oauth2/rfc6749/grant_types/implicit.py | 8 | ||||
-rw-r--r-- | oauthlib/oauth2/rfc6749/grant_types/refresh_token.py | 4 | ||||
-rw-r--r-- | oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py | 4 | ||||
-rw-r--r-- | tests/oauth2/rfc6749/test_grant_types.py | 22 | ||||
-rw-r--r-- | tests/oauth2/rfc6749/test_server.py | 30 | ||||
-rw-r--r-- | tests/oauth2/rfc6749/test_servers.py | 178 |
12 files changed, 179 insertions, 146 deletions
diff --git a/docs/oauth2/endpoints.rst b/docs/oauth2/endpoints.rst index aa73d46..1441938 100644 --- a/docs/oauth2/endpoints.rst +++ b/docs/oauth2/endpoints.rst @@ -79,9 +79,8 @@ Grant and the Client Credentials Grant. except FatalClientError as e: # this is your custom error page - from your_views import authorization_error_page_uri - # Use in_uri to embed error code and description in the redirect uri - redirect(e.in_uri(authorization_error_page_uri)) + from your_view_helpers import error_to_response + return error_to_response(e) **Post Authorization Request** @@ -107,23 +106,22 @@ Grant and the Client Credentials Grant. scopes = request.POST.get('scopes') from oauthlib.oauth2 import FatalClientError, OAuth2Error - from your_framework import redirect + from your_framework import http_response + http_response(body, status=status, headers=headers) try: - uri, headers, body, status = server.create_authorization_response( + headers, body, status = server.create_authorization_response( uri, http_method, body, headers, scopes, credentials) - # uri = https://foo.com/welcome_back?code=somerandomstring&state=xyz - # headers = {}, this might change to include suggested headers related + # headers = {'Location': 'https://foo.com/welcome_back?code=somerandomstring&state=xyz'}, this might change to include suggested headers related # to cache best practices etc. # body = '', this might be set in future custom grant types # status = 302, suggested HTTP status code - redirect(uri, headers=headers, status=status, body=body) + return http_response(body, status=status, headers=headers) except FatalClientError as e: # this is your custom error page - from your_views import authorization_error_page_uri - # Use in_uri to embed error code and description in the redirect uri - redirect(e.in_uri(authorization_error_page_uri)) + from your_view_helpers import error_to_response + return error_to_response(e) except OAuth2Error as e: # Less grave errors will be reported back to client @@ -181,10 +179,9 @@ tokens which unless you are certain you need them, are a bad idea. # Extra credentials you wish to include credentials = {'client_ip': '1.2.3.4'} - uri, headers, body, status = server.create_token_response( + headers, body, status = server.create_token_response( uri, http_method, body, headers, credentials) - # uri is not used by most grant types # headers will contain some suggested headers to add to your response { 'Content-Type': 'application/json;charset=UTF-8', diff --git a/docs/oauth2/server.rst b/docs/oauth2/server.rst index 22e5d43..cba56d1 100644 --- a/docs/oauth2/server.rst +++ b/docs/oauth2/server.rst @@ -260,7 +260,6 @@ as well as provide an interface for a backend to store tokens, clients, etc. def __init__(self): # Using the server from previous section self._authorization_endpoint = server - self._error_uri = '/error' def get(self, request): # You need to define extract_params and make sure it does not @@ -288,7 +287,7 @@ as well as provide an interface for a backend to store tokens, clients, etc. # Errors that should be shown to the user on the provider website except errors.FatalClientError as e: - return HttpResponseRedirect(e.in_uri(self._error_uri)) + return response_from_error(e) # Errors embedded in the redirect URI back to the client except errors.OAuth2Error as e: @@ -297,7 +296,7 @@ as well as provide an interface for a backend to store tokens, clients, etc. @csrf_exempt def post(self, request): uri, http_method, body, headers = extract_params(request) - + # The scopes the user actually authorized, i.e. checkboxes # that were selected. scopes = request.POST.getlist(['scopes']) @@ -309,15 +308,12 @@ as well as provide an interface for a backend to store tokens, clients, etc. credentials.update(request.session.get('oauth2_credentials', {})) try: - url, headers, body, status = self._authorization_endpoint.create_authorization_response( + headers, body, status = self._authorization_endpoint.create_authorization_response( uri, http_method, body, headers, scopes, credentials) - return HttpResponseRedirect(url) + return response_from_return(headers, body, status) except errors.FatalClientError as e: - return HttpResponseRedirect(e.in_uri(self._error_uri)) - - except errors.OAuth2Error as e: - return HttpResponseRedirect(e.in_uri(redirect_uri)) + return response_from_error(e) # Handles requests to /token class TokenView(View): @@ -333,21 +329,23 @@ as well as provide an interface for a backend to store tokens, clients, etc. # use in the validator, do so here. credentials = {'foo': 'bar'} - url, headers, body, status = self._token_endpoint.create_token_response( + headers, body, status = self._token_endpoint.create_token_response( uri, http_method, body, headers, credentials) # All requests to /token will return a json response, no redirection. - response = HttpResponse(content=body, status=status) - for k, v in headers.items(): - response[k] = v - return response - + return response_from_return(headers, body, status) - class ErrorView(View): - response = HttpResponse() - response.write('Evil client is unable to send a proper request.') + def response_from_return(headers, body, status): + response = HttpResponse(content=body, status=status) + for k, v in headers.items(): + response[k] = v return response + def response_from_error(e) + return HttpResponseBadRequest('Evil client is unable to send a proper request. Error is: ' + e.description) + + + **5. Protect your APIs using scopes** Let's define a decorator we can use to protect the views. diff --git a/oauthlib/oauth2/rfc6749/__init__.py b/oauthlib/oauth2/rfc6749/__init__.py index c82c06a..6903955 100644 --- a/oauthlib/oauth2/rfc6749/__init__.py +++ b/oauthlib/oauth2/rfc6749/__init__.py @@ -43,7 +43,7 @@ def catch_errors_and_unavailability(f): if not endpoint.available: e = TemporarilyUnavailableError() log.info('Endpoint unavailable, ignoring request %s.' % uri) - return None, {}, e.json, 503 + return {}, e.json, 503 if endpoint.catch_errors: try: @@ -55,7 +55,7 @@ def catch_errors_and_unavailability(f): except Exception as e: error = ServerError() log.warning('Exception caught while processing request, %s.' % e) - return None, {}, error.json, 500 + return {}, error.json, 500 else: return f(endpoint, uri, *args, **kwargs) return wrapper diff --git a/oauthlib/oauth2/rfc6749/endpoints/base.py b/oauthlib/oauth2/rfc6749/endpoints/base.py index 41b3aef..984de2f 100644 --- a/oauthlib/oauth2/rfc6749/endpoints/base.py +++ b/oauthlib/oauth2/rfc6749/endpoints/base.py @@ -44,7 +44,7 @@ def catch_errors_and_unavailability(f): if not endpoint.available: e = TemporarilyUnavailableError() log.info('Endpoint unavailable, ignoring request %s.' % uri) - return None, {}, e.json, 503 + return {}, e.json, 503 if endpoint.catch_errors: try: @@ -56,7 +56,7 @@ def catch_errors_and_unavailability(f): except Exception as e: error = ServerError() log.warning('Exception caught while processing request, %s.' % e) - return None, {}, error.json, 500 + return {}, error.json, 500 else: return f(endpoint, uri, *args, **kwargs) return wrapper diff --git a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py index 55f70f3..71d018b 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py +++ b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py @@ -131,7 +131,7 @@ class AuthorizationCodeGrant(GrantTypeBase): :param request: oauthlib.commong.Request :param token_handler: A token handler instace, for example of type oauthlib.oauth2.BearerToken. - :returns: uri, headers, body, status + :returns: headers, body, status :raises: FatalClientError on invalid redirect URI or client id. ValueError if scopes are not set on the request object. @@ -203,12 +203,12 @@ class AuthorizationCodeGrant(GrantTypeBase): except errors.OAuth2Error as e: log.debug('Client error during validation of %r. %r.', request, e) request.redirect_uri = request.redirect_uri or self.error_uri - return common.add_params_to_uri(request.redirect_uri, e.twotuples), None, None, e.status_code + return {'Location': common.add_params_to_uri(request.redirect_uri, e.twotuples)}, None, 302 grant = self.create_authorization_code(request) log.debug('Saving grant %r for %r.', grant, request) self.request_validator.save_authorization_code(request.client_id, grant, request) - return common.add_params_to_uri(request.redirect_uri, grant.items()), None, None, 302 + return {'Location': common.add_params_to_uri(request.redirect_uri, grant.items())}, None, 302 def create_token_response(self, request, token_handler): """Validate the authorization code. @@ -229,12 +229,12 @@ class AuthorizationCodeGrant(GrantTypeBase): log.debug('Token request validation ok for %r.', request) except errors.OAuth2Error as e: log.debug('Client error during validation of %r. %r.', request, e) - return None, headers, e.json, e.status_code + return headers, e.json, e.status_code token = token_handler.create_token(request, refresh_token=True) self.request_validator.invalidate_authorization_code( request.client_id, request.code, request) - return None, headers, json.dumps(token), 200 + return headers, json.dumps(token), 200 def validate_authorization_request(self, request): """Check the authorization request for normal and fatal errors. diff --git a/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py b/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py index b2bfff3..6641f1f 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py +++ b/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py @@ -66,12 +66,12 @@ class ClientCredentialsGrant(GrantTypeBase): self.validate_token_request(request) except errors.OAuth2Error as e: log.debug('Client error in token request. %s.', e) - return None, {}, e.json, e.status_code + return {}, e.json, e.status_code token = token_handler.create_token(request, refresh_token=False) log.debug('Issuing token to client id %r (%r), %r.', request.client_id, request.client, token) - return None, {}, json.dumps(token), 200 + return {}, json.dumps(token), 200 def validate_token_request(self, request): if not getattr(request, 'grant_type'): diff --git a/oauthlib/oauth2/rfc6749/grant_types/implicit.py b/oauthlib/oauth2/rfc6749/grant_types/implicit.py index 7e2cc54..0761287 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/implicit.py +++ b/oauthlib/oauth2/rfc6749/grant_types/implicit.py @@ -220,12 +220,12 @@ class ImplicitGrant(GrantTypeBase): # http://tools.ietf.org/html/rfc6749#appendix-B except errors.OAuth2Error as e: log.debug('Client error during validation of %r. %r.', request, e) - return common.add_params_to_uri(request.redirect_uri, e.twotuples, - fragment=True), {}, None, e.status_code + return {'Location': common.add_params_to_uri(request.redirect_uri, e.twotuples, + fragment=True)}, None, 302 token = token_handler.create_token(request, refresh_token=False) - return common.add_params_to_uri(request.redirect_uri, token.items(), - fragment=True), {}, None, 302 + return {'Location': common.add_params_to_uri(request.redirect_uri, token.items(), + fragment=True)}, None, 302 def validate_authorization_request(self, request): return self.validate_token_request(request) diff --git a/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py b/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py index 8ea176e..644f7eb 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py +++ b/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py @@ -54,13 +54,13 @@ class RefreshTokenGrant(GrantTypeBase): log.debug('Validating refresh token request, %r.', request) self.validate_token_request(request) except errors.OAuth2Error as e: - return None, headers, e.json, e.status_code + return headers, e.json, e.status_code token = token_handler.create_token(request, refresh_token=self.issue_new_refresh_tokens) log.debug('Issuing new token to client id %r (%r), %r.', request.client_id, request.client, token) - return None, headers, json.dumps(token), 200 + return headers, json.dumps(token), 200 def validate_token_request(self, request): # REQUIRED. Value MUST be set to "refresh_token". diff --git a/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py b/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py index 5b991c6..8629518 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py +++ b/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py @@ -105,12 +105,12 @@ class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase): self.validate_token_request(request) except errors.OAuth2Error as e: log.debug('Client error in token request, %s.', e) - return None, headers, e.json, e.status_code + return headers, e.json, e.status_code token = token_handler.create_token(request, refresh_token=True) log.debug('Issuing token %r to client id %r (%r) and username %s.', token, request.client_id, request.client, request.username) - return None, headers, json.dumps(token), 200 + return headers, json.dumps(token), 200 def validate_token_request(self, request): """ diff --git a/tests/oauth2/rfc6749/test_grant_types.py b/tests/oauth2/rfc6749/test_grant_types.py index 3830d3b..6aee0e8 100644 --- a/tests/oauth2/rfc6749/test_grant_types.py +++ b/tests/oauth2/rfc6749/test_grant_types.py @@ -70,7 +70,7 @@ class AuthorizationCodeGrantTest(TestCase): def test_create_token_response(self): bearer = BearerToken(self.mock_validator) - u, h, token, s = self.auth.create_token_response(self.request, bearer) + h, token, s = self.auth.create_token_response(self.request, bearer) token = json.loads(token) self.assertIn('access_token', token) self.assertIn('refresh_token', token) @@ -122,10 +122,12 @@ class ImplicitGrantTest(TestCase): orig_generate_token = common.generate_token self.addCleanup(setattr, common, 'generate_token', orig_generate_token) common.generate_token = lambda *args, **kwargs: '1234' - uri, headers, body, status_code = self.auth.create_token_response( + headers, body, status_code = self.auth.create_token_response( self.request, bearer) correct_uri = 'https://b.c/p#access_token=1234&token_type=Bearer&expires_in=1800&state=xyz&scope=hello+world' - self.assertURLEqual(uri, correct_uri, parse_fragment=True) + self.assertEqual(status_code, 302) + self.assertIn('Location', headers) + self.assertURLEqual(headers['Location'], correct_uri, parse_fragment=True) def test_error_response(self): pass @@ -148,7 +150,7 @@ class ResourceOwnerPasswordCredentialsGrantTest(TestCase): def test_create_token_response(self): bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( + headers, body, status_code = self.auth.create_token_response( self.request, bearer) token = json.loads(body) self.assertIn('access_token', token) @@ -178,7 +180,7 @@ class ClientCredentialsGrantTest(TestCase): def test_create_token_response(self): bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( + headers, body, status_code = self.auth.create_token_response( self.request, bearer) token = json.loads(body) self.assertIn('access_token', token) @@ -210,7 +212,7 @@ class RefreshTokenGrantTest(TestCase): def test_create_token_response(self): self.mock_validator.get_original_scopes.return_value = ['foo', 'bar'] bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( + headers, body, status_code = self.auth.create_token_response( self.request, bearer) token = json.loads(body) self.assertIn('access_token', token) @@ -222,7 +224,7 @@ class RefreshTokenGrantTest(TestCase): self.request.scope = None self.mock_validator.get_original_scopes.return_value = ['foo', 'bar'] bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( + headers, body, status_code = self.auth.create_token_response( self.request, bearer) token = json.loads(body) self.assertIn('access_token', token) @@ -233,7 +235,7 @@ class RefreshTokenGrantTest(TestCase): def test_invalid_scope(self): self.mock_validator.get_original_scopes.return_value = ['baz'] bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( + headers, body, status_code = self.auth.create_token_response( self.request, bearer) token = json.loads(body) self.assertEqual(token['error'], 'invalid_scope') @@ -242,7 +244,7 @@ class RefreshTokenGrantTest(TestCase): def test_invalid_token(self): self.mock_validator.validate_refresh_token.return_value = False bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( + headers, body, status_code = self.auth.create_token_response( self.request, bearer) token = json.loads(body) self.assertEqual(token['error'], 'invalid_grant') @@ -251,7 +253,7 @@ class RefreshTokenGrantTest(TestCase): def test_invalid_client(self): self.mock_validator.authenticate_client.return_value = False bearer = BearerToken(self.mock_validator) - uri, headers, body, status_code = self.auth.create_token_response( + headers, body, status_code = self.auth.create_token_response( self.request, bearer) token = json.loads(body) self.assertEqual(token['error'], 'invalid_client') diff --git a/tests/oauth2/rfc6749/test_server.py b/tests/oauth2/rfc6749/test_server.py index 24503b6..b6ad6c9 100644 --- a/tests/oauth2/rfc6749/test_server.py +++ b/tests/oauth2/rfc6749/test_server.py @@ -41,35 +41,39 @@ class AuthorizationEndpointTest(TestCase): def test_authorization_grant(self): uri = 'http://i.b/l?response_type=code&client_id=me&scope=all+of+them&state=xyz' uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme' - uri, headers, body, status_code = self.endpoint.create_authorization_response( + headers, body, status_code = self.endpoint.create_authorization_response( uri, scopes=['all', 'of', 'them']) - self.assertURLEqual(uri, 'http://back.to/me?code=abc&state=xyz') + self.assertIn('Location', headers) + self.assertURLEqual(headers['Location'], 'http://back.to/me?code=abc&state=xyz') @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc') def test_implicit_grant(self): uri = 'http://i.b/l?response_type=token&client_id=me&scope=all+of+them&state=xyz' uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme' - uri, headers, body, status_code = self.endpoint.create_authorization_response( + headers, body, status_code = self.endpoint.create_authorization_response( uri, scopes=['all', 'of', 'them']) - self.assertURLEqual(uri, 'http://back.to/me#access_token=abc&expires_in=' + str(self.expires_in) + '&token_type=Bearer&state=xyz&scope=all+of+them', parse_fragment=True) + self.assertIn('Location', headers) + self.assertURLEqual(headers['Location'], 'http://back.to/me#access_token=abc&expires_in=' + str(self.expires_in) + '&token_type=Bearer&state=xyz&scope=all+of+them', parse_fragment=True) def test_missing_type(self): uri = 'http://i.b/l?client_id=me&scope=all+of+them' uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme' self.mock_validator.validate_request = mock.MagicMock( side_effect=errors.InvalidRequestError()) - uri, headers, body, status_code = self.endpoint.create_authorization_response( + headers, body, status_code = self.endpoint.create_authorization_response( uri, scopes=['all', 'of', 'them']) - self.assertURLEqual(uri, 'http://back.to/me?error=invalid_request&error_description=Missing+response_type+parameter.') + self.assertIn('Location', headers) + self.assertURLEqual(headers['Location'], 'http://back.to/me?error=invalid_request&error_description=Missing+response_type+parameter.') def test_invalid_type(self): uri = 'http://i.b/l?response_type=invalid&client_id=me&scope=all+of+them' uri += '&redirect_uri=http%3A%2F%2Fback.to%2Fme' self.mock_validator.validate_request = mock.MagicMock( side_effect=errors.UnsupportedResponseTypeError()) - uri, headers, body, status_code = self.endpoint.create_authorization_response( + headers, body, status_code = self.endpoint.create_authorization_response( uri, scopes=['all', 'of', 'them']) - self.assertURLEqual(uri, 'http://back.to/me?error=unsupported_response_type') + self.assertIn('Location', headers) + self.assertURLEqual(headers['Location'], 'http://back.to/me?error=unsupported_response_type') class TokenEndpointTest(TestCase): @@ -104,7 +108,7 @@ class TokenEndpointTest(TestCase): @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc') def test_authorization_grant(self): body = 'grant_type=authorization_code&code=abc&scope=all+of+them&state=xyz' - uri, headers, body, status_code = self.endpoint.create_token_response( + headers, body, status_code = self.endpoint.create_token_response( '', body=body) token = { 'token_type': 'Bearer', @@ -118,7 +122,7 @@ class TokenEndpointTest(TestCase): @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc') def test_password_grant(self): body = 'grant_type=password&username=a&password=hello&scope=all+of+them' - uri, headers, body, status_code = self.endpoint.create_token_response( + headers, body, status_code = self.endpoint.create_token_response( '', body=body) token = { 'token_type': 'Bearer', @@ -132,7 +136,7 @@ class TokenEndpointTest(TestCase): @mock.patch('oauthlib.common.generate_token', new=lambda: 'abc') def test_client_grant(self): body = 'grant_type=client_credentials&scope=all+of+them' - uri, headers, body, status_code = self.endpoint.create_token_response( + headers, body, status_code = self.endpoint.create_token_response( '', body=body) token = { 'token_type': 'Bearer', @@ -143,13 +147,13 @@ class TokenEndpointTest(TestCase): self.assertEqual(json.loads(body), token) def test_missing_type(self): - _, _, body, _ = self.endpoint.create_token_response('', body='') + _, body, _ = self.endpoint.create_token_response('', body='') token = {'error': 'unsupported_grant_type'} self.assertEqual(json.loads(body), token) def test_invalid_type(self): body = 'grant_type=invalid' - _, _, body, _ = self.endpoint.create_token_response('', body=body) + _, body, _ = self.endpoint.create_token_response('', body=body) token = {'error': 'unsupported_grant_type'} self.assertEqual(json.loads(body), token) diff --git a/tests/oauth2/rfc6749/test_servers.py b/tests/oauth2/rfc6749/test_servers.py index 4f85a95..1a26d7c 100644 --- a/tests/oauth2/rfc6749/test_servers.py +++ b/tests/oauth2/rfc6749/test_servers.py @@ -86,29 +86,34 @@ class TestScopeHandling(TestCase): token_uri = 'http://example.com/path' # authorization grant - uri, _, _, _ = self.web.create_authorization_response( + h, _, s = self.web.create_authorization_response( auth_uri + 'code', scopes=decoded_scope.split(' ')) self.validator.validate_code.side_effect = self.set_scopes(decoded_scope.split(' ')) - code = get_query_credentials(uri)['code'][0] - _, _, body, _ = self.web.create_token_response(token_uri, + self.assertEqual(s, 302) + self.assertIn('Location', h) + code = get_query_credentials(h['Location'])['code'][0] + _, body, _ = self.web.create_token_response(token_uri, body='grant_type=authorization_code&code=%s' % code) self.assertEqual(json.loads(body)['scope'], decoded_scope) # implicit grant - uri, _, _, _ = self.mobile.create_authorization_response( + h, _, s = self.mobile.create_authorization_response( auth_uri + 'token', scopes=decoded_scope.split(' ')) - self.assertEqual(get_fragment_credentials(uri)['scope'][0], decoded_scope) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertEqual(get_fragment_credentials(h['Location'])['scope'][0], decoded_scope) # resource owner password credentials grant body = 'grant_type=password&username=abc&password=secret&scope=%s' - _, _, body, _ = self.legacy.create_token_response(token_uri, + + _, body, _ = self.legacy.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['scope'], decoded_scope) # client credentials grant body = 'grant_type=client_credentials&scope=%s' self.validator.authenticate_client.side_effect = self.set_user - _, _, body, _ = self.backend.create_token_response(token_uri, + _, body, _ = self.backend.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['scope'], decoded_scope) @@ -120,24 +125,28 @@ class TestScopeHandling(TestCase): token_uri = 'http://example.com/path' # authorization grant - uri, _, _, _ = self.web.create_authorization_response( + h, _, s = self.web.create_authorization_response( auth_uri + 'code', scopes=scopes) - code = get_query_credentials(uri)['code'][0] + self.assertEqual(s, 302) + self.assertIn('Location', h) + code = get_query_credentials(h['Location'])['code'][0] self.validator.validate_code.side_effect = self.set_scopes(scopes) - _, _, body, _ = self.web.create_token_response(token_uri, + _, body, _ = self.web.create_token_response(token_uri, body='grant_type=authorization_code&code=%s' % code) self.assertEqual(json.loads(body)['scope'], decoded_scope) # implicit grant self.validator.validate_scopes.side_effect = self.set_scopes(scopes) - uri, _, _, _ = self.mobile.create_authorization_response( + h, _, s = self.mobile.create_authorization_response( auth_uri + 'token', scopes=scopes) - self.assertEqual(get_fragment_credentials(uri)['scope'][0], decoded_scope) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertEqual(get_fragment_credentials(h['Location'])['scope'][0], decoded_scope) # resource owner password credentials grant self.validator.validate_scopes.side_effect = self.set_scopes(scopes) body = 'grant_type=password&username=abc&password=secret&scope=%s' - _, _, body, _ = self.legacy.create_token_response(token_uri, + _, body, _ = self.legacy.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['scope'], decoded_scope) @@ -145,8 +154,9 @@ class TestScopeHandling(TestCase): self.validator.validate_scopes.side_effect = self.set_scopes(scopes) self.validator.authenticate_client.side_effect = self.set_user body = 'grant_type=client_credentials&scope=%s' - _, _, body, _ = self.backend.create_token_response(token_uri, + _, body, _ = self.backend.create_token_response(token_uri, body=body % scope) + self.assertEqual(json.loads(body)['scope'], decoded_scope) def test_invalid_scope(self): @@ -157,27 +167,31 @@ class TestScopeHandling(TestCase): self.validator.validate_scopes.return_value = False # authorization grant - uri, _, _, _ = self.web.create_authorization_response( + h, _, s = self.web.create_authorization_response( auth_uri + 'code', scopes=['invalid']) - error = get_query_credentials(uri)['error'][0] + self.assertEqual(s, 302) + self.assertIn('Location', h) + error = get_query_credentials(h['Location'])['error'][0] self.assertEqual(error, 'invalid_scope') # implicit grant - uri, _, _, _ = self.mobile.create_authorization_response( + h, _, s = self.mobile.create_authorization_response( auth_uri + 'token', scopes=['invalid']) - error = get_fragment_credentials(uri)['error'][0] + self.assertEqual(s, 302) + self.assertIn('Location', h) + error = get_fragment_credentials(h['Location'])['error'][0] self.assertEqual(error, 'invalid_scope') # resource owner password credentials grant body = 'grant_type=password&username=abc&password=secret&scope=%s' - _, _, body, _ = self.legacy.create_token_response(token_uri, + _, body, _ = self.legacy.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['error'], 'invalid_scope') # client credentials grant self.validator.authenticate_client.side_effect = self.set_user body = 'grant_type=client_credentials&scope=%s' - _, _, body, _ = self.backend.create_token_response(token_uri, + _, body, _ = self.backend.create_token_response(token_uri, body=body % scope) self.assertEqual(json.loads(body)['error'], 'invalid_scope') @@ -207,18 +221,22 @@ class PreservationTest(TestCase): token_uri = 'http://example.com/path' # authorization grant - uri, _, _, _ = self.web.create_authorization_response( + h, _, s = self.web.create_authorization_response( auth_uri + 'code', scopes=['random']) - code = get_query_credentials(uri)['code'][0] + self.assertEqual(s, 302) + self.assertIn('Location', h) + code = get_query_credentials(h['Location'])['code'][0] self.validator.validate_code.side_effect = self.set_state('xyz') - _, _, body, _ = self.web.create_token_response(token_uri, + _, body, _ = self.web.create_token_response(token_uri, body='grant_type=authorization_code&code=%s' % code) self.assertEqual(json.loads(body)['state'], 'xyz') # implicit grant - uri, _, _, _ = self.mobile.create_authorization_response( + h, _, s = self.mobile.create_authorization_response( auth_uri + 'token', scopes=['random']) - self.assertEqual(get_fragment_credentials(uri)['state'][0], 'xyz') + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertEqual(get_fragment_credentials(h['Location'])['state'][0], 'xyz') def test_redirect_uri_preservation(self): auth_uri = 'http://example.com/path?redirect_uri=http%3A%2F%2Fi.b%2Fpath&client_id=abc' @@ -226,22 +244,26 @@ class PreservationTest(TestCase): token_uri = 'http://example.com/path' # authorization grant - uri, _, _, _ = self.web.create_authorization_response( + h, _, s = self.web.create_authorization_response( auth_uri + '&response_type=code', scopes=['random']) - self.assertTrue(uri.startswith(redirect_uri)) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertTrue(h['Location'].startswith(redirect_uri)) # confirm_redirect_uri should return false if the redirect uri # was given in the authorization but not in the token request. self.validator.confirm_redirect_uri.return_value = False - code = get_query_credentials(uri)['code'][0] - _, _, body, _ = self.web.create_token_response(token_uri, + code = get_query_credentials(h['Location'])['code'][0] + _, body, _ = self.web.create_token_response(token_uri, body='grant_type=authorization_code&code=%s' % code) self.assertEqual(json.loads(body)['error'], 'access_denied') # implicit grant - uri, _, _, _ = self.mobile.create_authorization_response( + h, _, s = self.mobile.create_authorization_response( auth_uri + '&response_type=token', scopes=['random']) - self.assertTrue(uri.startswith(redirect_uri)) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertTrue(h['Location'].startswith(redirect_uri)) def test_invalid_redirect_uri(self): auth_uri = 'http://example.com/path?redirect_uri=http%3A%2F%2Fi.b%2Fpath&client_id=abc' @@ -313,13 +335,13 @@ class ClientAuthenticationTest(TestCase): # authorization code grant self.validator.authenticate_client.return_value = False self.validator.authenticate_client_id.return_value = False - _, _, body, _ = self.web.create_token_response(token_uri, + _, body, _ = self.web.create_token_response(token_uri, body='grant_type=authorization_code&code=mock') self.assertEqual(json.loads(body)['error'], 'invalid_client') self.validator.authenticate_client_id.return_value = True self.validator.authenticate_client.side_effect = self.set_client - _, _, body, _ = self.web.create_token_response(token_uri, + _, body, _ = self.web.create_token_response(token_uri, body='grant_type=authorization_code&code=mock') self.assertIn('access_token', json.loads(body)) @@ -329,8 +351,10 @@ class ClientAuthenticationTest(TestCase): auth_uri, scopes=['random']) self.validator.validate_client_id.side_effect = self.set_client_id - uri, _, _, _ = self.mobile.create_authorization_response(auth_uri, scopes=['random']) - self.assertIn('access_token', get_fragment_credentials(uri)) + h, _, s = self.mobile.create_authorization_response(auth_uri, scopes=['random']) + self.assertEqual(302, s) + self.assertIn('Location', h) + self.assertIn('access_token', get_fragment_credentials(h['Location'])) def test_custom_authentication(self): token_uri = 'http://example.com/path' @@ -397,16 +421,18 @@ class ResourceOwnerAssociationTest(TestCase): def test_web_application(self): # TODO: code generator + intercept test - uri, _, _, _ = self.web.create_authorization_response( + h, _, s = self.web.create_authorization_response( self.auth_uri + '&response_type=code', credentials={'user': 'test'}, scopes=['random']) - code = get_query_credentials(uri)['code'][0] + self.assertEqual(s, 302) + self.assertIn('Location', h) + code = get_query_credentials(h['Location'])['code'][0] self.assertRaises(ValueError, self.web.create_token_response, self.token_uri, body='grant_type=authorization_code&code=%s' % code) self.validator.validate_code.side_effect = self.set_user - _, _, body, _ = self.web.create_token_response(self.token_uri, + _, body, _ = self.web.create_token_response(self.token_uri, body='grant_type=authorization_code&code=%s' % code) self.assertEqual(json.loads(body)['access_token'], 'abc') @@ -415,10 +441,12 @@ class ResourceOwnerAssociationTest(TestCase): self.mobile.create_authorization_response, self.auth_uri + '&response_type=token') - uri, _, _, _ = self.mobile.create_authorization_response( + h, _, s = self.mobile.create_authorization_response( self.auth_uri + '&response_type=token', credentials={'user': 'test'}, scopes=['random']) - self.assertEqual(get_fragment_credentials(uri)['access_token'][0], 'abc') + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertEqual(get_fragment_credentials(h['Location'])['access_token'][0], 'abc') def test_legacy_application(self): body = 'grant_type=password&username=abc&password=secret' @@ -427,7 +455,7 @@ class ResourceOwnerAssociationTest(TestCase): self.token_uri, body=body) self.validator.validate_user.side_effect = self.set_user_from_username - _, _, body, _ = self.legacy.create_token_response( + _, body, _ = self.legacy.create_token_response( self.token_uri, body=body) self.assertEqual(json.loads(body)['access_token'], 'abc') @@ -438,7 +466,7 @@ class ResourceOwnerAssociationTest(TestCase): self.token_uri, body=body) self.validator.authenticate_client.side_effect = self.set_user_from_credentials - _, _, body, _ = self.backend.create_token_response( + _, body, _ = self.backend.create_token_response( self.token_uri, body=body) self.assertEqual(json.loads(body)['access_token'], 'abc') @@ -546,15 +574,17 @@ class ErrorResponseTest(TestCase): self.assertRaises(errors.InvalidRequestError, self.web.validate_authorization_request, uri.format('code')) - url, _, _, _ = self.web.create_authorization_response( + h, _, s = self.web.create_authorization_response( uri.format('code'), scopes=['foo']) - self.assertIn('error=invalid_request', url) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertIn('error=invalid_request', h['Location']) invalid_bodies = [ # duplicate params 'grant_type=authorization_code&client_id=nope&client_id=nope&code=foo' ] for body in invalid_bodies: - _, _, body, _ = self.web.create_token_response(token_uri, + _, body, _ = self.web.create_token_response(token_uri, body=body) self.assertEqual('invalid_request', json.loads(body)['error']) @@ -563,9 +593,11 @@ class ErrorResponseTest(TestCase): self.assertRaises(errors.InvalidRequestError, self.mobile.validate_authorization_request, uri.format('token')) - url, _, _, _ = self.mobile.create_authorization_response( + h, _, s = self.mobile.create_authorization_response( uri.format('token'), scopes=['foo']) - self.assertIn('error=invalid_request', url) + self.assertEqual(s, 302) + self.assertIn('Location', h) + self.assertIn('error=invalid_request', h['Location']) # Password credentials grant invalid_bodies = [ @@ -578,7 +610,7 @@ class ErrorResponseTest(TestCase): ] self.validator.authenticate_client.side_effect = self.set_client for body in invalid_bodies: - _, _, body, _ = self.legacy.create_token_response(token_uri, + _, body, _ = self.legacy.create_token_response(token_uri, body=body) self.assertEqual('invalid_request', json.loads(body)['error']) @@ -588,7 +620,7 @@ class ErrorResponseTest(TestCase): 'grant_type=client_credentials&scope=foo&scope=bar' ] for body in invalid_bodies: - _, _, body, _ = self.backend.create_token_response(token_uri, + _, body, _ = self.backend.create_token_response(token_uri, body=body) self.assertEqual('invalid_request', json.loads(body)['error']) @@ -603,7 +635,7 @@ class ErrorResponseTest(TestCase): self.assertRaises(errors.UnauthorizedClientError, self.web.validate_authorization_request, 'https://i.b/auth?response_type=code&client_id=foo') - _, _, body, _ = self.web.create_token_response(token_uri, + _, body, _ = self.web.create_token_response(token_uri, body='grant_type=authorization_code&code=foo') self.assertEqual('unauthorized_client', json.loads(body)['error']) @@ -613,12 +645,12 @@ class ErrorResponseTest(TestCase): 'https://i.b/auth?response_type=token&client_id=foo') # Password credentials grant - _, _, body, _ = self.legacy.create_token_response(token_uri, + _, body, _ = self.legacy.create_token_response(token_uri, body='grant_type=password&username=foo&password=bar') self.assertEqual('unauthorized_client', json.loads(body)['error']) # Client credentials grant - _, _, body, _ = self.backend.create_token_response(token_uri, + _, body, _ = self.backend.create_token_response(token_uri, body='grant_type=client_credentials') self.assertEqual('unauthorized_client', json.loads(body)['error']) @@ -627,7 +659,7 @@ class ErrorResponseTest(TestCase): self.validator.confirm_redirect_uri.return_value = False token_uri = 'https://i.b/token' # Authorization code grant - _, _, body, _ = self.web.create_token_response(token_uri, + _, body, _ = self.web.create_token_response(token_uri, body='grant_type=authorization_code&code=foo') self.assertEqual('access_denied', json.loads(body)['error']) @@ -660,13 +692,13 @@ class ErrorResponseTest(TestCase): 'https://i.b/auth?response_type=token&client_id=foo') # Password credentials grant - _, _, body, _ = self.legacy.create_token_response( + _, body, _ = self.legacy.create_token_response( 'https://i.b/token', body='grant_type=password&username=foo&password=bar') self.assertEqual('invalid_scope', json.loads(body)['error']) # Client credentials grant - _, _, body, _ = self.backend.create_token_response( + _, body, _ = self.backend.create_token_response( 'https://i.b/token', body='grant_type=client_credentials') self.assertEqual('invalid_scope', json.loads(body)['error']) @@ -681,11 +713,11 @@ class ErrorResponseTest(TestCase): # Authorization code grant self.web.catch_errors = True - _, _, _, s = self.web.create_authorization_response( + _, _, s = self.web.create_authorization_response( 'https://i.b/auth?client_id=foo&response_type=code', scopes=['foo']) self.assertEqual(s, 500) - _, _, _, s = self.web.create_token_response( + _, _, s = self.web.create_token_response( 'https://i.b/token', body='grant_type=authorization_code&code=foo', scopes=['foo']) @@ -693,21 +725,21 @@ class ErrorResponseTest(TestCase): # Implicit grant self.mobile.catch_errors = True - _, _, _, s = self.mobile.create_authorization_response( + _, _, s = self.mobile.create_authorization_response( 'https://i.b/auth?client_id=foo&response_type=token', scopes=['foo']) self.assertEqual(s, 500) # Password credentials grant self.legacy.catch_errors = True - _, _, _, s = self.legacy.create_token_response( + _, _, s = self.legacy.create_token_response( 'https://i.b/token', body='grant_type=password&username=foo&password=foo') self.assertEqual(s, 500) # Client credentials grant self.backend.catch_errors = True - _, _, _, s = self.backend.create_token_response( + _, _, s = self.backend.create_token_response( 'https://i.b/token', body='grant_type=client_credentials') self.assertEqual(s, 500) @@ -715,11 +747,11 @@ class ErrorResponseTest(TestCase): def test_temporarily_unavailable(self): # Authorization code grant self.web.available = False - _, _, _, s = self.web.create_authorization_response( + _, _, s = self.web.create_authorization_response( 'https://i.b/auth?client_id=foo&response_type=code', scopes=['foo']) self.assertEqual(s, 503) - _, _, _, s = self.web.create_token_response( + _, _, s = self.web.create_token_response( 'https://i.b/token', body='grant_type=authorization_code&code=foo', scopes=['foo']) @@ -727,21 +759,21 @@ class ErrorResponseTest(TestCase): # Implicit grant self.mobile.available = False - _, _, _, s = self.mobile.create_authorization_response( + _, _, s = self.mobile.create_authorization_response( 'https://i.b/auth?client_id=foo&response_type=token', scopes=['foo']) self.assertEqual(s, 503) # Password credentials grant self.legacy.available = False - _, _, _, s = self.legacy.create_token_response( + _, _, s = self.legacy.create_token_response( 'https://i.b/token', body='grant_type=password&username=foo&password=foo') self.assertEqual(s, 503) # Client credentials grant self.backend.available = False - _, _, _, s = self.backend.create_token_response( + _, _, s = self.backend.create_token_response( 'https://i.b/token', body='grant_type=client_credentials') self.assertEqual(s, 503) @@ -751,17 +783,17 @@ class ErrorResponseTest(TestCase): self.validator.authenticate_client_id.return_value = False # Authorization code grant - _, _, body, _ = self.web.create_token_response('https://i.b/token', + _, body, _ = self.web.create_token_response('https://i.b/token', body='grant_type=authorization_code&code=foo') self.assertEqual('invalid_client', json.loads(body)['error']) # Password credentials grant - _, _, body, _ = self.legacy.create_token_response('https://i.b/token', + _, body, _ = self.legacy.create_token_response('https://i.b/token', body='grant_type=password&username=foo&password=bar') self.assertEqual('invalid_client', json.loads(body)['error']) # Client credentials grant - _, _, body, _ = self.legacy.create_token_response('https://i.b/token', + _, body, _ = self.legacy.create_token_response('https://i.b/token', body='grant_type=client_credentials') self.assertEqual('invalid_client', json.loads(body)['error']) @@ -770,13 +802,13 @@ class ErrorResponseTest(TestCase): # Authorization code grant self.validator.validate_code.return_value = False - _, _, body, _ = self.web.create_token_response('https://i.b/token', + _, body, _ = self.web.create_token_response('https://i.b/token', body='grant_type=authorization_code&code=foo') self.assertEqual('invalid_grant', json.loads(body)['error']) # Password credentials grant self.validator.validate_user.return_value = False - _, _, body, _ = self.legacy.create_token_response('https://i.b/token', + _, body, _ = self.legacy.create_token_response('https://i.b/token', body='grant_type=password&username=foo&password=bar') self.assertEqual('invalid_grant', json.loads(body)['error']) @@ -784,17 +816,17 @@ class ErrorResponseTest(TestCase): self.validator.authenticate_client.side_effect = self.set_client # Authorization code grant - _, _, body, _ = self.web.create_token_response('https://i.b/token', + _, body, _ = self.web.create_token_response('https://i.b/token', body='grant_type=bar&code=foo') self.assertEqual('unsupported_grant_type', json.loads(body)['error']) # Password credentials grant - _, _, body, _ = self.legacy.create_token_response('https://i.b/token', + _, body, _ = self.legacy.create_token_response('https://i.b/token', body='grant_type=bar&username=foo&password=bar') self.assertEqual('unsupported_grant_type', json.loads(body)['error']) # Client credentials grant - _, _, body, _ = self.backend.create_token_response('https://i.b/token', + _, body, _ = self.backend.create_token_response('https://i.b/token', body='grant_type=bar') self.assertEqual('unsupported_grant_type', json.loads(body)['error']) |