venv added, updated
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
authlib.oauth2.rfc6750
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module represents a direct implementation of
|
||||
The OAuth 2.0 Authorization Framework: Bearer Token Usage.
|
||||
|
||||
https://tools.ietf.org/html/rfc6750
|
||||
"""
|
||||
|
||||
from .errors import InvalidTokenError, InsufficientScopeError
|
||||
from .parameters import add_bearer_token
|
||||
from .token import BearerTokenGenerator
|
||||
from .validator import BearerTokenValidator
|
||||
|
||||
# TODO: add deprecation
|
||||
BearerToken = BearerTokenGenerator
|
||||
|
||||
|
||||
__all__ = [
|
||||
'InvalidTokenError', 'InsufficientScopeError',
|
||||
'add_bearer_token',
|
||||
'BearerToken',
|
||||
'BearerTokenGenerator',
|
||||
'BearerTokenValidator',
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
authlib.rfc6750.errors
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
OAuth Extensions Error Registration. When a request fails,
|
||||
the resource server responds using the appropriate HTTP
|
||||
status code and includes one of the following error codes
|
||||
in the response.
|
||||
|
||||
https://tools.ietf.org/html/rfc6750#section-6.2
|
||||
|
||||
:copyright: (c) 2017 by Hsiaoming Yang.
|
||||
"""
|
||||
from ..base import OAuth2Error
|
||||
|
||||
__all__ = [
|
||||
'InvalidTokenError', 'InsufficientScopeError'
|
||||
]
|
||||
|
||||
|
||||
class InvalidTokenError(OAuth2Error):
|
||||
"""The access token provided is expired, revoked, malformed, or
|
||||
invalid for other reasons. The resource SHOULD respond with
|
||||
the HTTP 401 (Unauthorized) status code. The client MAY
|
||||
request a new access token and retry the protected resource
|
||||
request.
|
||||
|
||||
https://tools.ietf.org/html/rfc6750#section-3.1
|
||||
"""
|
||||
error = 'invalid_token'
|
||||
description = (
|
||||
'The access token provided is expired, revoked, malformed, '
|
||||
'or invalid for other reasons.'
|
||||
)
|
||||
status_code = 401
|
||||
|
||||
def __init__(self, description=None, uri=None, status_code=None,
|
||||
state=None, realm=None, **extra_attributes):
|
||||
super().__init__(
|
||||
description, uri, status_code, state)
|
||||
self.realm = realm
|
||||
self.extra_attributes = extra_attributes
|
||||
|
||||
def get_headers(self):
|
||||
"""If the protected resource request does not include authentication
|
||||
credentials or does not contain an access token that enables access
|
||||
to the protected resource, the resource server MUST include the HTTP
|
||||
"WWW-Authenticate" response header field; it MAY include it in
|
||||
response to other conditions as well.
|
||||
|
||||
https://tools.ietf.org/html/rfc6750#section-3
|
||||
"""
|
||||
headers = super().get_headers()
|
||||
|
||||
extras = []
|
||||
if self.realm:
|
||||
extras.append(f'realm="{self.realm}"')
|
||||
if self.extra_attributes:
|
||||
extras.extend([f'{k}="{self.extra_attributes[k]}"' for k in self.extra_attributes])
|
||||
extras.append(f'error="{self.error}"')
|
||||
error_description = self.get_error_description()
|
||||
extras.append(f'error_description="{error_description}"')
|
||||
headers.append(
|
||||
('WWW-Authenticate', 'Bearer ' + ', '.join(extras))
|
||||
)
|
||||
return headers
|
||||
|
||||
|
||||
class InsufficientScopeError(OAuth2Error):
|
||||
"""The request requires higher privileges than provided by the
|
||||
access token. The resource server SHOULD respond with the HTTP
|
||||
403 (Forbidden) status code and MAY include the "scope"
|
||||
attribute with the scope necessary to access the protected
|
||||
resource.
|
||||
|
||||
https://tools.ietf.org/html/rfc6750#section-3.1
|
||||
"""
|
||||
error = 'insufficient_scope'
|
||||
description = 'The request requires higher privileges than provided by the access token.'
|
||||
status_code = 403
|
||||
@@ -0,0 +1,41 @@
|
||||
from authlib.common.urls import add_params_to_qs, add_params_to_uri
|
||||
|
||||
|
||||
def add_to_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
|
||||
"""
|
||||
return add_params_to_uri(uri, [('access_token', token)])
|
||||
|
||||
|
||||
def add_to_headers(token, headers=None):
|
||||
"""Add a Bearer Token to the request URI.
|
||||
Recommended method of passing bearer tokens.
|
||||
|
||||
Authorization: Bearer h480djs93hd8
|
||||
"""
|
||||
headers = headers or {}
|
||||
headers['Authorization'] = f'Bearer {token}'
|
||||
return headers
|
||||
|
||||
|
||||
def add_to_body(token, body=None):
|
||||
"""Add a Bearer Token to the request body.
|
||||
|
||||
access_token=h480djs93hd8
|
||||
"""
|
||||
if body is None:
|
||||
body = ''
|
||||
return add_params_to_qs(body, [('access_token', token)])
|
||||
|
||||
|
||||
def add_bearer_token(token, uri, headers, body, placement='header'):
|
||||
if placement in ('uri', 'url', 'query'):
|
||||
uri = add_to_uri(token, uri)
|
||||
elif placement in ('header', 'headers'):
|
||||
headers = add_to_headers(token, headers)
|
||||
elif placement == 'body':
|
||||
body = add_to_body(token, body)
|
||||
return uri, headers, body
|
||||
@@ -0,0 +1,88 @@
|
||||
class BearerTokenGenerator:
|
||||
"""Bearer token generator which can create the payload for token response
|
||||
by OAuth 2 server. A typical token response would be:
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json;charset=UTF-8
|
||||
Cache-Control: no-store
|
||||
Pragma: no-cache
|
||||
|
||||
{
|
||||
"access_token":"mF_9.B5f-4.1JqM",
|
||||
"token_type":"Bearer",
|
||||
"expires_in":3600,
|
||||
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
|
||||
}
|
||||
"""
|
||||
|
||||
#: default expires_in value
|
||||
DEFAULT_EXPIRES_IN = 3600
|
||||
#: default expires_in value differentiate by grant_type
|
||||
GRANT_TYPES_EXPIRES_IN = {
|
||||
'authorization_code': 864000,
|
||||
'implicit': 3600,
|
||||
'password': 864000,
|
||||
'client_credentials': 864000
|
||||
}
|
||||
|
||||
def __init__(self, access_token_generator,
|
||||
refresh_token_generator=None,
|
||||
expires_generator=None):
|
||||
self.access_token_generator = access_token_generator
|
||||
self.refresh_token_generator = refresh_token_generator
|
||||
self.expires_generator = expires_generator
|
||||
|
||||
def _get_expires_in(self, client, grant_type):
|
||||
if self.expires_generator is None:
|
||||
expires_in = self.GRANT_TYPES_EXPIRES_IN.get(
|
||||
grant_type, self.DEFAULT_EXPIRES_IN)
|
||||
elif callable(self.expires_generator):
|
||||
expires_in = self.expires_generator(client, grant_type)
|
||||
elif isinstance(self.expires_generator, int):
|
||||
expires_in = self.expires_generator
|
||||
else:
|
||||
expires_in = self.DEFAULT_EXPIRES_IN
|
||||
return expires_in
|
||||
|
||||
@staticmethod
|
||||
def get_allowed_scope(client, scope):
|
||||
if scope:
|
||||
scope = client.get_allowed_scope(scope)
|
||||
return scope
|
||||
|
||||
def generate(self, grant_type, client, user=None, scope=None,
|
||||
expires_in=None, include_refresh_token=True):
|
||||
"""Generate a bearer token for OAuth 2.0 authorization token endpoint.
|
||||
|
||||
:param client: the client that making the request.
|
||||
:param grant_type: current requested grant_type.
|
||||
:param user: current authorized user.
|
||||
:param expires_in: if provided, use this value as expires_in.
|
||||
:param scope: current requested scope.
|
||||
:param include_refresh_token: should refresh_token be included.
|
||||
:return: Token dict
|
||||
"""
|
||||
scope = self.get_allowed_scope(client, scope)
|
||||
access_token = self.access_token_generator(
|
||||
client=client, grant_type=grant_type, user=user, scope=scope)
|
||||
if expires_in is None:
|
||||
expires_in = self._get_expires_in(client, grant_type)
|
||||
|
||||
token = {
|
||||
'token_type': 'Bearer',
|
||||
'access_token': access_token,
|
||||
}
|
||||
if expires_in:
|
||||
token['expires_in'] = expires_in
|
||||
if include_refresh_token and self.refresh_token_generator:
|
||||
token['refresh_token'] = self.refresh_token_generator(
|
||||
client=client, grant_type=grant_type, user=user, scope=scope)
|
||||
if scope:
|
||||
token['scope'] = scope
|
||||
return token
|
||||
|
||||
def __call__(self, grant_type, client, user=None, scope=None,
|
||||
expires_in=None, include_refresh_token=True):
|
||||
return self.generate(grant_type, client, user, scope, expires_in, include_refresh_token)
|
||||
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
authlib.oauth2.rfc6750.validator
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Validate Bearer Token for in request, scope and token.
|
||||
"""
|
||||
|
||||
from ..rfc6749 import TokenValidator
|
||||
from .errors import (
|
||||
InvalidTokenError,
|
||||
InsufficientScopeError
|
||||
)
|
||||
|
||||
|
||||
class BearerTokenValidator(TokenValidator):
|
||||
TOKEN_TYPE = 'bearer'
|
||||
|
||||
def authenticate_token(self, token_string):
|
||||
"""A method to query token from database with the given token string.
|
||||
Developers MUST re-implement this method. For instance::
|
||||
|
||||
def authenticate_token(self, token_string):
|
||||
return get_token_from_database(token_string)
|
||||
|
||||
:param token_string: A string to represent the access_token.
|
||||
:return: token
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def validate_token(self, token, scopes, request):
|
||||
"""Check if token is active and matches the requested scopes."""
|
||||
if not token:
|
||||
raise InvalidTokenError(realm=self.realm, extra_attributes=self.extra_attributes)
|
||||
if token.is_expired():
|
||||
raise InvalidTokenError(realm=self.realm, extra_attributes=self.extra_attributes)
|
||||
if token.is_revoked():
|
||||
raise InvalidTokenError(realm=self.realm, extra_attributes=self.extra_attributes)
|
||||
if self.scope_insufficient(token.get_scope(), scopes):
|
||||
raise InsufficientScopeError()
|
||||
Reference in New Issue
Block a user