summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Huot <JonathanHuot@users.noreply.github.com>2018-08-13 00:53:28 +0200
committerGitHub <noreply@github.com>2018-08-13 00:53:28 +0200
commitf93dca00208fecdb9b4791a33c59b27c4199d0f4 (patch)
treeca97531d7ebb7e0ad61e0e8ab0f0fccd8e214bd1
parent37dad8391c7b9f00c93fba134e6be845e0adf2f1 (diff)
parent21e463712e5de5f2f4866e01d65f8edb8e86994c (diff)
downloadoauthlib-f93dca00208fecdb9b4791a33c59b27c4199d0f4.tar.gz
Merge branch 'master' into invalid-grant-should-respond-with-400
-rw-r--r--docs/oauth2/grants/jwt.rst17
-rw-r--r--docs/oauth2/preconfigured_servers.rst6
-rw-r--r--docs/oauth2/tokens/bearer.rst116
-rw-r--r--docs/oauth2/tokens/tokens.rst3
-rw-r--r--oauthlib/oauth2/rfc6749/grant_types/authorization_code.py2
-rw-r--r--oauthlib/oauth2/rfc6749/parameters.py4
-rw-r--r--tests/oauth2/rfc6749/clients/test_mobile_application.py2
-rw-r--r--tests/oauth2/rfc6749/endpoints/test_error_responses.py16
-rw-r--r--tests/oauth2/rfc6749/test_parameters.py2
9 files changed, 155 insertions, 13 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 </oauth2/tokens/bearer>` 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 </contributing>`.
+
+.. _`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 <tokens/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 </oauth2/preconfigured_servers>` 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 = <load_your_key_in_pem_format>
+ validator = <instantiate_your_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": "<client_id>",
+ "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="<your_secret>", issuer="<your_issuer_id>")
+
+ 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).
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/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/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'
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']
}