summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.rst43
-rw-r--r--LICENSE2
-rw-r--r--docs/Makefile2
-rw-r--r--docs/conf.py17
-rw-r--r--docs/oauth2/oauth2provider-legend.dot32
-rw-r--r--docs/oauth2/oauth2provider-server.dot215
-rw-r--r--docs/oauth2/server.rst22
-rw-r--r--oauthlib/__init__.py4
8 files changed, 320 insertions, 17 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index fd53769..2cc0dd3 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,9 +1,36 @@
Changelog
=========
-Unreleased
+3.0.0 (2019-01-01)
------------------
+OAuth2.0 Provider - outstanding Features
+* OpenID Connect Core support
+* RFC7662 Introspect support
+* RFC8414 OAuth2.0 Authorization Server Metadata support (#605)
+* RFC7636 PKCE support (#617 #624)
+
+OAuth2.0 Provider - API/Breaking Changes
+
+* Add "request" to confirm_redirect_uri #504
+* confirm_redirect_uri/get_default_redirect_uri has a bit changed #445
+* invalid_client is now a FatalError #606
+* Changed errors status code from 401 to 400:
+ - invalid_grant: #264
+ - invalid_scope: #620
+ - access_denied/unauthorized_client/consent_required/login_required #623
+ - 401 must have WWW-Authenticate HTTP Header set. #623
+
+OAuth2.0 Provider - Bugfixes
+
+* empty scopes no longer raise exceptions for implicit and authorization_code #475 / #406
+
+OAuth2.0 Client - Bugfixes / Changes:
+
+* expires_in in Implicit flow is now an integer #569
+* expires is no longer overriding expires_in #506
+* parse_request_uri_response is now required #499
+* Unknown error=xxx raised by OAuth2 providers was not understood #431
* OAuth2's `prepare_token_request` supports sending an empty string for `client_id` (#585)
* OAuth2's `WebApplicationClient.prepare_request_body` was refactored to better
support sending or omitting the `client_id` via a new `include_client_id` kwarg.
@@ -11,6 +38,20 @@ Unreleased
a `client_id` parameter is submitted; the already configured `self.client_id`
is the preferred option. (#585)
+OAuth1.0 Client:
+
+* Support for HMAC-SHA256 #498
+
+General fixes:
+
+* $ and ' are allowed to be unencoded in query strings #564
+* Request attributes are no longer overriden by HTTP Headers #409
+* Removed unnecessary code for handling python2.6
+* Add support of python3.7 #621
+* Several minors updates to setup.py and tox
+* Set pytest as the default unittest framework
+
+
2.1.0 (2018-05-21)
------------------
diff --git a/LICENSE b/LICENSE
index 84b5c75..d5a9e9a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2018 The OAuthlib Community
+Copyright (c) 2019 The OAuthlib Community
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/docs/Makefile b/docs/Makefile
index 9ec7a6d..d134c96 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -2,7 +2,7 @@
#
# You can set these variables from the command line.
-SPHINXOPTS =
+SPHINXOPTS = -v
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
diff --git a/docs/conf.py b/docs/conf.py
index 2594e38..f1a489a 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -21,11 +21,16 @@ sys.path.insert(0, os.path.abspath('..'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+needs_sphinx = '1.1'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.viewcode']
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.viewcode',
+ 'sphinx.ext.graphviz'
+]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -41,7 +46,7 @@ master_doc = 'index'
# General information about the project.
project = u'OAuthLib'
-copyright = u'2018, The OAuthlib Community'
+copyright = u'2019, The OAuthlib Community'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -186,7 +191,7 @@ latex_elements = {
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'OAuthLib.tex', u'OAuthLib Documentation',
- u'Idan Gazit and the Python Community', 'manual'),
+ u'The OAuhthlib Community', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -216,7 +221,7 @@ latex_documents = [
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'oauthlib', u'OAuthLib Documentation',
- [u'Idan Gazit and the Python Community'], 1)
+ [u'The OAuthlib Community'], 1)
]
# If true, show URL addresses after external links.
@@ -230,7 +235,7 @@ man_pages = [
# dir menu entry, description, category)
texinfo_documents = [
('index', 'OAuthLib', u'OAuthLib Documentation',
- u'Idan Gazit and the Python Community', 'OAuthLib', 'One line description of project.',
+ u'The OAuthlib Community', 'OAuthLib', 'One line description of project.',
'Miscellaneous'),
]
diff --git a/docs/oauth2/oauth2provider-legend.dot b/docs/oauth2/oauth2provider-legend.dot
new file mode 100644
index 0000000..746ac2b
--- /dev/null
+++ b/docs/oauth2/oauth2provider-legend.dot
@@ -0,0 +1,32 @@
+digraph oauthlib_legend {
+
+ subgraph cluster_legend {
+ label="Legend";
+
+ /*
+ method [ shape=record; label="{{RequestValidator\nmethod name|arguments}|return values}" ];
+ endpoint [ shape=record; label="{Endpoint name|{function name|arguments}|grant type}" ];
+ webframework [ shape=hexagon; label="Upstream functions" ];
+ */
+
+ flow_code_token [shape=none,label="Authorization Code\nAccess Token Request"];
+ flow_code_auth [shape=none,label="Authorization Code\nAuthorization Request"];
+ flow_implicit [shape=none,label="Implicit Grant"];
+ flow_password [shape=none,label="Resource Owner Password\nCredentials Grant"];
+ flow_clicreds [shape=none,label="Client Credentials Grant"];
+ flow_refresh [shape=none,label="Refresh Grant"];
+ flow_introspect [shape=none,label="Token Introspection"];
+ flow_revoke [shape=none,label="Token Revoke"];
+ flow_resource [shape=none,label="Resource Access"];
+ flow_code_token -> a [style=bold,color=green];
+ flow_code_auth -> b [style=bold,color=darkgreen];
+ flow_implicit -> c [style=bold,color=orange];
+ flow_password -> d [style=bold,color=red];
+ flow_clicreds -> e [style=bold,color=blue];
+ flow_refresh -> f [style=bold,color=brown];
+ flow_introspect -> g [style=bold,color=yellow];
+ flow_revoke -> h [style=bold,color=purple];
+ flow_resource -> i [style=bold,color=pink];
+ a, b, c, d, e, f, g, h, i [shape=none,label=""];
+ }
+}
diff --git a/docs/oauth2/oauth2provider-server.dot b/docs/oauth2/oauth2provider-server.dot
new file mode 100644
index 0000000..bf7df75
--- /dev/null
+++ b/docs/oauth2/oauth2provider-server.dot
@@ -0,0 +1,215 @@
+digraph oauthlib {
+ center="1"
+ edge [ style=bold ];
+
+ /* Web Framework Entry and Exit points */
+ {
+ node [ shape=hexagon ];
+ edge [ style=normal ];
+
+ webapi_request [ label="WebFramework\nHTTP request" ];
+ webapi_request:s ->
+ endpoint_authorize:top:n,
+ endpoint_token:top:n,
+ endpoint_introspect:top:n,
+ endpoint_revoke:top:n,
+ endpoint_resource:top:n;
+ webapi_response [ label="WebFramework\nHTTP response" ];
+ }
+
+ /* OAuthlib Endpoints */
+ {
+ rank=same;
+
+ endpoint_authorize [ shape=record; label="{<top>Authorize Endpoint|{create_authorize_response|{uri|method|body|headers|credentials}}|{<token>token|<code>code}}" ];
+ endpoint_token [ shape=record; label="{<top>Token Endpoint|{create_token_response|{uri|method|body|headers|credentials}}|{<authorization_code>authorization_code|<password>password|<client_credentials>client_credentials|<refresh_token>refresh_token}}" ];
+ endpoint_revoke [ shape=record; label="{<top>Revocation Endpoint|{create_revocation_response|{uri|method|body|headers}}}" ];
+ endpoint_introspect [ shape=record; label="{<top>Introspect Endpoint|{create_introspect_response|{uri|method|body|headers}}}" ];
+ endpoint_resource [ shape=record; label="{<top>Resource Endpoint|{verify_request|{uri|method|body|headers|scopes_list}}}" ];
+ }
+
+ /* OAuthlib RequestValidator Methods */
+ {
+ node [ shape=record ];
+
+ f_client_authentication_required [ label="{{<top>client_authentication_required|request}|{<true>True|<false>False}}"; ];
+ f_authenticate_client [ label="{{<top>authenticate_client|request}|{<true>True|<false>False}}";];
+ f_authenticate_client_id [ label="{{<top>authenticate_client_id|{client_id|request}}|{<true>True|<false>False}}"; ];
+ f_validate_grant_type [ label="{{<top>validate_grant_type|{client_id|grant_type|client|request}}|{<true>True|<false>False}}"; ];
+ f_validate_code [ label="{{<top>validate_code|{client_id|code|request}}|{<true>True|<false>False}}"; ];
+ f_confirm_redirect_uri [ label="{{<top>confirm_redirect_uri|{client_id|code|redirect_uri|client|request}}|{<true>True|<false>False}}"; ];
+ f_get_default_redirect_uri [ label="{{<top>get_default_redirect_uri|{client_id|request}}|{<redirect_uri>redirect_uri|<none>None}}"; ];
+ f_invalidate_authorization_code [ label="{{<top>invalidate_authorization_code|{client_id|code|request}}|None}"; ];
+ f_validate_scopes [ label="{{<top>validate_scopes|{client_id|scopes|client|request}}|{<true>True|<false>False}}"; ];
+ f_save_bearer_token [ label="{{<top>save_bearer_token|{token|request}}|None}"; ];
+ f_revoke_token [ label="{{<top>revoke_token|{token|token_type_hint|request}}|None}"; ];
+ f_validate_client_id [ label="{{<top>validate_client_id|{client_id|request}}|{<true>True|<false>False}}"; ];
+ f_validate_redirect_uri [ label="{{<top>validate_redirect_uri|{client_id|redirect_uri|request}}|{<true>True|<false>False}}"; ];
+ f_is_pkce_required [ label="{{<top>is_pkce_required|{client_id|request}}|{<true>True|<false>False}}"; ];
+ f_validate_response_type [ label="{{<top>validate_response_type|{client_id|response_type|client|request}}|{<true>True|<false>False}}"; ];
+ f_save_authorization_code [ label="{{<top>save_authorization_code|{client_id|code|request}}|None}"; ];
+ f_validate_bearer_token [ label="{{<top>validate_bearer_token|{token|scopes|request}}|{<true>True|<false>False}}"; ];
+ f_validate_refresh_token [ label="{{<top>validate_refresh_token|{refresh_token|client|request}}|{<true>True|<false>False}}"; ];
+ f_get_default_scopes [ label="{{<top>get_default_scopes|{client_id|request}}|{<scopes>[scopes]}}"; ];
+ f_get_original_scopes [ label="{{<top>get_original_scopes|{refresh_token|request}}|{<scopes>[scopes]}}"; ];
+ f_is_within_original_scope [ label="{{<top>is_within_original_scope|{refresh_scopes|refresh_token|request}}|{<true>True|<false>False}}"; ];
+ f_validate_user [ label="{{<top>validate_user|{username|password|client|request}}|{<true>True|<false>False}}"; ];
+ f_introspect_token [ label="{{<top>introspect_token|{token|token_type_hint|request}}|{<claims>\{claims\}|<none>None}}"; ];
+ }
+
+ /* OAuthlib Conditions */
+
+ if_code_challenge [ label="if code_challenge"; ];
+ if_redirect_uri [ label="if redirect_uri"; ];
+ if_redirect_uri_present [ shape=none;label="present"; ];
+ if_redirect_uri_missing [ shape=none;label="missing"; ];
+ if_scopes [ label="if scopes"; ];
+ if_all [ label="all(request_scopes not in scopes)"; ];
+
+ /* OAuthlib errors */
+ e_normal [ shape=none,label="ERROR" ];
+
+ /* Authorization Code - Access Token Request */
+ {
+ edge [ color=green ];
+
+ endpoint_token:authorization_code:s -> f_client_authentication_required;
+ f_client_authentication_required:true:s -> f_authenticate_client;
+ f_client_authentication_required:false -> f_authenticate_client_id;
+ f_authenticate_client:true:s -> f_validate_grant_type;
+ f_authenticate_client_id:true:s -> f_validate_grant_type;
+ f_validate_grant_type:true:s -> f_validate_code;
+
+ f_validate_code:true:s -> if_redirect_uri;
+ if_redirect_uri -> if_redirect_uri_present [ arrowhead=none ];
+ if_redirect_uri -> if_redirect_uri_missing [ arrowhead=none ];
+ if_redirect_uri_present -> f_confirm_redirect_uri;
+ if_redirect_uri_missing -> f_get_default_redirect_uri;
+
+ f_confirm_redirect_uri:true:s -> f_save_bearer_token;
+ f_get_default_redirect_uri -> f_save_bearer_token;
+
+ f_save_bearer_token -> f_invalidate_authorization_code;
+ f_invalidate_authorization_code -> webapi_response;
+ }
+ /* Authorization Code - Authorization Request */
+ {
+ edge [ color=darkgreen ];
+
+ endpoint_authorize:code:s -> f_validate_client_id;
+ f_validate_client_id:true:s -> if_redirect_uri;
+ if_redirect_uri -> if_redirect_uri_present [ arrowhead=none ];
+ if_redirect_uri -> if_redirect_uri_missing [ arrowhead=none ];
+ if_redirect_uri_present -> f_validate_redirect_uri;
+ if_redirect_uri_missing -> f_get_default_redirect_uri;
+
+ f_validate_redirect_uri:true:s -> f_validate_response_type;
+ f_get_default_redirect_uri -> f_validate_response_type;
+ f_validate_response_type:true:s -> f_is_pkce_required;
+ f_is_pkce_required:true:s -> if_code_challenge;
+ f_is_pkce_required:false -> f_validate_scopes;
+
+ if_code_challenge -> f_validate_scopes [ label="present" ];
+ if_code_challenge -> e_normal [ label="missing" ];
+
+ f_validate_scopes:true:s -> f_save_authorization_code;
+ }
+
+ /* Implicit */
+ {
+ edge [ color=orange ];
+
+ endpoint_authorize:token:s -> f_validate_client_id;
+ f_validate_client_id:true:s -> if_redirect_uri;
+ if_redirect_uri -> if_redirect_uri_present [ arrowhead=none ];
+ if_redirect_uri -> if_redirect_uri_missing [ arrowhead=none ];
+ if_redirect_uri_present -> f_validate_redirect_uri;
+ if_redirect_uri_missing -> f_get_default_redirect_uri;
+
+ f_validate_redirect_uri:true:s -> f_validate_response_type;
+ f_get_default_redirect_uri -> f_validate_response_type;
+ f_validate_response_type:true:s -> f_validate_scopes;
+ f_validate_scopes:true:s -> f_save_bearer_token;
+ }
+
+ /* Resource Owner Password Grant */
+ {
+ edge [ color=red ];
+
+ endpoint_token:password:s -> f_client_authentication_required;
+ f_client_authentication_required:true:s -> f_authenticate_client;
+ f_client_authentication_required:false -> f_authenticate_client_id;
+ f_authenticate_client:true:s -> f_validate_user;
+ f_authenticate_client_id:true:s -> f_validate_user;
+ f_validate_user:true:s -> f_validate_grant_type;
+
+ f_validate_grant_type:true:s -> if_scopes;
+ if_scopes -> f_validate_scopes [ label="present" ];
+ if_scopes -> f_get_default_scopes [ label="missing" ];
+
+ f_validate_scopes:true:s -> f_save_bearer_token;
+ f_get_default_scopes -> f_save_bearer_token;
+ f_save_bearer_token -> webapi_response;
+ }
+
+ /* Client Credentials Grant */
+ {
+ edge [ color=blue ];
+
+ endpoint_token:client_credentials:s -> f_authenticate_client;
+ f_authenticate_client -> f_validate_grant_type;
+ f_validate_grant_type:true:s -> f_validate_scopes;
+ f_validate_scopes:true:s -> f_save_bearer_token;
+ f_save_bearer_token -> webapi_response;
+ }
+
+ /* Refresh Grant */
+ {
+ edge [ color=brown ];
+
+ endpoint_token:refresh_token:s -> f_client_authentication_required;
+ f_client_authentication_required:true:s -> f_authenticate_client;
+ f_client_authentication_required:false -> f_authenticate_client_id;
+ f_authenticate_client:true:s -> f_validate_grant_type;
+ f_authenticate_client_id:true:s -> f_validate_grant_type;
+ f_validate_grant_type:true:s -> f_validate_refresh_token;
+ f_validate_refresh_token:true:s -> f_get_original_scopes;
+ f_get_original_scopes -> if_all;
+ if_all -> f_is_within_original_scope [ label="True" ];
+ if_all -> f_save_bearer_token [ label="False" ];
+ f_is_within_original_scope:true:s -> f_save_bearer_token;
+ f_save_bearer_token -> webapi_response;
+ }
+
+ /* Introspect Endpoint */
+ {
+ edge [ color=yellow ];
+
+ endpoint_introspect:s -> f_client_authentication_required [ label="" ];
+ f_client_authentication_required:true:s -> f_authenticate_client;
+ f_client_authentication_required:false -> f_authenticate_client_id;
+ f_authenticate_client:true:s -> f_introspect_token;
+ f_authenticate_client_id:true:s -> f_introspect_token;
+ f_introspect_token:claims -> webapi_response;
+ }
+
+ /* Revocation Endpoint */
+ {
+ edge [ color=purple ];
+
+ endpoint_revoke:s -> f_client_authentication_required;
+ f_client_authentication_required:true:s -> f_authenticate_client;
+ f_client_authentication_required:false -> f_authenticate_client_id;
+ f_authenticate_client:true:s -> f_revoke_token;
+ f_authenticate_client_id:true:s -> f_revoke_token;
+ f_revoke_token:s -> webapi_response;
+ }
+
+ /* Resource Access - Verify Request */
+ {
+ edge [ color=pink ];
+
+ endpoint_resource:s -> f_validate_bearer_token;
+ f_validate_bearer_token:true -> webapi_response;
+ }
+}
diff --git a/docs/oauth2/server.rst b/docs/oauth2/server.rst
index 6c065c5..dad0aae 100644
--- a/docs/oauth2/server.rst
+++ b/docs/oauth2/server.rst
@@ -25,7 +25,17 @@ as well as provide an interface for a backend to store tokens, clients, etc.
.. contents:: Tutorial Contents
:depth: 3
-1. Create your datastore models
+1. OAuth2.0 Provider flows
+-------------------------------
+
+OAuthLib interface between web framework and provider implementation are not always easy to follow, it's why a graph below has been done to better understand the implication of OAuthLib in the request's lifecycle.
+
+
+.. graphviz:: oauth2provider-legend.dot
+.. graphviz:: oauth2provider-server.dot
+
+
+2. Create your datastore models
-------------------------------
These models will represent various OAuth specific concepts. There are a few
@@ -257,7 +267,7 @@ the token.
challenge_method = django.db.models.CharField(max_length=6)
-2. Implement a validator
+3. Implement a validator
------------------------
The majority of the work involved in implementing an OAuth 2 provider
@@ -301,7 +311,7 @@ Relevant sections include:
security
-3. Create your composite endpoint
+4. Create your composite endpoint
---------------------------------
Each of the endpoints can function independently from each other, however
@@ -326,7 +336,7 @@ Relevant sections include:
preconfigured_servers
-4. Create your endpoint views
+5. Create your endpoint views
-----------------------------
We are implementing support for the Authorization Code Grant and will
@@ -430,7 +440,7 @@ The example using Django but should be transferable to any framework.
return HttpResponseBadRequest('Evil client is unable to send a proper request. Error is: ' + e.description)
-5. Protect your APIs using scopes
+6. Protect your APIs using scopes
---------------------------------
Let's define a decorator we can use to protect the views.
@@ -501,7 +511,7 @@ at runtime by a function, rather then by a list.
# A view that has its views functionally set.
return HttpResponse('pictures of cats')
-6. Let us know how it went!
+7. Let us know how it went!
---------------------------
Drop a line in our `Gitter OAuthLib community`_ or open a `GitHub issue`_ =)
diff --git a/oauthlib/__init__.py b/oauthlib/__init__.py
index 5b1b380..b37d288 100644
--- a/oauthlib/__init__.py
+++ b/oauthlib/__init__.py
@@ -5,13 +5,13 @@
A generic, spec-compliant, thorough implementation of the OAuth
request-signing logic.
- :copyright: (c) 2018 by The OAuthlib Community
+ :copyright: (c) 2019 by The OAuthlib Community
:license: BSD, see LICENSE for details.
"""
import logging
from logging import NullHandler
__author__ = 'The OAuthlib Community'
-__version__ = '3.0.0-dev'
+__version__ = '3.0.0'
logging.getLogger('oauthlib').addHandler(NullHandler())