From 3a769e29c2a94bad3460ab09f748569432257396 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Mon, 30 Jul 2018 15:07:05 +0200 Subject: Add syntax check of get_default_redirect_uri Authorization Code was missing this check, whereas Implicit was checking it. --- .../oauth2/rfc6749/grant_types/authorization_code.py | 2 ++ tests/oauth2/rfc6749/endpoints/test_error_responses.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py index 0660263..3d08871 100644 --- a/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py +++ b/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py @@ -312,6 +312,8 @@ class AuthorizationCodeGrant(GrantTypeBase): log.debug('Using default redirect_uri %s.', request.redirect_uri) if not request.redirect_uri: raise errors.MissingRedirectURIError(request=request) + if not is_absolute_uri(request.redirect_uri): + raise errors.InvalidRedirectURIError(request=request) # Then check for normal errors. diff --git a/tests/oauth2/rfc6749/endpoints/test_error_responses.py b/tests/oauth2/rfc6749/endpoints/test_error_responses.py index 875b3a5..de0d834 100644 --- a/tests/oauth2/rfc6749/endpoints/test_error_responses.py +++ b/tests/oauth2/rfc6749/endpoints/test_error_responses.py @@ -44,6 +44,22 @@ class ErrorResponseTest(TestCase): self.assertRaises(errors.InvalidRedirectURIError, self.mobile.create_authorization_response, uri.format('token'), scopes=['foo']) + def test_invalid_default_redirect_uri(self): + uri = 'https://example.com/authorize?response_type={0}&client_id=foo' + self.validator.get_default_redirect_uri.return_value = "wrong" + + # Authorization code grant + self.assertRaises(errors.InvalidRedirectURIError, + self.web.validate_authorization_request, uri.format('code')) + self.assertRaises(errors.InvalidRedirectURIError, + self.web.create_authorization_response, uri.format('code'), scopes=['foo']) + + # Implicit grant + self.assertRaises(errors.InvalidRedirectURIError, + self.mobile.validate_authorization_request, uri.format('token')) + self.assertRaises(errors.InvalidRedirectURIError, + self.mobile.create_authorization_response, uri.format('token'), scopes=['foo']) + def test_missing_redirect_uri(self): uri = 'https://example.com/authorize?response_type={0}&client_id=foo' -- cgit v1.2.1 From 38467a8a001fdbb5ae5661acfcea4e806b82b2b5 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Mon, 6 Aug 2018 19:01:53 +0200 Subject: Implicit was not converting expires_in into integers --- oauthlib/oauth2/rfc6749/parameters.py | 4 ++++ tests/oauth2/rfc6749/clients/test_mobile_application.py | 2 +- tests/oauth2/rfc6749/test_parameters.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/oauthlib/oauth2/rfc6749/parameters.py b/oauthlib/oauth2/rfc6749/parameters.py index 9ea8c44..c5127e7 100644 --- a/oauthlib/oauth2/rfc6749/parameters.py +++ b/oauthlib/oauth2/rfc6749/parameters.py @@ -279,6 +279,10 @@ def parse_implicit_response(uri, state=None, scope=None): fragment = urlparse.urlparse(uri).fragment params = dict(urlparse.parse_qsl(fragment, keep_blank_values=True)) + for key in ('expires_in',): + if key in params: # cast things to int + params[key] = int(params[key]) + if 'scope' in params: params['scope'] = scope_to_list(params['scope']) diff --git a/tests/oauth2/rfc6749/clients/test_mobile_application.py b/tests/oauth2/rfc6749/clients/test_mobile_application.py index 51e4dab..622b275 100644 --- a/tests/oauth2/rfc6749/clients/test_mobile_application.py +++ b/tests/oauth2/rfc6749/clients/test_mobile_application.py @@ -40,7 +40,7 @@ class MobileApplicationClientTest(TestCase): token = { "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "example", - "expires_in": "3600", + "expires_in": 3600, "expires_at": 4600, "scope": scope, "example_parameter": "example_value" diff --git a/tests/oauth2/rfc6749/test_parameters.py b/tests/oauth2/rfc6749/test_parameters.py index 6ba98c0..b211d1e 100644 --- a/tests/oauth2/rfc6749/test_parameters.py +++ b/tests/oauth2/rfc6749/test_parameters.py @@ -86,7 +86,7 @@ class ParameterTests(TestCase): 'access_token': '2YotnFZFEjr1zCsicMWpAA', 'state': state, 'token_type': 'example', - 'expires_in': '3600', + 'expires_in': 3600, 'expires_at': 4600, 'scope': ['abc'] } -- cgit v1.2.1 From d9b3f24b497698eddcbe4a0cc1a009d5aa21e9c7 Mon Sep 17 00:00:00 2001 From: Jonathan Huot Date: Fri, 10 Aug 2018 12:24:57 +0200 Subject: Added access_token as JWT examples, and updated JWT grant section A confusion between JWT as token and as authentication mechanism was introduced long-time back and I tried to make a bit of clarity to not confuse again the newcomers. --- docs/oauth2/grants/jwt.rst | 17 +++-- docs/oauth2/preconfigured_servers.rst | 6 +- docs/oauth2/tokens/bearer.rst | 116 +++++++++++++++++++++++++++++++++- docs/oauth2/tokens/tokens.rst | 3 +- 4 files changed, 131 insertions(+), 11 deletions(-) diff --git a/docs/oauth2/grants/jwt.rst b/docs/oauth2/grants/jwt.rst index db65342..2c1c0e2 100644 --- a/docs/oauth2/grants/jwt.rst +++ b/docs/oauth2/grants/jwt.rst @@ -1,7 +1,14 @@ -========== -JWT Tokens -========== +============================================================== +JWT Profile for Client Authentication and Authorization Grants +============================================================== -Not yet implemented. Track progress in `GitHub issue 50`_. +If you're looking at JWT Tokens, please see :doc:`Bearer Tokens ` instead. -.. _`GitHub issue 50`: https://github.com/oauthlib/oauthlib/issues/50 +The JWT Profile `RFC7523`_ implements the `RFC7521`_ abstract assertion +protocol. It aims to extend the OAuth2 protocol to use JWT as an +additional authorization grant. + +Currently, this is not implemented but all PRs are welcome. See how to :doc:`Contribute `. + +.. _`RFC7521`: https://tools.ietf.org/html/rfc7521 +.. _`RFC7523`: https://tools.ietf.org/html/rfc7523 diff --git a/docs/oauth2/preconfigured_servers.rst b/docs/oauth2/preconfigured_servers.rst index 6184c27..e1f629c 100644 --- a/docs/oauth2/preconfigured_servers.rst +++ b/docs/oauth2/preconfigured_servers.rst @@ -12,7 +12,8 @@ Construction is simple, only import your validator and you are good to go:: server = WebApplicationServer(your_validator) -If you prefer to construct tokens yourself you may pass a token generator:: +If you prefer to construct tokens yourself you may pass a token generator (see + :doc:`Tokens ` for more examples like JWT) :: def your_token_generator(request, refresh_token=False): return 'a_custom_token' + request.client_id @@ -21,6 +22,9 @@ If you prefer to construct tokens yourself you may pass a token generator:: This function is passed the request object and a boolean indicating whether to generate an access token (False) or a refresh token (True). +.. autoclass:: oauthlib.oauth2.Server + :members: + .. autoclass:: oauthlib.oauth2.WebApplicationServer :members: diff --git a/docs/oauth2/tokens/bearer.rst b/docs/oauth2/tokens/bearer.rst index 8c6270d..0776db8 100644 --- a/docs/oauth2/tokens/bearer.rst +++ b/docs/oauth2/tokens/bearer.rst @@ -2,12 +2,122 @@ Bearer Tokens ============= -The most common OAuth 2 token type. It provides very little in terms of security -and relies heavily upon the ability of the client to keep the token secret. +The most common OAuth 2 token type. -Bearer tokens are the default setting with all configured endpoints. Generally +Bearer tokens is the default setting for all configured endpoints. Generally you will not need to ever construct a token yourself as the provided servers will do so for you. +By default, :doc:`*Server ` generate Bearer tokens as +random strings. However, you can change the default behavior to generate JWT +instead. All preconfigured servers take as parameters `token_generator` and +`refresh_token_generator` to fit your needs. + +.. contents:: Tutorial Contents + :depth: 3 + + +1. Generate signed JWT +---------------------- + +A function is available to generate signed JWT (with RS256 PEM key) with static +and dynamic claims. + +.. code-block:: python + + from oauthlib.oauth2.rfc6749 import tokens + from oauthlib.oauth2 import Server + + private_pem_key = + validator = + + server = Server( + your_validator, + token_generator=tokens.signed_token_generator(private_pem_key, issuer="foobar") + ) + + +Note that you can add any custom claims in `RequestValidator` methods by adding them to +`request.claims` dictionary. Example below: + + +.. code-block:: python + + def validate_client_id(self, client_id, request): + (.. your usual checks ..) + + request.claims = { + 'aud': self.client_id + } + return True + + +Once completed, the token endpoint will generate access_token in JWT form: + +.. code-block:: shell + + + access_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJy(..)&expires_in=120&token_type=Bearer(..) + + +And you will find all claims in its decoded form: + + +.. code-block:: javascript + + { + "aud": "", + "iss": "foobar", + "scope": "profile calendar", + "exp": 12345, + } + + +2. Define your own implementation (text, JWT, JWE, ...) +---------------------------------------------------------------- + +Sometime you may want to generate custom `access_token` with a reference from a +database (as text) or use a HASH signature in JWT or use JWE (encrypted content). + +Also, note that you can declare the generate function in your instanciated +validator to benefit of the `self` variables. + +See the example below: + +.. code-block:: python + + class YourValidator(RequestValidator): + def __init__(self, secret, issuer): + self.secret = secret + self.issuer = issuer + + def generate_access_token(self, request): + token = jwt.encode({ + "ref": str(libuuid.uuid4()), + "aud": request.client_id, + "iss": self.issuer, + "exp": now + datetime.timedelta(seconds=request.expires_in) + }, self.secret, algorithm='HS256').decode() + return token + + +Then associate it to your `Server`: + +.. code-block:: python + + validator = YourValidator(secret="", issuer="") + + server = Server( + your_validator, + token_generator=validator.generate_access_token + ) + + +3. BearerToken API +------------------ + +If none of the :doc:`/oauth2/preconfigured_servers` fit your needs, you can +declare your own Endpoints and use the `BearerToken` API as below. + .. autoclass:: oauthlib.oauth2.BearerToken :members: diff --git a/docs/oauth2/tokens/tokens.rst b/docs/oauth2/tokens/tokens.rst index f341509..4e19e7e 100644 --- a/docs/oauth2/tokens/tokens.rst +++ b/docs/oauth2/tokens/tokens.rst @@ -3,8 +3,7 @@ Tokens ====== The main token type of OAuth 2 is Bearer tokens and that is what OAuthLib -currently supports. Other tokens, such as JWT, SAML and possibly MAC (if the -spec matures) can easily be added (and will be in due time). +currently supports. Other tokens, such as SAML and MAC can easily be added. The purpose of a token is to authorize access to protected resources to a client (i.e. your G+ feed). -- cgit v1.2.1