OAUTH2 Technical Summary

In this document you can find out information about OAUTH2 implementation in Fantastico framework. It is important to have read the previous sections before actually deep diving into technical details.

Overview

class fantastico.oauth2.oauth2_controller.OAuth2Controller(settings_facade, handler_factory_cls=<class 'fantastico.oauth2.grant_handler_factory.GrantHandlerFactory'>)[source]

This class provides the routes specified in OAUTH 2 specification (RFC6479 [http://tools.ietf.org/html/rfc6749]). A technical overview of OAuth2 implementation in Fantastico is presented below:

../../_images/oauth2_overview.png
handle_authorize(*args, **kwargs)[source]

This method provides the /authorize endpoint compliant with RFC6479 [http://tools.ietf.org/html/rfc6749] standard. Authorize endpoint provides an API for obtaining an access token or an authorization code depending on the grant type.

handle_token(request)[source]

This method provides the /token endpoint compliant with RFC6479 [http://tools.ietf.org/html/rfc6749]. Token endpoint provides an API for obtaining access tokens.

class fantastico.oauth2.middleware.tokens_middleware.OAuth2TokensMiddleware(app, tokens_service_cls=<class 'fantastico.oauth2.tokens_service.TokensService'>)[source]

This class provides a middleware responsible for decoding an access token (if exists) and building a security context. It is extremely import to configure this middleware to run after fantastico.middleware.request_middleware.RequestMiddleware and after fantastico.middleware.model_session_middleware.ModelSessionMiddleware because it needs a valid request and connection manager saved in the current pipeline execution.

class fantastico.oauth2.middleware.exceptions_middleware.OAuth2ExceptionsMiddleware(app, settings_facade_cls=<class 'fantastico.settings.SettingsFacade'>, exceptions_factory_cls=<class 'fantastico.exception_formatters.ExceptionFormattersFactory'>)[source]

This class provides the support for dynamically casting OAuth2 errors into concrete error responses. At the moment responses are returned only in english and have the format specified in RFC6749. Mainly, at each intercepted OAuth2 exceptions a json response is returned to the client.

class fantastico.oauth2.security_context.SecurityContext(access_token, required_scopes=None)[source]

This class provides the OAuth2 security context. Security context is available for each request and can be accessed using the following code snippet:

@Controller(url="/test/controller")
def handle_request(self, request):
    security_ctx = request.context.security

    # do something with security context
access_token[source]

This property returns the current access token passed to the current http request. Access token is already decoded as documented in OAUTH2 Fantastico Tokens (encrypted section).

required_scopes[source]

This property returns the current required scopes for http request. Required scopes are only available at runtime.

validate_context(attr_scope='scopes')[source]

This method tries to validate the current security context using the current access token and required scopes. Internally, the method simply ensures required scopes are present in access token granted scopes. Moreover, it receives an optional parameter which allows requester to decide what section of required scopes it wants to validates. Valid values are: scopes, create_scopes, read_scopes, update_scopes or delete_scopes.

Enforcing authorization

class fantastico.oauth2.oauth2_decorators.RequiredScopes(scopes=None, create=None, read=None, update=None, delete=None)[source]

This class provides the decorator for enforcing fantastico to authorize requests against ROA resources and MVC controllers.

# enforce authorization for MVC controllers.
@ControllerProvider()
class SecuredController(BaseController):
    @Controller(url="/secured-controller/ui/index")
    @RequiredScopes(scopes=["greet.verbose", "greet.read"])
    def say_hello(self, request):
        return "<html><body><h1>Hello world</body></html>"
# enforce authorization for ROA resources.
@Resource(name="app-setting", url="/app-settings", version=1.0)
@RequiredScopes(create="app_setting.create",
                read="app_setting.read",
                update="app_setting.update",
                delete="app_setting.delete"})
class AppSetting(BASEMODEL):
    id = Column("id", Integer, primary_key=True, autoincrement=True)
    name = Column("name", String(50), unique=True, nullable=False)
    value = Column("value", Text, nullable=False)

    def __init__(self, name, value):
        self.name = name
        self.value = value
create_scopes[source]

This property returns the scopes required for create calls.

delete_scopes[source]

This property returns the scopes required for delete calls.

inject_scopes_in_security(request)[source]

This method injects the request scopes into request security context.

read_scopes[source]

This property returns the scopes required for read calls.

scopes[source]

This property returns the currently set scopes (including create, read, update, delete).

update_scopes[source]

This property returns the scopes required for update calls.

Common tokens usage

Obtain authenticated user id

It is common in web applications to want to obtain the current authenticated user unique identifier so that additional information can be obtained in a secure context.

@Controller(url="^/users/ui/show-profile$")
def show_profile(self, request):
   security_ctx = request.context.security
   user_id = security_ctx.access_token.user_id

   # use profile endpoint to obtain additional information.

Obtain current granted scopes

It is common to want to access currently granted scopes for a given request. In order to do this use the following code snippet:

@Controller(url="^/sample-controller$")
@RequiredScopes(scopes=["custom_scope1.read"])
def handle_request(self, request):
   access_token = request.context.security.access_token
   scopes = access_token.scopes

   # validate scopes

If you try to use access token property of security context when no access token is expected this will be None.

Supported token generators

class fantastico.oauth2.token.Token(desc, immutable=True)[source]

This class provides a token model which can be built from a generic dictionary. All dictionary keys become token members.

class fantastico.oauth2.tokens_service.TokensService(db_conn, factory_cls=<class 'fantastico.oauth2.tokengenerator_factory.TokenGeneratorFactory'>, client_repo_cls=<class 'fantastico.oauth2.models.client_repository.ClientRepository'>, encryptor_cls=<class 'fantastico.oauth2.token_encryption.PublicTokenEncryption'>)[source]

This class provides an abstraction for working with all supported token types. Internally it uses fantastico.oauth2.tokengenerator_factory.TokenGeneratorFactory for obtaining a correct token generator. Then, it delegates all calls to that token generator.

db_conn[source]

This property returns the database connection used by this token service.

decrypt(encrypted_str)[source]

This method decrypts a given string and returns a concrete token object.

encrypt(token, client_id)[source]

This method encrypts a given token and returns the encrypted string representation. Client id is required in order to obtain the encryption keys.

generate(token_desc, token_type)[source]

This method generates a concrete token from the given token descriptor. It uses token_type in order to choose the right token generator.

# extract db_conn from one of your controller injected facade models.

# generate a new access token
tokens_service = TokensService(db_conn)

token_desc = {"client_id": "sample-client",
              "user_id": 123,
              "scopes": "scope1 scope2 scope3",
              "expires_in": 3600}
access_token = tokens_service.generate(token_desc, TokenGeneratorFactory.ACCESS_TOKEN)
invalidate(token)[source]

This method invalidates a given token object. For instance, authorization codes can be invalidated. In order to invalidate a token you can use the code snippet below:

# extract db_conn from one of your controller injected facade models.
# extract token from request or instantiate a new token.

tokens_service = TokensService(db_conn)
tokens_service.invalidate(token)
validate(token)[source]

This method validates a given token object. Internally, a generator is selected to validate the given token based on the given token type.

# extract db_conn from one of your controller injected facade models.
# extract token from request or instantiate a new token.

tokens_service = TokensService(db_conn)
tokens_service.validate(token)
class fantastico.oauth2.tokengenerator_factory.TokenGeneratorFactory[source]

This class provides the entry point for working with generators. It provides a factory for easily instantiating a generator which can work with a request token type.

login_generator = TokenGeneratorFactory().get_generator(TokenGeneratorFactory.LOGIN_TOKEN)
get_generator(token_type, db_conn)[source]

This method returns an instance of a token generator which can handel requested token type.

Parameters:
  • token_type (string) – A unique token type.
  • db_conn – An existing database connection (sql alchemy object) used when working with client context.
Returns:

An instance of a concrete token generator which is compatible with the request token type.

Return type:

fantastico.oauth2.token_generator.TokenGenerator

class fantastico.oauth2.token_generator.TokenGenerator(db_conn, model_facade_cls=<class 'fantastico.mvc.model_facade.ModelFacade'>)[source]

This class provides an abstract contract which must be provided by each concrete token generator. A token generator must provide the following functionality:

  • generate a new token
  • validate a given token
  • invalidate a given token
generate(token_desc)[source]

This method must be overriden so that it builds a correct token from the given descriptor. Descriptor is a free form object.

Parameters:token_desc (dict) – A dictionary containing all keys required for generating a new token.
Returns:A new token object.
Return type:fantastico.oauth2.token.Token
invalidate(token)[source]

This method must be overriden if the given token supports invalidation (e.g: authorization code). In many cases this is not necessary so this is a nop.

validate(token)[source]

This method must be overriden so that it validates the given token. Usually, if the token is not valid a concrete exception must be raised.

Parameters:token (fantastico.oauth2.token.Token) – The token object we want to validate.
class fantastico.oauth2.logintoken_generator.LoginTokenGenerator(db_conn, model_facade_cls=<class 'fantastico.mvc.model_facade.ModelFacade'>)[source]

This class provides support for generating and working with login tokens. A login token is used for proving that a user is authenticated correctly. For more information, read OAUTH2 Fantastico Tokens.

generate(token_desc, time_provider=<module 'time' (built-in)>)[source]

This method generates a login token. In order to succeed token descriptor must contain the following keys:

  • client_id - a unique identifier for the idp which generated the token.
  • user_id - idp user unique identifier.
  • expires_in - an integer value in seconds determining the maximum validity of the token.

If any of the above keys are missing an oauth 2 exception is raised.

validate(token)[source]

This method checks the given login token for:

  • correct type (login).
  • expiration time.
class fantastico.oauth2.accesstoken_generator.AccessTokenGenerator(db_conn, model_facade_cls=<class 'fantastico.mvc.model_facade.ModelFacade'>)[source]

This class provides the methods for working with access tokens: (generate and validate).

generate(token_desc, time_provider=<module 'time' (built-in)>)[source]

This method generates a new access token starting from the givent token descriptor. In order to succeed the token descriptor must contain the following keys:

  • client_id - Client unique identifier.
  • user_id - User unique identifier.
  • scopes - The scopes requested for this client (a space delimited list of strings).
  • expires_in - The time to live period (in seconds) for the newly generated access token.
validate(token)[source]

This method validates a given access token. It checks for:

  • valid client id
  • valid token type
  • token not expired

Encryption / decryption

In Fantastico OAuth2, tokens are encrypted / decrypted using AES symmetric encryption. Below you can find the classes which provides AES implementation:

class fantastico.oauth2.token_encryption.TokenEncryption[source]

This class provides an abstract model for token encryption providers. A token encryption provider must be able to encrypt / decrypt a fantastico.oauth2.token.Token objects.

decrypt_token(encrypted_str, token_iv, token_key)[source]

This method must be overriden by concrete providers in order to correctly transform an encrypted string into a token object.

Parameters:
  • encrypted_str (str) – Encrypted token representation.
  • token_iv (byte[]) – Token initialization vector used in symmetric encryption. Most of the times this will have a fix 128 bits length.
  • token_key (byte[]) – Token key used in symmetric encryption. Based on the implementation the length might vary: 128 / 192 / 256 bits.
Returns:

Decrypted token object.

Return type:

fantastico.oauth2.token.Token

encrypt_token(token, token_iv, token_key)[source]

This method must be overriden by concrete providers in order to correctly transform a token object into an encrypted string.

Parameters:
  • token (fantastico.oauth2.token.Token) – A token object we want to encrypt.
  • token_iv (byte[]) – Token initialization vector used in symmetric encryption. Most of the times this will have a fix 128 bits length.
  • token_key (byte[]) – Token key used in symmetric encryption. Based on the implementation the length might vary: 128 / 192 / 256 bits.
Returns:

The encrypted representation of the token.

Return type:

str

class fantastico.oauth2.token_encryption.AesTokenEncryption[source]

This class provides a generic AES token encryption provider. It allows developers to specify the number of bits used for AES (128 / 192 / 256 bits).

decrypt_token(encrypted_str, token_iv, token_key)[source]

This method uses AES for decrypting the given string. Internally, decrypted string is converted into a dictionary and then into a concrete token object.

encrypt_token(token, token_iv, token_key)[source]

This method uses AES for encrypting the given token. Internally it transform the token into a JSON string and encrypt it using given token_iv and token_key.

class fantastico.oauth2.token_encryption.PublicTokenEncryption(symmetric_encryptor)[source]

This class provides a special token encryption: a mix of base64 encoded and symmetrical encrypted token. We need this mix because client_id is required for every operation involving oauth2 tokens.

decrypt_token(encrypted_str, token_iv=None, token_key=None, client_repo=None)[source]

This methods receives a public token representation and returns a concrete token object. In many cases token_iv and token_key will not be known so they will obtained from the public part of the token using client_id descriptor persisted in database.

encrypt_token(token, token_iv=None, token_key=None, client_repo=None)[source]

This method takes a concrete token object and returns a base64 representation of the token. In the rare cases where the encryption vectors are not known client_repo is used to read client descriptor and lazy obtain the vectors.

Suported grant types

class fantastico.oauth2.grant_handler.GrantHandler(tokens_service, settings_facade)[source]

This class provides the abstract contract of a handler. Each concrete handler must implement this contract in order to correctly extend Fantastico OAuth2 supported handlers.

handle_grant(request)[source]

This method must be overriden in order to correctly implement grant logic. It receives the current http request and return a http response.

class fantastico.oauth2.grant_handler_factory.GrantHandlerFactory(tokens_service_cls=<class 'fantastico.oauth2.tokens_service.TokensService'>, settings_facade_cls=<class 'fantastico.settings.SettingsFacade'>)[source]

This class provides a factory which can be used to obtain a concrete grant handler. Below you can find a code snippet for obtaining and implicit grant type handler:

grant_handler = GrantHandlerFactory().get_handler(GrantHandlerFactory.IMPLICIT_GRANT)
get_handler(handler_type, db_conn)[source]

This method builds a grant handler which matches requested handler_type.

Parameters:
  • handler_type (str) – A string value describing the grant_type which must be handled.
  • db_conn (Connection) – A db connection active session which cn be used.
Returns:

A concrete grant handler instance.

Return type:

fantastico.oauth2.grant_handler.GrantHandler

class fantastico.oauth2.implicit_grant_handler.ImplicitGrantHandler(tokens_service, settings_facade, exception_formatters_cls=<class 'fantastico.exception_formatters.ExceptionFormattersFactory'>, client_repo_cls=<class 'fantastico.oauth2.models.client_repository.ClientRepository'>)[source]

This class provides the implementation for implicit grant type described in RFC6749 [http://tools.ietf.org/html/rfc6749]. Implementation of this grant type is fully compliant with OAuth2 spec. In addition to RFC6749, the implicit grant implemented in fantastico supports an additional query parameter named redirect which is optional. If redirect query parameter is specified with value 0 then a 200 OK response is returned to the client in comparison with redirect 1 value where 302 Found response is returned. We do this in order to support more advanced use cases where user can orchestrate the redirects rather than browser.

handle_grant(request)[source]

This method provides the algorithm for implementing implicit grant type handler. Internally it will use TokensService in order to generate a new access token. In addition, if redirect query parameter is set to false then a 200 OK response with Location header and no body is sent to the client.

Concrete exceptions

class fantastico.oauth2.exceptions.OAuth2Error(error_code=12000, msg=None, http_code=400)[source]

This class provides the base class for OAuth2 exceptions. In order to be compliant with OAuth2 spec [http://tools.ietf.org/html/rfc6749] each oauth error is described by a status code, an error code and a friendly description.

error_code[source]

This property returns the exception error code.

class fantastico.oauth2.exceptions.OAuth2InvalidTokenDescriptorError(attr_name)[source]

This class provides a concrete exception used to notify a missing attribute from a token descriptor.

attr_name[source]

This property returns the missing attribute name.

class fantastico.oauth2.exceptions.OAuth2InvalidTokenTypeError(token_type, msg)[source]

This class provides a concrete exception used to notify that a token has been sent to a token generator which does not support it or the token type is unknown.

token_type[source]

This property returns the invalid token type.

class fantastico.oauth2.exceptions.OAuth2TokenExpiredError(msg=None)[source]

This class provides a concrete exception used to notify that a token is expired.

class fantastico.oauth2.exceptions.OAuth2InvalidClientError(msg)[source]

This class provides a concrete exception used to notify an invalid client (not found or revoked).

class fantastico.oauth2.exceptions.OAuth2InvalidScopesError(msg)[source]

This class provides a concrete exception used to notify that a client is not allowed to use a request set of scopes.

class fantastico.oauth2.exceptions.OAuth2MissingQueryParamError(param_name)[source]

This class provides a concrete exception used to notify a missing query parameter from an OAuth2 endpoint.

param_name[source]

This property return the name of the query parameter which is missing.

class fantastico.oauth2.exceptions.OAuth2TokenEncryptionError(msg)[source]

This class provides a concrete exception used to notify an error during encrypt / decrypt token operations.

class fantastico.oauth2.exceptions.OAuth2UnsupportedGrantError(handler_type)[source]

This class provides a concrete exception for notifying unsupport oauth2 grant type.

handler_type[source]

This property holds the unsupported grant type name.

class fantastico.oauth2.exceptions.OAuth2UnauthorizedError(msg)[source]

This class provides a concrete exception for notifying unauthorized access to oauth2 protected resources.

class fantastico.oauth2.exceptions.OAuth2AuthenticationError(msg, http_code=403)[source]

This class provides a concrete exception used to notify a failed authentication attempt from an OAuth2 IDP.