mirror of
https://github.com/rembo10/headphones.git
synced 2026-07-20 07:54:01 +01:00
Adding Requests library
This commit is contained in:
@@ -0,0 +1,497 @@
|
||||
"""
|
||||
oauthlib.oauth2.draft_25
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module is an implementation of various logic needed
|
||||
for signing and checking OAuth 2.0 draft 25 requests.
|
||||
"""
|
||||
from tokens import prepare_bearer_uri, prepare_bearer_headers
|
||||
from tokens import prepare_bearer_body, prepare_mac_header
|
||||
from parameters import prepare_grant_uri, prepare_token_request
|
||||
from parameters import parse_authorization_code_response
|
||||
from parameters import parse_implicit_response, parse_token_response
|
||||
|
||||
|
||||
AUTH_HEADER = u'auth_header'
|
||||
URI_QUERY = u'query'
|
||||
BODY = u'body'
|
||||
|
||||
|
||||
class Client(object):
|
||||
|
||||
def __init__(self, client_id,
|
||||
default_redirect_uri=None,
|
||||
token_type=None,
|
||||
access_token=None,
|
||||
refresh_token=None):
|
||||
"""Initialize a client with commonly used attributes."""
|
||||
|
||||
self.client_id = client_id
|
||||
self.default_redirect_uri = default_redirect_uri
|
||||
self.token_type = token_type
|
||||
self.access_token = access_token
|
||||
self.refresh_token = refresh_token
|
||||
self.token_types = {
|
||||
u'bearer': self._add_bearer_token,
|
||||
u'mac': self._add_mac_token
|
||||
}
|
||||
|
||||
def add_token(self, uri, http_method=u'GET', body=None, headers=None,
|
||||
token_placement=AUTH_HEADER):
|
||||
"""Add token to the request uri, body or authorization header.
|
||||
|
||||
The access token type provides the client with the information
|
||||
required to successfully utilize the access token to make a protected
|
||||
resource request (along with type-specific attributes). The client
|
||||
MUST NOT use an access token if it does not understand the token
|
||||
type.
|
||||
|
||||
For example, the "bearer" token type defined in
|
||||
[I-D.ietf-oauth-v2-bearer] is utilized by simply including the access
|
||||
token string in the request:
|
||||
|
||||
GET /resource/1 HTTP/1.1
|
||||
Host: example.com
|
||||
Authorization: Bearer mF_9.B5f-4.1JqM
|
||||
|
||||
while the "mac" token type defined in [I-D.ietf-oauth-v2-http-mac] is
|
||||
utilized by issuing a MAC key together with the access token which is
|
||||
used to sign certain components of the HTTP requests:
|
||||
|
||||
GET /resource/1 HTTP/1.1
|
||||
Host: example.com
|
||||
Authorization: MAC id="h480djs93hd8",
|
||||
nonce="274312:dj83hs9s",
|
||||
mac="kDZvddkndxvhGRXZhvuDjEWhGeE="
|
||||
|
||||
.. _`I-D.ietf-oauth-v2-bearer`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#ref-I-D.ietf-oauth-v2-bearer
|
||||
.. _`I-D.ietf-oauth-v2-http-mac`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#ref-I-D.ietf-oauth-v2-http-mac
|
||||
"""
|
||||
return self.token_types[self.token_type](uri, http_method, body,
|
||||
headers, token_placement)
|
||||
|
||||
def prepare_refresh_body(self, body=u'', refresh_token=None, scope=None):
|
||||
"""Prepare an access token request, using a refresh token.
|
||||
|
||||
If the authorization server issued a refresh token to the client, the
|
||||
client makes a refresh request to the token endpoint by adding the
|
||||
following parameters using the "application/x-www-form-urlencoded"
|
||||
format in the HTTP request entity-body:
|
||||
|
||||
grant_type
|
||||
REQUIRED. Value MUST be set to "refresh_token".
|
||||
refresh_token
|
||||
REQUIRED. The refresh token issued to the client.
|
||||
scope
|
||||
OPTIONAL. The scope of the access request as described by
|
||||
Section 3.3. The requested scope MUST NOT include any scope
|
||||
not originally granted by the resource owner, and if omitted is
|
||||
treated as equal to the scope originally granted by the
|
||||
resource owner.
|
||||
"""
|
||||
refresh_token = refresh_token or self.refresh_token
|
||||
return prepare_token_request(u'refresh_token', body=body, scope=scope,
|
||||
refresh_token=refresh_token)
|
||||
|
||||
def _add_bearer_token(self, uri, http_method=u'GET', body=None,
|
||||
headers=None, token_placement=AUTH_HEADER):
|
||||
"""Add a bearer token to the request uri, body or authorization header."""
|
||||
if token_placement == AUTH_HEADER:
|
||||
headers = prepare_bearer_headers(self.token, headers)
|
||||
|
||||
if token_placement == URI_QUERY:
|
||||
uri = prepare_bearer_uri(self.token, uri)
|
||||
|
||||
if token_placement == BODY:
|
||||
body = prepare_bearer_body(self.token, body)
|
||||
|
||||
return uri, headers, body
|
||||
|
||||
def _add_mac_token(self, uri, http_method=u'GET', body=None,
|
||||
headers=None, token_placement=AUTH_HEADER):
|
||||
"""Add a MAC token to the request authorization header."""
|
||||
headers = prepare_mac_header(self.token, uri, self.key, http_method,
|
||||
headers=headers, body=body, ext=self.ext,
|
||||
hash_algorithm=self.hash_algorithm)
|
||||
return uri, headers, body
|
||||
|
||||
def _populate_attributes(self, response):
|
||||
"""Add commonly used values such as access_token to self."""
|
||||
|
||||
if u'access_token' in response:
|
||||
self.access_token = response.get(u'access_token')
|
||||
|
||||
if u'refresh_token' in response:
|
||||
self.refresh_token = response.get(u'refresh_token')
|
||||
|
||||
if u'token_type' in response:
|
||||
self.token_type = response.get(u'token_type')
|
||||
|
||||
if u'expires_in' in response:
|
||||
self.expires_in = response.get(u'expires_in')
|
||||
|
||||
if u'code' in response:
|
||||
self.code = response.get(u'code')
|
||||
|
||||
def prepare_request_uri(self, *args, **kwargs):
|
||||
"""Abstract method used to create request URIs."""
|
||||
raise NotImplementedError("Must be implemented by inheriting classes.")
|
||||
|
||||
def prepare_request_body(self, *args, **kwargs):
|
||||
"""Abstract method used to create request bodies."""
|
||||
raise NotImplementedError("Must be implemented by inheriting classes.")
|
||||
|
||||
def parse_request_uri_response(self, *args, **kwargs):
|
||||
"""Abstract method used to parse redirection responses."""
|
||||
|
||||
def parse_request_body_response(self, *args, **kwargs):
|
||||
"""Abstract method used to parse JSON responses."""
|
||||
|
||||
|
||||
class WebApplicationClient(Client):
|
||||
"""A client utilizing the authorization code grant workflow.
|
||||
|
||||
A web application is a confidential client running on a web
|
||||
server. Resource owners access the client via an HTML user
|
||||
interface rendered in a user-agent on the device used by the
|
||||
resource owner. The client credentials as well as any access
|
||||
token issued to the client are stored on the web server and are
|
||||
not exposed to or accessible by the resource owner.
|
||||
|
||||
The authorization code grant type is used to obtain both access
|
||||
tokens and refresh tokens and is optimized for confidential clients.
|
||||
As a redirection-based flow, the client must be capable of
|
||||
interacting with the resource owner's user-agent (typically a web
|
||||
browser) and capable of receiving incoming requests (via redirection)
|
||||
from the authorization server.
|
||||
"""
|
||||
|
||||
def prepare_request_uri(self, uri, redirect_uri=None, scope=None,
|
||||
state=None, **kwargs):
|
||||
"""Prepare the authorization code request URI
|
||||
|
||||
The client constructs the request URI by adding the following
|
||||
parameters to the query component of the authorization endpoint URI
|
||||
using the "application/x-www-form-urlencoded" format as defined by
|
||||
[`W3C.REC-html401-19991224`_]:
|
||||
|
||||
response_type
|
||||
REQUIRED. Value MUST be set to "code".
|
||||
client_id
|
||||
REQUIRED. The client identifier as described in `Section 2.2`_.
|
||||
redirect_uri
|
||||
OPTIONAL. As described in `Section 3.1.2`_.
|
||||
scope
|
||||
OPTIONAL. The scope of the access request as described by
|
||||
`Section 3.3`_.
|
||||
state
|
||||
RECOMMENDED. An opaque value used by the client to maintain
|
||||
state between the request and callback. The authorization
|
||||
server includes this value when redirecting the user-agent back
|
||||
to the client. The parameter SHOULD be used for preventing
|
||||
cross-site request forgery as described in `Section 10.12`_.
|
||||
|
||||
.. _`W3C.REC-html401-19991224`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#ref-W3C.REC-html401-19991224
|
||||
.. _`Section 2.2`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-2.2
|
||||
.. _`Section 3.1.2`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-3.1.2
|
||||
.. _`Section 3.3`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-3.3
|
||||
.. _`Section 10.12`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-10.12
|
||||
"""
|
||||
redirect_uri = redirect_uri or self.default_redirect_uri
|
||||
return prepare_grant_uri(uri, self.client_id, u'code',
|
||||
redirect_uri=redirect_uri, scope=scope, state=state, **kwargs)
|
||||
|
||||
def prepare_request_body(self, code, body=u'', redirect_uri=None, **kwargs):
|
||||
"""Prepare the access token request body.
|
||||
|
||||
The client makes a request to the token endpoint by adding the
|
||||
following parameters using the "application/x-www-form-urlencoded"
|
||||
format in the HTTP request entity-body:
|
||||
|
||||
grant_type
|
||||
REQUIRED. Value MUST be set to "authorization_code".
|
||||
code
|
||||
REQUIRED. The authorization code received from the
|
||||
authorization server.
|
||||
redirect_uri
|
||||
REQUIRED, if the "redirect_uri" parameter was included in the
|
||||
authorization request as described in Section 4.1.1, and their
|
||||
values MUST be identical.
|
||||
|
||||
.. _`Section 4.1.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-4.1.1
|
||||
"""
|
||||
redirect_uri = redirect_uri or self.default_redirect_uri
|
||||
code = code or self.code
|
||||
return prepare_token_request(u'authorization_code', code=code, body=body,
|
||||
redirect_uri=redirect_uri, **kwargs)
|
||||
|
||||
def parse_request_uri_response(self, uri, state=None):
|
||||
"""Parse the URI query for code and state.
|
||||
|
||||
If the resource owner grants the access request, the authorization
|
||||
server issues an authorization code and delivers it to the client by
|
||||
adding the following parameters to the query component of the
|
||||
redirection URI using the "application/x-www-form-urlencoded" format:
|
||||
|
||||
code
|
||||
REQUIRED. The authorization code generated by the
|
||||
authorization server. The authorization code MUST expire
|
||||
shortly after it is issued to mitigate the risk of leaks. A
|
||||
maximum authorization code lifetime of 10 minutes is
|
||||
RECOMMENDED. The client MUST NOT use the authorization code
|
||||
more than once. If an authorization code is used more than
|
||||
once, the authorization server MUST deny the request and SHOULD
|
||||
revoke (when possible) all tokens previously issued based on
|
||||
that authorization code. The authorization code is bound to
|
||||
the client identifier and redirection URI.
|
||||
state
|
||||
REQUIRED if the "state" parameter was present in the client
|
||||
authorization request. The exact value received from the
|
||||
client.
|
||||
"""
|
||||
response = parse_authorization_code_response(uri, state=state)
|
||||
self._populate_attributes(response)
|
||||
return response
|
||||
|
||||
def parse_request_body_response(self, body, scope=None):
|
||||
"""Parse the JSON response body.
|
||||
|
||||
If the access token request is valid and authorized, the
|
||||
authorization server issues an access token and optional refresh
|
||||
token as described in `Section 5.1`_. If the request client
|
||||
authentication failed or is invalid, the authorization server returns
|
||||
an error response as described in `Section 5.2`_.
|
||||
|
||||
.. `Section 5.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-5.1
|
||||
.. `Section 5.2`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-5.2
|
||||
"""
|
||||
response = parse_token_response(body, scope=scope)
|
||||
self._populate_attributes(response)
|
||||
return response
|
||||
|
||||
|
||||
class UserAgentClient(Client):
|
||||
"""A public client utilizing the implicit code grant workflow.
|
||||
|
||||
A user-agent-based application is a public client in which the
|
||||
client code is downloaded from a web server and executes within a
|
||||
user-agent (e.g. web browser) on the device used by the resource
|
||||
owner. Protocol data and credentials are easily accessible (and
|
||||
often visible) to the resource owner. Since such applications
|
||||
reside within the user-agent, they can make seamless use of the
|
||||
user-agent capabilities when requesting authorization.
|
||||
|
||||
The implicit grant type is used to obtain access tokens (it does not
|
||||
support the issuance of refresh tokens) and is optimized for public
|
||||
clients known to operate a particular redirection URI. These clients
|
||||
are typically implemented in a browser using a scripting language
|
||||
such as JavaScript.
|
||||
|
||||
As a redirection-based flow, the client must be capable of
|
||||
interacting with the resource owner's user-agent (typically a web
|
||||
browser) and capable of receiving incoming requests (via redirection)
|
||||
from the authorization server.
|
||||
|
||||
Unlike the authorization code grant type in which the client makes
|
||||
separate requests for authorization and access token, the client
|
||||
receives the access token as the result of the authorization request.
|
||||
|
||||
The implicit grant type does not include client authentication, and
|
||||
relies on the presence of the resource owner and the registration of
|
||||
the redirection URI. Because the access token is encoded into the
|
||||
redirection URI, it may be exposed to the resource owner and other
|
||||
applications residing on the same device.
|
||||
"""
|
||||
|
||||
def prepare_request_uri(self, uri, redirect_uri=None, scope=None,
|
||||
state=None, **kwargs):
|
||||
"""Prepare the implicit grant request URI.
|
||||
|
||||
The client constructs the request URI by adding the following
|
||||
parameters to the query component of the authorization endpoint URI
|
||||
using the "application/x-www-form-urlencoded" format:
|
||||
|
||||
response_type
|
||||
REQUIRED. Value MUST be set to "token".
|
||||
client_id
|
||||
REQUIRED. The client identifier as described in Section 2.2.
|
||||
redirect_uri
|
||||
OPTIONAL. As described in Section 3.1.2.
|
||||
scope
|
||||
OPTIONAL. The scope of the access request as described by
|
||||
Section 3.3.
|
||||
state
|
||||
RECOMMENDED. An opaque value used by the client to maintain
|
||||
state between the request and callback. The authorization
|
||||
server includes this value when redirecting the user-agent back
|
||||
to the client. The parameter SHOULD be used for preventing
|
||||
cross-site request forgery as described in Section 10.12.
|
||||
"""
|
||||
redirect_uri = redirect_uri or self.default_redirect_uri
|
||||
return prepare_grant_uri(uri, self.client_id, u'token',
|
||||
redirect_uri=redirect_uri, state=state, scope=scope, **kwargs)
|
||||
|
||||
def parse_request_uri_response(self, uri, state=None, scope=None):
|
||||
"""Parse the response URI fragment.
|
||||
|
||||
If the resource owner grants the access request, the authorization
|
||||
server issues an access token and delivers it to the client by adding
|
||||
the following parameters to the fragment component of the redirection
|
||||
URI using the "application/x-www-form-urlencoded" format:
|
||||
|
||||
access_token
|
||||
REQUIRED. The access token issued by the authorization server.
|
||||
token_type
|
||||
REQUIRED. The type of the token issued as described in
|
||||
`Section 7.1`_. Value is case insensitive.
|
||||
expires_in
|
||||
RECOMMENDED. The lifetime in seconds of the access token. For
|
||||
example, the value "3600" denotes that the access token will
|
||||
expire in one hour from the time the response was generated.
|
||||
If omitted, the authorization server SHOULD provide the
|
||||
expiration time via other means or document the default value.
|
||||
scope
|
||||
OPTIONAL, if identical to the scope requested by the client,
|
||||
otherwise REQUIRED. The scope of the access token as described
|
||||
by `Section 3.3`_.
|
||||
state
|
||||
REQUIRED if the "state" parameter was present in the client
|
||||
authorization request. The exact value received from the
|
||||
client.
|
||||
|
||||
.. _`Section 7.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-7.1
|
||||
.. _`Section 3.3`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-3.3
|
||||
"""
|
||||
response = parse_implicit_response(uri, state=state, scope=scope)
|
||||
self._populate_attributes(response)
|
||||
return response
|
||||
|
||||
|
||||
class NativeApplicationClient(Client):
|
||||
"""A public client utilizing the client credentials grant workflow.
|
||||
|
||||
A native application is a public client installed and executed on
|
||||
the device used by the resource owner. Protocol data and
|
||||
credentials are accessible to the resource owner. It is assumed
|
||||
that any client authentication credentials included in the
|
||||
application can be extracted. On the other hand, dynamically
|
||||
issued credentials such as access tokens or refresh tokens can
|
||||
receive an acceptable level of protection. At a minimum, these
|
||||
credentials are protected from hostile servers with which the
|
||||
application may interact with. On some platforms these
|
||||
credentials might be protected from other applications residing on
|
||||
the same device.
|
||||
|
||||
The client can request an access token using only its client
|
||||
credentials (or other supported means of authentication) when the
|
||||
client is requesting access to the protected resources under its
|
||||
control, or those of another resource owner which has been previously
|
||||
arranged with the authorization server (the method of which is beyond
|
||||
the scope of this specification).
|
||||
|
||||
The client credentials grant type MUST only be used by confidential
|
||||
clients.
|
||||
|
||||
Since the client authentication is used as the authorization grant,
|
||||
no additional authorization request is needed.
|
||||
"""
|
||||
|
||||
def prepare_request_body(self, body=u'', scope=None, **kwargs):
|
||||
"""Add the client credentials to the request body.
|
||||
|
||||
The client makes a request to the token endpoint by adding the
|
||||
following parameters using the "application/x-www-form-urlencoded"
|
||||
format in the HTTP request entity-body:
|
||||
|
||||
grant_type
|
||||
REQUIRED. Value MUST be set to "client_credentials".
|
||||
scope
|
||||
OPTIONAL. The scope of the access request as described by
|
||||
`Section 3.3`_.
|
||||
|
||||
.. _`Section 3.3`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-3.3
|
||||
"""
|
||||
return prepare_token_request(u'client_credentials', body=body,
|
||||
scope=scope, **kwargs)
|
||||
|
||||
def parse_request_body_response(self, body, scope=None):
|
||||
"""Parse the JSON response body.
|
||||
|
||||
If the access token request is valid and authorized, the
|
||||
authorization server issues an access token as described in
|
||||
`Section 5.1`_. A refresh token SHOULD NOT be included. If the request
|
||||
failed client authentication or is invalid, the authorization server
|
||||
returns an error response as described in `Section 5.2`_.
|
||||
|
||||
.. `Section 5.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-5.1
|
||||
.. `Section 5.2`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-5.2
|
||||
"""
|
||||
response = parse_token_response(body, scope=scope)
|
||||
self._populate_attributes(response)
|
||||
return response
|
||||
|
||||
|
||||
class PasswordCredentialsClient(Client):
|
||||
"""A public client using the resource owner password and username directly.
|
||||
|
||||
The resource owner password credentials grant type is suitable in
|
||||
cases where the resource owner has a trust relationship with the
|
||||
client, such as the device operating system or a highly privileged
|
||||
application. The authorization server should take special care when
|
||||
enabling this grant type, and only allow it when other flows are not
|
||||
viable.
|
||||
|
||||
The grant type is suitable for clients capable of obtaining the
|
||||
resource owner's credentials (username and password, typically using
|
||||
an interactive form). It is also used to migrate existing clients
|
||||
using direct authentication schemes such as HTTP Basic or Digest
|
||||
authentication to OAuth by converting the stored credentials to an
|
||||
access token.
|
||||
|
||||
The method through which the client obtains the resource owner
|
||||
credentials is beyond the scope of this specification. The client
|
||||
MUST discard the credentials once an access token has been obtained.
|
||||
"""
|
||||
|
||||
def prepare_request_body(self, username, password, body=u'', scope=None,
|
||||
**kwargs):
|
||||
"""Add the resource owner password and username to the request body.
|
||||
|
||||
The client makes a request to the token endpoint by adding the
|
||||
following parameters using the "application/x-www-form-urlencoded"
|
||||
format in the HTTP request entity-body:
|
||||
|
||||
grant_type
|
||||
REQUIRED. Value MUST be set to "password".
|
||||
username
|
||||
REQUIRED. The resource owner username.
|
||||
password
|
||||
REQUIRED. The resource owner password.
|
||||
scope
|
||||
OPTIONAL. The scope of the access request as described by
|
||||
`Section 3.3`_.
|
||||
|
||||
.. _`Section 3.3`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-3.3
|
||||
"""
|
||||
return prepare_token_request(u'password', body=body, username=username,
|
||||
password=password, scope=scope, **kwargs)
|
||||
|
||||
def parse_request_body_response(self, body, scope=None):
|
||||
"""Parse the JSON response body.
|
||||
|
||||
If the access token request is valid and authorized, the
|
||||
authorization server issues an access token and optional refresh
|
||||
token as described in `Section 5.1`_. If the request failed client
|
||||
authentication or is invalid, the authorization server returns an
|
||||
error response as described in `Section 5.2`_.
|
||||
|
||||
.. `Section 5.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-5.1
|
||||
.. `Section 5.2`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-5.2
|
||||
"""
|
||||
response = parse_token_response(body, scope=scope)
|
||||
self._populate_attributes(response)
|
||||
return response
|
||||
|
||||
|
||||
class Server(object):
|
||||
pass
|
||||
@@ -0,0 +1,256 @@
|
||||
"""
|
||||
oauthlib.oauth2_draft28.parameters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module contains methods related to `Section 4`_ of the OAuth 2 draft.
|
||||
|
||||
.. _`Section 4`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-4
|
||||
"""
|
||||
|
||||
import json
|
||||
import urlparse
|
||||
from oauthlib.common import add_params_to_uri, add_params_to_qs
|
||||
|
||||
|
||||
def prepare_grant_uri(uri, client_id, response_type, redirect_uri=None,
|
||||
scope=None, state=None, **kwargs):
|
||||
"""Prepare the authorization grant request URI.
|
||||
|
||||
The client constructs the request URI by adding the following
|
||||
parameters to the query component of the authorization endpoint URI
|
||||
using the "application/x-www-form-urlencoded" format as defined by
|
||||
[W3C.REC-html401-19991224]:
|
||||
|
||||
response_type
|
||||
REQUIRED. Value MUST be set to "code".
|
||||
client_id
|
||||
REQUIRED. The client identifier as described in `Section 2.2`_.
|
||||
redirect_uri
|
||||
OPTIONAL. As described in `Section 3.1.2`_.
|
||||
scope
|
||||
OPTIONAL. The scope of the access request as described by
|
||||
`Section 3.3`_.
|
||||
state
|
||||
RECOMMENDED. An opaque value used by the client to maintain
|
||||
state between the request and callback. The authorization
|
||||
server includes this value when redirecting the user-agent back
|
||||
to the client. The parameter SHOULD be used for preventing
|
||||
cross-site request forgery as described in `Section 10.12`_.
|
||||
|
||||
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
|
||||
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
|
||||
Host: server.example.com
|
||||
|
||||
.. _`W3C.REC-html401-19991224`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#ref-W3C.REC-html401-19991224
|
||||
.. _`Section 2.2`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-2.2
|
||||
.. _`Section 3.1.2`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-3.1.2
|
||||
.. _`Section 3.3`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-3.3
|
||||
.. _`section 10.12`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-10.12
|
||||
"""
|
||||
params = [((u'response_type', response_type)),
|
||||
((u'client_id', client_id))]
|
||||
|
||||
if redirect_uri:
|
||||
params.append((u'redirect_uri', redirect_uri))
|
||||
if scope:
|
||||
params.append((u'scope', scope))
|
||||
if state:
|
||||
params.append((u'state', state))
|
||||
|
||||
for k in kwargs:
|
||||
params.append((unicode(k), kwargs[k]))
|
||||
|
||||
return add_params_to_uri(uri, params)
|
||||
|
||||
|
||||
def prepare_token_request(grant_type, body=u'', **kwargs):
|
||||
"""Prepare the access token request.
|
||||
|
||||
The client makes a request to the token endpoint by adding the
|
||||
following parameters using the "application/x-www-form-urlencoded"
|
||||
format in the HTTP request entity-body:
|
||||
|
||||
grant_type
|
||||
REQUIRED. Value MUST be set to "authorization_code".
|
||||
code
|
||||
REQUIRED. The authorization code received from the
|
||||
authorization server.
|
||||
redirect_uri
|
||||
REQUIRED, if the "redirect_uri" parameter was included in the
|
||||
authorization request as described in `Section 4.1.1`_, and their
|
||||
values MUST be identical.
|
||||
|
||||
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
|
||||
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
|
||||
|
||||
.. _`Section 4.1.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-4.1.1
|
||||
"""
|
||||
params = [(u'grant_type', grant_type)]
|
||||
for k in kwargs:
|
||||
params.append((unicode(k), kwargs[k]))
|
||||
|
||||
return add_params_to_qs(body, params)
|
||||
|
||||
|
||||
def parse_authorization_code_response(uri, state=None):
|
||||
"""Parse authorization grant response URI into a dict.
|
||||
|
||||
If the resource owner grants the access request, the authorization
|
||||
server issues an authorization code and delivers it to the client by
|
||||
adding the following parameters to the query component of the
|
||||
redirection URI using the "application/x-www-form-urlencoded" format:
|
||||
|
||||
code
|
||||
REQUIRED. The authorization code generated by the
|
||||
authorization server. The authorization code MUST expire
|
||||
shortly after it is issued to mitigate the risk of leaks. A
|
||||
maximum authorization code lifetime of 10 minutes is
|
||||
RECOMMENDED. The client MUST NOT use the authorization code
|
||||
more than once. If an authorization code is used more than
|
||||
once, the authorization server MUST deny the request and SHOULD
|
||||
revoke (when possible) all tokens previously issued based on
|
||||
that authorization code. The authorization code is bound to
|
||||
the client identifier and redirection URI.
|
||||
state
|
||||
REQUIRED if the "state" parameter was present in the client
|
||||
authorization request. The exact value received from the
|
||||
client.
|
||||
|
||||
For example, the authorization server redirects the user-agent by
|
||||
sending the following HTTP response:
|
||||
|
||||
HTTP/1.1 302 Found
|
||||
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
|
||||
&state=xyz
|
||||
|
||||
"""
|
||||
query = urlparse.urlparse(uri).query
|
||||
params = dict(urlparse.parse_qsl(query))
|
||||
|
||||
if not u'code' in params:
|
||||
raise KeyError("Missing code parameter in response.")
|
||||
|
||||
if state and params.get(u'state', None) != state:
|
||||
raise ValueError("Mismatching or missing state in response.")
|
||||
|
||||
return params
|
||||
|
||||
|
||||
def parse_implicit_response(uri, state=None, scope=None):
|
||||
"""Parse the implicit token response URI into a dict.
|
||||
|
||||
If the resource owner grants the access request, the authorization
|
||||
server issues an access token and delivers it to the client by adding
|
||||
the following parameters to the fragment component of the redirection
|
||||
URI using the "application/x-www-form-urlencoded" format:
|
||||
|
||||
access_token
|
||||
REQUIRED. The access token issued by the authorization server.
|
||||
token_type
|
||||
REQUIRED. The type of the token issued as described in
|
||||
Section 7.1. Value is case insensitive.
|
||||
expires_in
|
||||
RECOMMENDED. The lifetime in seconds of the access token. For
|
||||
example, the value "3600" denotes that the access token will
|
||||
expire in one hour from the time the response was generated.
|
||||
If omitted, the authorization server SHOULD provide the
|
||||
expiration time via other means or document the default value.
|
||||
scope
|
||||
OPTIONAL, if identical to the scope requested by the client,
|
||||
otherwise REQUIRED. The scope of the access token as described
|
||||
by Section 3.3.
|
||||
state
|
||||
REQUIRED if the "state" parameter was present in the client
|
||||
authorization request. The exact value received from the
|
||||
client.
|
||||
|
||||
HTTP/1.1 302 Found
|
||||
Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
|
||||
&state=xyz&token_type=example&expires_in=3600
|
||||
"""
|
||||
fragment = urlparse.urlparse(uri).fragment
|
||||
params = dict(urlparse.parse_qsl(fragment, keep_blank_values=True))
|
||||
validate_token_parameters(params, scope)
|
||||
|
||||
if state and params.get(u'state', None) != state:
|
||||
raise ValueError("Mismatching or missing state in params.")
|
||||
|
||||
return params
|
||||
|
||||
|
||||
def parse_token_response(body, scope=None):
|
||||
"""Parse the JSON token response body into a dict.
|
||||
|
||||
The authorization server issues an access token and optional refresh
|
||||
token, and constructs the response by adding the following parameters
|
||||
to the entity body of the HTTP response with a 200 (OK) status code:
|
||||
|
||||
access_token
|
||||
REQUIRED. The access token issued by the authorization server.
|
||||
token_type
|
||||
REQUIRED. The type of the token issued as described in
|
||||
`Section 7.1`_. Value is case insensitive.
|
||||
expires_in
|
||||
RECOMMENDED. The lifetime in seconds of the access token. For
|
||||
example, the value "3600" denotes that the access token will
|
||||
expire in one hour from the time the response was generated.
|
||||
If omitted, the authorization server SHOULD provide the
|
||||
expiration time via other means or document the default value.
|
||||
refresh_token
|
||||
OPTIONAL. The refresh token which can be used to obtain new
|
||||
access tokens using the same authorization grant as described
|
||||
in `Section 6`_.
|
||||
scope
|
||||
OPTIONAL, if identical to the scope requested by the client,
|
||||
otherwise REQUIRED. The scope of the access token as described
|
||||
by `Section 3.3`_.
|
||||
|
||||
The parameters are included in the entity body of the HTTP response
|
||||
using the "application/json" media type as defined by [`RFC4627`_]. The
|
||||
parameters are serialized into a JSON structure by adding each
|
||||
parameter at the highest structure level. Parameter names and string
|
||||
values are included as JSON strings. Numerical values are included
|
||||
as JSON numbers. The order of parameters does not matter and can
|
||||
vary.
|
||||
|
||||
For example:
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
Cache-Control: no-store
|
||||
Pragma: no-cache
|
||||
|
||||
{
|
||||
"access_token":"2YotnFZFEjr1zCsicMWpAA",
|
||||
"token_type":"example",
|
||||
"expires_in":3600,
|
||||
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
|
||||
"example_parameter":"example_value"
|
||||
}
|
||||
|
||||
.. _`Section 7.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-7.1
|
||||
.. _`Section 6`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-6
|
||||
.. _`Section 3.3`: http://tools.ietf.org/html/draft-ietf-oauth-v2-28#section-3.3
|
||||
.. _`RFC4627`: http://tools.ietf.org/html/rfc4627
|
||||
"""
|
||||
params = json.loads(body)
|
||||
validate_token_parameters(params, scope)
|
||||
return params
|
||||
|
||||
|
||||
def validate_token_parameters(params, scope=None):
|
||||
"""Ensures token precence, token type, expiration and scope in params."""
|
||||
|
||||
if not u'access_token' in params:
|
||||
raise KeyError("Missing access token parameter.")
|
||||
|
||||
if not u'token_type' in params:
|
||||
raise KeyError("Missing token type parameter.")
|
||||
|
||||
# If the issued access token scope is different from the one requested by
|
||||
# the client, the authorization server MUST include the "scope" response
|
||||
# parameter to inform the client of the actual scope granted.
|
||||
# http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-3.3
|
||||
new_scope = params.get(u'scope', None)
|
||||
if scope and new_scope and scope != new_scope:
|
||||
raise Warning("Scope has changed to %s." % new_scope)
|
||||
@@ -0,0 +1,132 @@
|
||||
from __future__ import absolute_import
|
||||
"""
|
||||
oauthlib.oauth2.draft25.tokens
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module contains methods for adding two types of access tokens to requests.
|
||||
|
||||
- Bearer http://tools.ietf.org/html/draft-ietf-oauth-saml2-bearer-08
|
||||
- MAC http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-00
|
||||
|
||||
"""
|
||||
from binascii import b2a_base64
|
||||
import hashlib
|
||||
import hmac
|
||||
from urlparse import urlparse
|
||||
|
||||
from oauthlib.common import add_params_to_uri, add_params_to_qs
|
||||
from . import utils
|
||||
|
||||
|
||||
def prepare_mac_header(token, uri, key, http_method, nonce=None, headers=None,
|
||||
body=None, ext=u'', hash_algorithm=u'hmac-sha-1'):
|
||||
"""Add an `MAC Access Authentication`_ signature to headers.
|
||||
|
||||
Unlike OAuth 1, this HMAC signature does not require inclusion of the request
|
||||
payload/body, neither does it use a combination of client_secret and
|
||||
token_secret but rather a mac_key provided together with the access token.
|
||||
|
||||
Currently two algorithms are supported, "hmac-sha-1" and "hmac-sha-256",
|
||||
`extension algorithms`_ are not supported.
|
||||
|
||||
Example MAC Authorization header, linebreaks added for clarity
|
||||
|
||||
Authorization: MAC id="h480djs93hd8",
|
||||
nonce="1336363200:dj83hs9s",
|
||||
mac="bhCQXTVyfj5cmA9uKkPFx1zeOXM="
|
||||
|
||||
.. _`MAC Access Authentication`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01
|
||||
.. _`extension algorithms`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-7.1
|
||||
|
||||
:param uri: Request URI.
|
||||
:param headers: Request headers as a dictionary.
|
||||
:param http_method: HTTP Request method.
|
||||
:param key: MAC given provided by token endpoint.
|
||||
:param algorithm: HMAC algorithm provided by token endpoint.
|
||||
:return: headers dictionary with the authorization field added.
|
||||
"""
|
||||
http_method = http_method.upper()
|
||||
host, port = utils.host_from_uri(uri)
|
||||
|
||||
if hash_algorithm.lower() == u'hmac-sha-1':
|
||||
h = hashlib.sha1
|
||||
else:
|
||||
h = hashlib.sha256
|
||||
|
||||
nonce = nonce or u'{0}:{1}'.format(utils.generate_nonce(), utils.generate_timestamp())
|
||||
sch, net, path, par, query, fra = urlparse(uri)
|
||||
|
||||
if query:
|
||||
request_uri = path + u'?' + query
|
||||
else:
|
||||
request_uri = path
|
||||
|
||||
# Hash the body/payload
|
||||
if body is not None:
|
||||
bodyhash = b2a_base64(h(body).digest())[:-1].decode('utf-8')
|
||||
else:
|
||||
bodyhash = u''
|
||||
|
||||
# Create the normalized base string
|
||||
base = []
|
||||
base.append(nonce)
|
||||
base.append(http_method.upper())
|
||||
base.append(request_uri)
|
||||
base.append(host)
|
||||
base.append(port)
|
||||
base.append(bodyhash)
|
||||
base.append(ext)
|
||||
base_string = '\n'.join(base) + u'\n'
|
||||
|
||||
# hmac struggles with unicode strings - http://bugs.python.org/issue5285
|
||||
if isinstance(key, unicode):
|
||||
key = key.encode('utf-8')
|
||||
sign = hmac.new(key, base_string, h)
|
||||
sign = b2a_base64(sign.digest())[:-1].decode('utf-8')
|
||||
|
||||
header = []
|
||||
header.append(u'MAC id="%s"' % token)
|
||||
header.append(u'nonce="%s"' % nonce)
|
||||
if bodyhash:
|
||||
header.append(u'bodyhash="%s"' % bodyhash)
|
||||
if ext:
|
||||
header.append(u'ext="%s"' % ext)
|
||||
header.append(u'mac="%s"' % sign)
|
||||
|
||||
headers = headers or {}
|
||||
headers[u'Authorization'] = u', '.join(header)
|
||||
return headers
|
||||
|
||||
|
||||
def prepare_bearer_uri(token, uri):
|
||||
"""Add a `Bearer Token`_ to the request URI.
|
||||
Not recommended, use only if client can't use authorization header or body.
|
||||
|
||||
http://www.example.com/path?access_token=h480djs93hd8
|
||||
|
||||
.. _`Bearer Token`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-18
|
||||
"""
|
||||
return add_params_to_uri(uri, [((u'access_token', token))])
|
||||
|
||||
|
||||
def prepare_bearer_headers(token, headers=None):
|
||||
"""Add a `Bearer Token`_ to the request URI.
|
||||
Recommended method of passing bearer tokens.
|
||||
|
||||
Authorization: Bearer h480djs93hd8
|
||||
|
||||
.. _`Bearer Token`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-18
|
||||
"""
|
||||
headers = headers or {}
|
||||
headers[u'Authorization'] = u'Bearer %s' % token
|
||||
return headers
|
||||
|
||||
|
||||
def prepare_bearer_body(token, body=u''):
|
||||
"""Add a `Bearer Token`_ to the request body.
|
||||
|
||||
access_token=h480djs93hd8
|
||||
|
||||
.. _`Bearer Token`: http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-18
|
||||
"""
|
||||
return add_params_to_qs(body, [((u'access_token', token))])
|
||||
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
oauthlib.utils
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module contains utility methods used by various parts of the OAuth 2 spec.
|
||||
"""
|
||||
|
||||
import urllib
|
||||
import urlparse
|
||||
|
||||
|
||||
def host_from_uri(uri):
|
||||
"""Extract hostname and port from URI.
|
||||
|
||||
Will use default port for HTTP and HTTPS if none is present in the URI.
|
||||
"""
|
||||
default_ports = {
|
||||
u'HTTP': u'80',
|
||||
u'HTTPS': u'443',
|
||||
}
|
||||
|
||||
sch, netloc, path, par, query, fra = urlparse.urlparse(uri)
|
||||
if u':' in netloc:
|
||||
netloc, port = netloc.split(u':', 1)
|
||||
else:
|
||||
port = default_ports.get(sch.upper())
|
||||
|
||||
return netloc, port
|
||||
|
||||
|
||||
def escape(u):
|
||||
"""Escape a string in an OAuth-compatible fashion.
|
||||
|
||||
TODO: verify whether this can in fact be used for OAuth 2
|
||||
|
||||
"""
|
||||
if not isinstance(u, unicode):
|
||||
raise ValueError('Only unicode objects are escapable.')
|
||||
return urllib.quote(u.encode('utf-8'), safe='~')
|
||||
Reference in New Issue
Block a user