Metadata-Version: 2.1
Name: FlaskSimpleAuth
Version: 2.1.0
Summary: Simple authentication, authorization and parameters for Flask, emphasizing configurability
Home-page: https://github.com/zx80/flask-simple-auth
Author: Fabien Coelho
Author-email: flask.auth@coelho.net
License: UNKNOWN
Description: # Flask Simple Auth
        
        Simple authentication, authorization, parameter checks and utils
        for [Flask](https://flask.palletsprojects.com/), controled from
        Flask configuration and the extended `route` decorator.
        
        
        ## Example
        
        The application code below performs authentication, authorization and
        parameter checks triggered by the extended `route` decorator.
        There is no clue in the source about what kind of authentication is used,
        which is the whole point: authentication schemes are managed in the configuration,
        not explicitely in the application code.
        The authorization rule is declared explicitely on each function with the
        `authorize` parameter.
        Path and HTTP/JSON parameters are type checked and converted automatically
        based on type annotations.
        Basically, you just have to implement a type-annotated Python function and
        most of the crust is managed by Flask and FlaskSimpleAuth.
        
        ```Python
        from FlaskSimpleAuth import Flask
        app = Flask("demo")
        app.config.from_envvar("DEMO_CONFIG")
        
        # register hooks
        # user_to_password_fun is a function returning the hashed password for a user.
        app.get_user_pass(user_to_password_fun)
        # user_in_group_fun is a function telling whether a user is in a group.
        app.user_in_group(user_in_group_fun)
        
        # users belonging to the "patcher" group can patch "whatever/*"
        # the function gets 3 arguments: one int coming from the path (id)
        # and the remaining two coming from request parameters (some, stuff).
        # "some" is mandatory, stuff is optional because it has a default.
        @app.route("/whatever/<id>", methods=["PATCH"], authorize="patcher")
        def patch_whatever(id: int, some: int, stuff: str = "wow"):
            # ok to do it, with parameters id, some & stuff
            return "", 204
        ```
        
        Authentication is manage from the application flask configuration
        with `FSA_*` (Flask simple authentication) directives:
        
        ```Python
        FSA_TYPE = "httpd"     # inherit web-serveur authentication
        # or others such as: basic, token (eg jwt), param…
        ```
        
        If the `authorize` argument is not supplied, the security first approach
        results in the route to be forbidden (*403*).
        
        Various aspects of the implemented schemes can be configured with other
        directives, with reasonable defaults provided so that not much is really
        needed beyond choosing the authentication scheme.
        
        
        ## Description
        
        This module helps managing authentication, authorizations and parameters
        in a Flask REST application.
        
        **Authentication** is available through the `get_user` function.
        It is performed on demand when the function is called, automatically when
        checking for permissions in a per-role authorization model, or possibly
        forced for all/most paths.
        The module implements inheriting the web-server authentication,
        password authentication (HTTP Basic, or HTTP/JSON parameters),
        authentication tokens (custom or jwt passed in headers or as a
        parameter), and a fake authentication scheme useful for application testing.
        It allows to have a login route to generate authentication tokens.
        For registration, support functions allow to hash new passwords consistently
        with password checks.
        
        **Authorization** are managed by declaring permissions on a route (eg a role name),
        and relies on a supplied function to check whether a user has this role.
        This approach is enough for simple authorization management, but would be
        insufficient for realistic applications where users can edit their own data
        but not those of others.
        An additional feature is that the application aborts requests on routes
        for which there is no explicit authorization declarations, allowing to
        catch forgotten requirements.
        
        **Parameters** expected in the request can be declared, their presence and type
        checked, and they are added automatically as named parameters to route functions,
        skipping the burden of checking them in typical REST functions. In practice,
        importing Flask's `request` global variable is not necessary.
        
        **Utils** include the convenient `Reference` class which allows to share for
        import an unitialized variable, and the `CacheOK` decorator to memoize true
        answers (eg for user/group checks).
        
        
        ## Documentation
        
        ### Install
        
        Use `pip install FlaskSimpleAuth` to install the module, or whatever
        other installation method you prefer.
        
        Depending on options, the following modules should be installed:
        
        - [passlib](https://pypi.org/project/passlib/) for password management
        - [bcrypt](https://pypi.org/project/bcrypt/)  for password hashing (default algorithm)
        - [PyJWT](https://pypi.org/project/PyJWT/) for JSON Web Token (JWT)
        - [cryptography](https://pypi.org/project/cryptography/) for pubkey-signed JWT
        
        ### Features
        
        The module provides a wrapper around the `Flask` class which
        extends its capabilities for managing authentication, authorization and
        parameters.
        
        This is intended for a REST API implementation serving a remote client application.
        It does not make much sense to "login" and "logout" to/from a REST API
        because the point of the API is to serve and collect data
        to all who deserve it, i.e. are authorized, unlike a web application
        which is served while the client is on the page which maintains a session
        and should disappear when disconnected as the web browser page is wiped out.
        However, there is still a "login" concept which is only dedicated at
        obtaining an auth token, that the application client needs to update from
        time to time.
        
        You should also consider the many options provided by
        [Flask HTTPAuth](https://github.com/miguelgrinberg/Flask-HTTPAuth).
        However, it cannot be easily configured to change authentication methods.
        Also, this module performs authentication before any user code is executed.
        It also adds a convenient management of request parameters.
        
        Note that web-oriented flask authentication modules are not really
        relevant in the REST API context, where the server does not care about
        presenting login forms for instance.
        
        ### Initialisation
        
        The module is simply initialize by calling its `Flask` constructor
        and providing a configuration through `FSA_*` directives, or possibly
        by calling some methods to register helper functions.
        
         - a function to retrieve the password hash from the user name.
         - a function which tells whether a user is in a group or role.
        
        ```Python
        from FlaskSimpleAuth import Flask
        app = Flask("test")
        app.config.from_envvar("TEST_CONFIG")
        
        # register hooks
        
        # return password hash if any, or None
        @app.get_user_pass
        def get_user_pass(user):
            return …
        
        # return whether user is in group
        @app.user_in_group
        def user_in_group(user, group):
            return …
        
        # they can also be provided in the Flask configuration with
        # - FSA_GET_USER_PASS
        # - FSA_USER_IN_GROUP
        ```
        
        Once initialized `app` is a standard Flask object with some additions:
        
        - `route` decorator, an extended version of Flask's own.
        - `user_in_group` and `get_user_pass` methods/decorator to register helper functions.
        - `get_user` to extract the authenticated user or raise an `AuthException`.
        - `current_user` to get the authenticated user if any, or `None`
        - `hash_password` and `check_password` to hash or check a password.
        - `create_token` to compute a new authentication token for the current user.
        
        Alternatively, it is possible to use the flask extension model, in which case
        the `FlaskSimpleAuth` object must be instanciated and routes must be created
        using this object:
        
        ```Python
        from flask import Flask
        app = Flask("demo")
        app.config.from_envvar("DEMO_CONFIG")
        
        from FlaskSimpleAuth import FlaskSimpleAuth, ALL
        fsa = FlaskSimpleAuth(app)
        
        # imaginary blueprint registration on the fsa object:
        from DemoAdmin import abp
        fsa.register_blueprint(abp, url_path="/admin")
        
        # define a route with an optional paramater "flt"
        @fsa.route("/users", methods=["GET"], authorize=ALL)
        def get_what(flt: str = None):
            …
        ```
        
        ### Using Authentication, Authorization and Parameter Check
        
        The authentication, authorization and parameter chechs are managed
        automatically through the extented `route` decorator.
        
        **Authentication** is transparently activated and controlled by many
        configuration directives as described in the next section.
        
        **Authorization** is managed through the added `authorize` parameter
        to the `route` decorator.
        Three special group names are available in the module: `ANY`
        to declare a route opened to anyone, `NONE` to close a route (eg
        temporarily) and `ALL` for all authenticated users.
        If the authorize directive is absent or empty, the route is forbidden (*403*).
        Note that more advanced permissions (eg users can edit themselves) will
        still require manual permission checks at the beginning of the function.
        
        **Parameters** are managed transparently, either coming from the route path
        or from HTTP/JSON parameters. Type conversion are performed based on
        type annotations for all parameters. Parameters with default values are
        optional, those without are mandatory.
        
        ```Python
        @app.route("/somewhere/<stuff>", methods=["POST"], authorize="posters")
        def post_somewhere(stuff: str, nstuff: int, bstuff: bool = False):
            …
        ```
        
        An opened route for user registration with mandatory parameters
        could look like that:
        
        ```Python
        # with FSA_SKIP_PATH = (r"/register", …)
        @app.route("/register", methods=["POST"], authorize=ANY)
        def post_register(user: str, password: str):
            if user_already_exists_somewhere(user):
                return f"cannot create {user}", 409
            add_new_user_with_hashed_pass(user, app.hash_password(password))
            return "", 201
        ```
        
        For `token` authentication, a token can be created on a path authenticated
        by one of the other methods. The code for that would be:
        
        ```Python
        # token creation route for all registered users
        @app.route("/login", methods=["GET"], authorize=ALL)
        def get_login():
            return jsonify(app.create_token(app.get_user())), 200
        ```
        
        The client application will return the token as a parameter or in
        headers for authenticating later requests, till it expires.
        
        
        ### Authentication
        
        Three directives impact how and when authentication is performed.
        The main configuration directive is `FSA_TYPE` which governs authentication
        methods used by the `get_user` function, as described in the following sections.
        
        - `FSA_TYPE` governs the *how*: `none`, `httpd`, `basic`, `param`, `password`,
        `token`… as described in details in the next sections.  Default is `httpd`.
        
        - `FSA_ALWAYS` tells whether to perform authentication in a before request
        hook. Default is *True*.  On authentication failures *401* are returned.
        Once in a route function, `get_user` will always return the authenticated
        user and cannot fail.
        
        - `FSA_SKIP_PATH` is a list of regular expression patterns which are matched
        against the request path for skipping systematic authentication when
        `FSA_ALWAYS` is enabled.  Default is empty, i.e. authentication is applied
        for all paths.
        
        - `FSA_LAZY` tells whether to attempt authentication lazily when checking an
        authorization through a `authorize` decorator or argument to the `route`
        decorator.
        Default is *True*.
        
        - `FSA_CHECK` tells whether to generate a *500* internal error if a route
        is missing an explicit authorization check.
        Default is *True*.
        
        
        #### `httpd` Authentication
        
        Inherit web server supplied authentication through `request.remote_user`.
        This is the default.
        
        There are plenty authentication schemes available in a web server
        such as Apache or Nginx, all of which probably more efficiently implemented
        than python code, so this should be the preferred option.
        However, it could require significant configuration effort compared to
        the application-side approach.
        
        #### `none` Authentication
        
        Use to disactivate authentication.
        
        #### `basic` Authentication
        
        HTTP Basic password authentication, which rely on the `Authorization`
        HTTP header in the request.
        
        See also Password Authentication below for how the password is retrieved
        and checked.
        
        #### `param` Authentication
        
        HTTP parameter or JSON password authentication.
        User name and password are passed as request parameters.
        
        The following configuration directives are available:
        
         - `FSA_PARAM_USER` parameter name for the user name.
           Default is `USER`.
         - `FSA_PARAM_PASS` parameter name for the password.
           Default is `PASS`.
        
        See also Password Authentication below for how the password is retrieved
        and checked.
        
        #### `password` Authentication
        
        Tries `basic` then `param` authentication.
        
        #### `token` Authentication
        
        Only rely on signed tokens for authentication.
        A token certifies that a *user* is authenticated in a *realm* up to some
        time *limit*.
        The token is authenticated by a signature which is the hash of the payload
        (*realm*, *user* and *limit*) and a secret hold by the server.
        
        There are two token types chosen with the `FSA_TOKEN_TYPE` configuration
        directive: `fsa` is a compact custom format, and `jwt`
        [RFC 7519](https://tools.ietf.org/html/rfc7519) standard based
        on [PyJWT](https://pypi.org/project/PyJWT/) implementation.
        
        The `fsa` token syntax is: `<realm>:<user>:<limit>:<signature>`,
        for instance: `kiva:calvin:20210221160258:4ee89cd4cc7afe0a86b26bdce6d11126`.
        The time limit is an easily parsable UTC timestamp *YYYYMMDDHHmmSS* so that
        it can be checked easily by the application client.
        Compared to `jwt` tokens, they are easy to interpret manually, no
        decoding is involved.
        
        The following configuration directives are available:
        
         - `FSA_TOKEN_TYPE` type of token, either *fsa*, *jwt* or `None` to disable.
           Default is *fsa*.
         - `FSA_TOKEN_REALM` realm of token.
           Default is the simplified lower case application name.
           For *jwt*, this is translated as the audience.
         - `FKA_TOKEN_NAME` name of parameter holding the auth token, or
           *None* to use a *bearer* authorization header.
           Default is *None*.
         - `FSA_TOKEN_SECRET` secret string used for validating tokens.
           Default is a system-generated random string containing 256 bits.
           This default with only work with itself, as it is not shared
           across server instances or processes.
         - `FSA_TOKEN_SIGN` secret string used for signing tokens, if
           different from previous secret. This is only relevant for public-key
           *jwt* schemes (`R…`, `E…`, `P…`).
           Default is to use the previous secret.
         - `FSA_TOKEN_DELAY` number of minutes of token validity.
           Default is *60* minutes.
         - `FSA_TOKEN_GRACE` number of minutes of grace time for token validity.
           Default is *0* minutes.
         - `FSA_TOKEN_ALGO` algorithm used to sign the token.
           Default is `blake2s` for `fsa` and `HS256` for *jwt*.
         - `FSA_TOKEN_LENGTH` number of hash bytes kept for token signature.
           Default is *16* for `fsa`. The directive is ignored for `jwt`.
        
        Function `create_token(user)` creates a token for the user depending
        on the current scheme.
        
        Note that token authentication is always attempted unless the secret is empty.
        Setting `FSA_TYPE` to `token` results in *only* token authentication to be used.
        
        Also note that token authentication is usually much faster than password verification
        because password checks are designed to be slow so as to hinder password cracking.
        Another benefit of token is that it avoids sending passwords over and over.
        The rational option is to use a password scheme to retrieve a token and then to
        use it till it expires.
        
        #### `fake` Authentication
        
        Trust a parameter for authentication claims.
        Only for local tests, obviously.
        This is inforced.
        
        The following configuration directive is available:
        
         - `FSA_FAKE_LOGIN` name of parameter holding the user name.
           Default is `LOGIN`.
        
        #### Password Authentication (`param` or `basic`)
        
        For checking passwords the password (salted hash) must be retrieved through
        `get_user_pass(user)`.
        This function must be provided by the application when the module is initialized.
        
        The following configuration directives are available to configure
        `passlib` password checks:
        
         - `FSA_PASSWORD_SCHEME` password scheme to use for passwords.
           Default is `bcrypt`.
           See [passlib documentation](https://passlib.readthedocs.io/en/stable/lib/passlib.hash.html)
           for available options.
           Set to `None` to disable password checking.
         - `FSA_PASSWORD_OPTIONS` relevant options (for `passlib.CryptContext`).
           Default is `{'bcrypt__default_rounds': 4, 'bcrypt__default_ident': '2y'}`.
        
        Beware that modern password checking is often pretty expensive in order to
        thwart password cracking if the hashed passwords are leaked, so that you
        do not want to have to use that on every request in real life (eg hundreds
        milliseconds for passlib bcrypt *12* rounds).
        The above defaults result in manageable password checks of a few milliseconds.
        Consider enabling tokens to reduce the authentication load on each request.
        
        Function `hash_password(pass)` computes the password salted digest compatible
        with the current configuration.
        
        
        ### Authorization
        
        Role-oriented authorizations are managed through the `authorize` parameter to
        the `route` decorator, which provides a just one or possibly a list of roles
        authorized to call a route. A role is identified as an integer or a string.
        The check calls `user_in_group(user, group)` function to check whether the
        authenticated user belongs to any of the authorized roles.
        
        There are three special values that can be passed to the `authorize` decorator:
        
         - `ANY` declares that no authentication is needed on that route.
         - `ALL` declares that all authenticated user can access this route.
         - `NONE` returns a *403* on all access. It can be used to close a route
           temporarily. This is the default.
        
        The following configuration directive is available:
        
         - `FSA_LAZY` allows the `authorize` decorator to perform the authentication
           when needed, which mean that the before request hook can be skipped.
           Default is *True*.
        
        Note that this simplistic model does is not enough for non-trivial applications,
        where permissions on objects often depend on the object owner.
        For those, careful per-operation authorization will still be needed.
        
        
        ### Parameters
        
        Request parameters (HTTP or JSON) are translated automatically to
        function parameters, by relying on function type annotations.
        
        By default, the decorator guesses whether parameters are mandatory based on
        provided default values, i.e. they are optional when a default is provided.
        
        Request parameter string values are converted to the target type.
        For `int`, base syntax is accepted, i.e. `0x11`, `0b10001` and `17`
        all mean decimal *17*.
        For `bool`, *False* is an empty string, `0`, `False` or `F`, otherwise
        the value is *True*.
        Type `path` is a special `str` type which allow to trigger accepting
        any path on a route.
        
        The `required` parameter allows to declare whether all parameters
        must be set (when *True*), or whether they are optional (*False*) in which
        case *None* values are passed if no defaults are given, or if this is
        guessed (when *None*, the default).
        
        The `allparams` parameter makes all request parameters be translated to
        named function parameters that can be manipulated as such, as shown below:
        
        ```Python
        @app.route("/awesome", methods=["PUT"], authorize=ALL, allparams=True)
        def put_awesome(**kwargs):
            …
        ```
        
        A side-effect of passing of request parameters as named function parameters
        is that request parameter names must be valid python identifiers,
        which excludes keywords such as `pass`, `def` or `for`, unless passed
        as keyword arguments.
        
        Custom classes can be used as path and HTTP parameter types, provided that
        the constructor accepts a string to convert the parameter value to the
        expected type.
        
        ```Python
        class EmailAddr:
            def __init__(self, addr: str):
                self._addr = addr
        
        @app.route("/mail/<addr>", methods=["GET"], authorize=ALL)
        def get_mail_addr(addr: EmailAddr):
            …
        ```
        
        
        ## `Reference` Object Wrapper
        
        This class implements a generic share-able global variable which can be
        shared between modules (eg apps, blueprints…) with its initialization
        differed:
        
        ```Python
        # file Shared.py
        from FlaskSimpleAuth import Reference
        stuff = Reference()
        init_app(**conf):
            stuff._setobj(…)
        ```
        
        Then in a blueprint:
        
        ```Python
        # file SubStuff.py
        from FlaskSimpleAuth import Blueprint, ALL
        from Shared import stuff
        
        sub = Blueprint(…)
        
        @sub.add("/stuff", authorize=ALL):
        def get_stuff():
            return str(stuff), 200
        ```
        
        Then in the app itself:
        
        ```Python
        # file App.py
        from FlaskSimpleAuth import Flask
        app = Flask(__name__)
        
        from SubStuff import sub
        app.register_blueprint(sub, url_prefix="/sub")
        
        # deferred "stuff" initialization
        import Shared
        Shared.init_app(…)
        
        …
        ```
        
        
        ## `CacheOK` Decorator
        
        This decorator memorize the underlying function true answers, but keep trying
        on false answers. Call `cache_clear` to reset cache.
        
        ```Python
        @CacheOK
        def user_in_group(user, group):
            return …
        ```
        
        
        ## Versions
        
        Sources are available on [GitHub](https://github.com/zx80/flask-simple-auth)
        and packaged on [PyPI](https://pypi.org/project/FlaskSimpleAuth/).
        Software license is *public domain*.
        
        ### 2.1.0
        
        Add `Reference` any object wrapper class.
        Add `CacheOK` positive caching decorator.
        Add `current_user` function.
        Add `none` authentication type.
        Add `path` parameter type.
        Add more tests.
        
        ### 2.0.0
        
        Make the module as an extension *and* a full `Flask` wrapper.
        Advertise only the extended `route` decorator in the documentation
        (though others are still used internally).
        Change passlib bcrypt version to be compatible with Apache httpd.
        Allow disabling password checking.
        Rename `FSA_TOKEN_HASH` as `FSA_TOKEN_ALGO`.
        Disable tokens by setting their type to `None`.
        Import Flask `session`, `redirect`, `url_for`, `make_response`,
        `abort`, `render_template`, `current_app` objects.
        Add parameter support for `date`, `time` and `datetime` in iso format.
        Allow to use any type as path parameters, not just Flask predefined ones.
        Make blueprints work.
        Add special `path` type for parameters taken from the path.
        
        ### 1.9.0
        
        Add *bearer* authorization for tokens and make it the default.
        Add *JWT* tokens, both hmac and pubkey variants.
        Add *500* generation if a route is missing an authorization declaration.
        Add convenient `route` decorator.
        Add type inference for HTTP/JSON parameters based on default value, when provided.
        Add type inference for root path parameters based on function declaration.
        
        ### 1.8.1
        
        Fix typo in distribution configuration file.
        
        ### 1.8.0
        
        Merge `autoparams` and `parameters` decorators into a single `parameters`
        decorator.
        Make it guess optional parameters based on default values.
        Fix conversion issues with boolean type parameters.
        Enhance integer type to accept other base syntaxes.
        Improve documentation to advertise the simple and elegant approach.
        Implement decorator with functions instead of a class.
        
        ### 1.7.0
        
        Simplify code.
        Add `FSA_ALWAYS` configuration directive and move the authentication before request
        hook logic inside the module.
        Add `FSA_SKIP_PATH` to skip authentication for some paths.
        Update documentation to reflect this simplified model.
        Switch all decorators to functions.
        
        ### 1.6.0
        
        Add `autoparams` decorator with required or optional parameters.
        Add typed parameters to `parameters` decorator.
        Make `parameters` pass request parameters as named function parameters.
        Simplify `authorize` decorator syntax and implementation.
        Advise `authorize` *then* `parameters` or `autoparams` decorator order.
        Improved documentation.
        
        ### 1.5.0
        
        Flask *internal* tests with a good coverage.
        Switch to `setup.cfg` configuration.
        Add convenient `parameters` decorator.
        
        ### 1.4.0
        
        Add `FSA_LAZY` configuration directive.
        Simplify code.
        Improve warning on short secrets.
        Repackage…
        
        ### 1.3.0
        
        Improved documentation.
        Reduce default token signature length and default token secret.
        Warn on random or short token secrets.
        
        ### 1.2.0
        
        Add grace time for auth token validity.
        Some code refactoring.
        
        ### 1.1.0
        
        Add after request module cleanup.
        
        ### 1.0.0
        
        Add `authorize` decorator.
        Add `password` authentication scheme.
        Improved documentation.
        
        ### 0.9.0
        
        Initial release in beta.
        
        
        ## TODO
        
        Hmmm…
        
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
Classifier: Programming Language :: Python
Classifier: Environment :: Web Environment
Classifier: Framework :: Flask
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Description-Content-Type: text/markdown
