Metadata-Version: 2.1
Name: more.jwtauth
Version: 0.12
Summary: JWT Access Auth Identity Policy for Morepath
Home-page: https://github.com/morepath/more.jwtauth
Author: Morepath developers
Author-email: morepath@googlegroups.com
License: BSD
Description: more.jwtauth: JWT Authentication integration for Morepath
        =========================================================
        
        Overview
        --------
        
        This is a Morepath_ authentication extension for the JSON Web Token (JWT)
        Authentication.
        
        For more information about JWT, see:
        
        -  `JSON Web Token draft`_ - the official JWT draft
        -  `Auth with JSON Web Tokens`_ - an interesting blog post by José Padilla
        
        To access resources using JWT Access Authentication, the client must have
        obtained a JWT to make signed requests to the server.
        The Token can be opaque to client, although, unless it is encrypted,
        the client can read the claims made in the token.
        
        JWT validates the authenticity of the claimset using the signature.
        
        This plugin uses the `PyJWT library`_ from José Padilla for verifying JWTs.
        
        Introduction
        ------------
        
        The general workflow of JWT Access Authentication:
            * After the client has sent the login form we check if the user
              exists and if the password is valid.
            * In this case more.jwtauth generates a JWT token including all
              information in a claim set and send it back to the client inside
              the HTTP authentication header.
            * The client stores it in some local storage and send it back in the
              authentication header on every request.
            * more.jwtauth validates the authenticity of the claim set using the
              signature included in the token.
            * The logout should be handled by the client by removing the token and
              making some cleanup depending on the implementation.
        
        You can include all necessary information about the identity in the token
        so JWT Access Authentication can be used by a stateless service e.g. with
        external password validation.
        
        
        Requirements
        ------------
        
        -  Python (3.4, 3.5, 3.6, 3.7, 3.8)
        -  morepath (>= 0.19)
        -  PyJWT (1.7.1)
        -  optional: cryptography (2.9.2)
        
        .. Note::
           If you want to use another algorithm than HMAC (HS*), you need to install
           cryptography.
           On some systems this can be a little tricky. Please follow the instructions
           in https://cryptography.io/en/latest/installation.
        
        
        Installation
        ------------
        
        You can use pip for installing more.jwtauth:
        
        * ``pip install -U more.jwtauth[crypto]`` - for installing with cryptography
        * ``pip install -U more.jwtauth`` - installing without cryptography
        
        
        Usage
        -----
        
        For a basic setup just set the necessary settings including a key or key file
        and pass them to JWTIdentityPolicy:
        
        .. code-block:: python
        
            import morepath
            from more.jwtauth import JWTIdentityPolicy
        
        
            class App(morepath.App):
                pass
        
        
            @App.setting_section(section="jwtauth")
            def get_jwtauth_settings():
                return {
                    # Set a key or key file.
                    'master_secret': 'secret',
        
                    # Adjust the settings which you need.
                    'leeway': 10
                }
        
        
            @App.identity_policy()
            def get_identity_policy(settings):
                # Get the jwtauth settings as a dictionary.
                jwtauth_settings = settings.jwtauth.__dict__.copy()
        
                # Pass the settings dictionary to the identity policy.
                return JWTIdentityPolicy(**jwtauth_settings)
        
        
            @App.verify_identity()
            def verify_identity(identity):
                # As we use a token based authentication
                # we can trust the claimed identity.
                return True
        
        The login can be done in the standard Morepath way. You can add extra
        information about the identity, which will be stored in the JWT token
        and can be accessed through the morepath.Identity object:
        
        .. code-block:: python
        
            class Login:
                pass
        
        
            @App.path(model=Login, path='login')
            def get_login():
                return Login()
        
        
            @App.view(model=Login, request_method='POST')
            def login(self, request):
                username = request.POST['username']
                password = request.POST['password']
        
                # Here you get some extra user information.
                email = request.POST['email']
                role = request.POST['role']
        
                # Do the password validation.
                if not user_has_password(username, password):
                    raise HTTPProxyAuthenticationRequired('Invalid username/password')
        
                @request.after
                def remember(response):
                    # We pass the extra info to the identity object.
                    identity = morepath.Identity(username, email=email, role=role)
                    request.app.remember_identity(response, request, identity)
        
                return "You're logged in."  # or something more fancy
        
        Don't use reserved claim names as "iss", "aud", "exp", "nbf", "iat", "jti",
        "refresh_until", "nonce" or the user_id_claim (default: "sub", see settings_).
        They will be silently ignored.
        
        Advanced:
            For testing or if we want to use some methods of the JWTIdentityPolicy
            class directly we can pass the settings as arguments to the class:
        
            .. code-block:: python
        
                identity_policy = JWTIdentityPolicy(
                    master_secret='secret',
                    leeway=10
                )
        
        
        Refreshing the token
        --------------------
        
        There are some risks related with using long-term tokens:
        
        * If you use a stateless solution the token contains user data which
          could not be up-to-date anymore.
        * If a token get compromised there's no way to destroy sessions server-side.
        
        A solution is to use short-term tokens and refresh them either just before
        they expire or even after until the ``refresh_until`` claim not expires.
        
        To help you with this more.jwtauth has a refresh API, which uses 4 settings:
        
        * ``allow_refresh``: Enables the token refresh API when True.
            Default is False
        * ``refresh_delta``: The time delta in which the token can be refreshed
            considering the leeway.
            Default is 7 days. When None you can always refresh the token.
        * ``refresh_nonce_handler``: Either dotted path to callback function or the
            callback function itself, which receives the current request and the userid
            as arguments and returns a nonce which will be validated before refreshing.
            When None no nonce will be created or validated for refreshing.
        * ``verify_expiration_on_refresh``: If False, expiration_delta for the JWT
            token will not be checked during refresh. Otherwise you can refresh the
            token only if it's not yet expired. Default is False.
        
        When refreshing is enabled by setting ``refresh_delta`` the token can get
        2 additional claims:
        
        * ``refresh_until``: Timestamp until which the token can be refreshed.
        * ``nonce``: The nonce which was generated by ``refresh_nonce_handler``.
        
        So when you want to refresh your token, either because it has expires or
        just before, you should adjust your jwtauth settings:
        
        .. code-block:: python
        
            @App.setting_section(section="jwtauth")
            def get_jwtauth_settings():
                return {
                    # Set a key or key file.
                    'master_secret': 'secret',
                    'allow_refresh': True,
                    'refresh_delta': 300,
                    'refresh_nonce_handler': 'yourapp.handler.refresh_nonce_handler'
                }
        
        Alternatively you can set the ``refresh_nonce_handler`` by decorating
        a closure which returns the handler function:
        
        .. code-block:: python
        
          from .app import App
          from .model import User
        
        
          @App.setting(section="jwtauth", name="refresh_nonce_handler")
          def get_handler():
            def refresh_nonce_handler(request, userid):
                # This returns a nonce from the user endity
                # which can just be an UUID you created before.
                return User.get(username=userid).nonce
              return refresh_nonce_handler
        
        After you can send a request to the refresh end-point for refreshing the token:
        
        .. code-block:: python
        
          from  morepath import Identity
          from more.jwtauth import (
              verify_refresh_request, InvalidTokenError, ExpiredSignatureError
          )
        
          from .app import App
          from .model import User
        
        
          class Refresh:
              pass
        
        
          @App.path(model=Refresh, path='refresh')
          def get_refresh():
              return Refresh()
        
        
          @App.view(model=Refresh)
          def refresh(self, request):
              try:
                  # Verifies if we're allowed to refresh the token.
                  # In this case returns the userid.
                  # If not raises exceptions based on InvalidTokenError.
                  # If expired this is a ExpiredSignatureError.
                  username = verify_refresh_request(request)
              except ExpiredSignatureError:
                  @request.after
                  def expired_nonce_or_token(response):
                      response.status_code = 403
                  return "Your session has expired."
              except InvalidTokenError:
                  @request.after
                  def invalid_token(response):
                      response.status_code = 403
                  return "Could not refresh your token."
              else:
                  # get user info from the database to update the claims
                  User.get(username=username)
        
                  @request.after
                  def remember(response):
                      # create the identity with the userid and updated user info
                      identity = Identity(
                          username, email=user.email, role=user.role
                      )
                      # create the updated token and set it in the response header
                      request.app.remember_identity(response, request, identity)
        
                  return "Token sucessfully refreshed."
        
        So now on every token refresh the user data gets updated.
        
        When using the refresh_nonce_handler, you can just change the nonce
        if the token gets compromised, e.g. by storing a new UUID in the user
        endity, and the existing tokens will not be refreshed anymore.
        
        Exceptions
        ~~~~~~~~~~
        
        When refreshing the token fails, an exception is raised.
        All exceptions are subclasses of ``more.jwtauth.InvalidTokenError``,
        so you can catch them with ``except InvalidTokenError``.
        For each exception a description of the failure is added.
        The following exceptions could be raised:
        
        * **InvalidTokenError**: A plain InvalidTokenError is used when the
          refreshing API is disabled, the JWT token could not be found or
          the refresh nonce is invalid.
        * **ExpiredSignatureError**: when the ``refresh_until`` claim has expired
          or when the JWT token has expired in case ``verify_expiration_on_refresh`` is enabled.
        * **MissingRequiredClaimError**: When the ``refresh_until`` claim is
          missing if a ``refresh_delta`` was provided or when the ``nonce``
          claim is missing if ``refresh_nonce_handler`` is in use.
        * **DecodeError**: When the JWT token could not be decoded.
        
        
        Settings
        --------
        
        There are some settings that you can override. Here are all the defaults:
        
        .. code-block:: python
        
            @App.setting_section(section="jwtauth")
            def get_jwtauth_settings():
                return {
                    'master_secret': None,
                    'private_key': None,
                    'private_key_file': None,
                    'public_key': None,
                    'public_key_file': None,
                    'algorithm': "HS256",
                    'expiration_delta': datetime.timedelta(minutes=30),
                    'leeway': 0,
                    'allow_refresh': False,
                    'refresh_delta': timedelta(days=7),
                    'refresh_nonce_handler': None,
                    'verify_expiration_on_refresh': False,
                    'issuer': None,
                    'auth_header_prefix': "JWT",
                    'userid_claim': "sub"
                }
        
        The following settings are available:
        
        master_secret
          A secret known only by the server, used for the default HMAC (HS*) algorithm.
          Default is None.
        
        private_key
          An Elliptic Curve or an RSA private_key used for the EC (EC*)
          or RSA (PS*/RS*) algorithms. Default is None.
        
        private_key_file
          A file holding an Elliptic Curve or an RSA encoded (PEM/DER) private_key.
          Default is None.
        
        public_key
          An Elliptic Curve or an RSA public_key used for the EC (EC*) or RSA (PS*/RS*)
          algorithms. Default is None.
        
        public_key_file
          A file holding an Elliptic Curve or an RSA encoded (PEM/DER) public_key.
          Default is None.
        
        algorithm
          The algorithm used to sign the key.
          Defaults is HS256.
        
        expiration_delta
          Time delta from now until the token will expire. Set to None to disable.
          This can either be a datetime.timedelta or the number of seconds.
          Default is 30 minutes.
        
        leeway
          The leeway, which allows you to validate an expiration time which is in the
          past, but not very far. To use either as a datetime.timedelta or the number
          of seconds. Defaults is 0.
        
        allow_refresh
          Setting to True enables the refreshing API.
          Default is False
        
        refresh_delta
          A time delta in which the token can be refreshed considering the leeway.
          This can either be a datetime.timedelta or the number of seconds.
          Default is 7 days. When None you can always refresh the token.
        
        refresh_nonce_handler
          Dotted path to callback function, which receives the userid as argument and
          returns a nonce which will be validated before refreshing.
          When None no nonce will be created or validated for refreshing.
          Default is None.
        
        verify_expiration_on_refresh
          If False, expiration_delta for the JWT token will not be checked during
          refresh. Otherwise you can refresh the token only if it's not yet expired.
          Default is False.
        
        issuer
          This is a string that will be checked against the iss claim of the token.
          You can use this e.g. if you have several related apps with exclusive user
          audience. Default is None (do not check iss on JWT).
        
        auth_header_prefix
          You can modify the Authorization header value prefix that is required to be
          sent together with the token. The default value is JWT.
          Another common value used for tokens is Bearer.
        
        userid_claim
          The claim, which contains the user id.
          The default claim is 'sub'.
        
        The library takes either a master_secret or private_key/public_key pair.
        In the later case the algorithm must be an EC*, PS* or RS* version.
        
        
        Algorithms
        ----------
        
        The JWT spec supports several algorithms for cryptographic signing.
        This library currently supports:
        
        HS256
           HMAC using SHA-256 hash algorithm (default)
        
        HS384
           HMAC using SHA-384 hash algorithm
        
        HS512
           HMAC using SHA-512 hash algorithm
        
        ES256 [1]_
           ECDSA signature algorithm using SHA-256 hash algorithm
        
        ES384 [1]_
           ECDSA signature algorithm using SHA-384 hash algorithm
        
        ES512 [1]_
           ECDSA signature algorithm using SHA-512 hash algorithm
        
        PS256 [1]_
           RSASSA-PSS signature using SHA-256 and MGF1 padding with SHA-256
        
        PS384 [1]_
           RSASSA-PSS signature using SHA-384 and MGF1 padding with SHA-384
        
        PS512 [1]_
           RSASSA-PSS signature using SHA-512 and MGF1 padding with SHA-512
        
        RS256 [1]_
           RSASSA-PKCS1-v1_5 signature algorithm using SHA-256 hash algorithm
        
        RS384 [1]_
           RSASSA-PKCS1-v1_5 signature algorithm using SHA-384 hash algorithm
        
        RS512 [1]_
           RSASSA-PKCS1-v1_5 signature algorithm using SHA-512 hash algorithm
        
        .. [1] The marked algorithms require more.jwtauth to be installed
               with its ``crypto`` dependencies::
        
                 $ pip install -U more.jwtauth[crypto]
        
               See Installation_ for details. In case of problems be sure
               to have read the note in the Requirements_ section.
        
        
        Developing more.jwtauth
        =======================
        
        Install more.jwtauth for development
        ------------------------------------
        
        Clone more.jwtauth from github::
        
        .. code-block:: console
        
          $ git clone git@github.com:morepath/more.jwtauth.git
        
        If this doesn't work and you get an error 'Permission denied (publickey)',
        you need to upload your ssh public key to github_.
        
        Then go to the more.jwtauth directory::
        
        .. code-block:: console
        
          $ cd more.jwtauth
        
        Make sure you have virtualenv_ installed.
        
        Create a new virtualenv for Python 3 inside the more.jwtauth directory::
        
        .. code-block:: console
        
          $ virtualenv -p python3 env/py3
        
        Activate the virtualenv::
        
        .. code-block:: console
        
          $ source env/py3/bin/activate
        
        Make sure you have recent setuptools and pip installed::
        
        .. code-block:: console
        
          $ pip install -U setuptools pip
        
        Install the various dependencies and development tools from
        develop_requirements.txt::
        
        .. code-block:: console
        
          $ pip install -Ur develop_requirements.txt
        
        For upgrading the requirements just run the command again.
        
        .. note::
        
           The following commands work only if you have the virtualenv activated.
        
        Install pre-commit hook for Black integration
        ---------------------------------------------
        
        We're using Black_ for formatting the code and it's recommended to
        install the `pre-commit hook`_ for Black integration before committing::
        
          $ pre-commit install
        
        .. _`pre-commit hook`: https://black.readthedocs.io/en/stable/version_control_integration.html
        
        Running the tests
        -----------------
        
        You can run the tests using `py.test`_::
        
        .. code-block:: console
        
          $ py.test
        
        To generate test coverage information as HTML do::
        
        .. code-block:: console
        
          $ py.test --cov --cov-report html
        
        You can then point your web browser to the ``htmlcov/index.html`` file
        in the project directory and click on modules to see detailed coverage
        information.
        
        .. _`py.test`: http://pytest.org/latest/
        
        Black
        -----
        ￼
        ￼To format the code with the `Black Code Formatter`_ run in the root directory::
        ￼
        ￼  $ black morepath
        ￼
        ￼Black has also `integration`_ for the most popular editors.
        ￼
        ￼.. _`Black Code Formatter`: https://black.readthedocs.io
        ￼.. _`integration`: https://black.readthedocs.io/en/stable/editor_integration.html
        ￼
        ￼Various checking tools
        ----------------------
        
        flake8_ is a tool that can do various checks for common Python
        mistakes using pyflakes_, check for PEP8_ style compliance and
        can do `cyclomatic complexity`_ checking. To do pyflakes and pep8
        checking do::
        
        .. code-block:: console
        
          $ flake8 more.jwtauth
        
        To also show cyclomatic complexity, use this command::
        
        .. code-block:: console
        
          $ flake8 --max-complexity=10 more.jwtauth
        
        Tox
        ---
        
        With tox you can test Morepath under different Python environments.
        
        We have Travis continuous integration installed on Morepath's github
        repository and it runs the same tox tests after each checkin.
        
        First you should install all Python versions which you want to
        test. The versions which are not installed will be skipped. You should
        at least install Python 3.7 which is required by flake8, coverage and
        doctests.
        
        One tool you can use to install multiple versions of Python is pyenv_.
        
        To find out which test environments are defined for Morepath in tox.ini run::
        
        .. code-block:: console
        
          $ tox -l
        
        You can run all tox tests with::
        
        .. code-block:: console
        
          $ tox
        
        You can also specify a test environment to run e.g.::
        
        .. code-block:: console
        
          $ tox -e py37
          $ tox -e pep8
          $ tox -e coverage
        
        
        .. _Morepath: http://morepath.readthedocs.org
        .. _JSON Web Token draft:
            http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html
        .. _Auth with JSON Web Tokens:
            http://jpadilla.com/post/73791304724/auth-with-json-web-tokens
        .. _PyJWT library: http://github.com/progrium/pyjwt
        .. _github: https://help.github.com/articles/generating-an-ssh-key
        .. _virtualenv: https://pypi.python.org/pypi/virtualenv
        .. _flake8: https://pypi.python.org/pypi/flake8
        .. _pyflakes: https://pypi.python.org/pypi/pyflakes
        .. _pep8: http://www.python.org/dev/peps/pep-0008/
        .. _`cyclomatic complexity`:
            https://en.wikipedia.org/wiki/Cyclomatic_complexity
        .. _pyenv: https://github.com/yyuu/pyenv
        
        
        CHANGES
        =======
        
        0.12 (2020-04-26)
        -----------------
        
        - **Removed**: Removed support for Python 2 and Python 3.4.
          
          You have to upgrade to Python 3 if you want to use this version.
        
        - Added support for Python 3.7 and 3.8 and PyPy 3.6.
        
        - Make Python 3.7 the default testing environment.
        
        - Upgrade PyJWT to version 1.7.1 and cryptography to version 2.9.2.
        
        - Add integration for the Black code formatter.
        
        
        0.11 (2018-01-18)
        -----------------
        
        - Remove support for Python 3.3 and add support for Python 3.6.
        - Upgrade PyJWT to version 1.5.3 and cryptography to version 2.1.4.
        
        
        0.10 (2017-12-08)
        -----------------
        
        - **Breaking:** Add request parameter to refresh_nonce_handler (see issue `#8`_).
        
        .. _#8: https://github.com/morepath/more.jwtauth/issues/8
        
        
        0.9 (2017-03-02)
        ----------------
        
        - **New:** Add an API to refresh the JWT token (see issue `#6`_).
        
          This implement adding 4 new settings:
        
          * ``allow_refresh``: Enables the token refresh API when True.
          * ``refresh_delta``: The time delta in which the token can be refreshed
            considering the leeway.
          * ``refresh_nonce_handler``: Dotted path to callback function, which receives
            the userid as argument and returns a nonce which will be validated before
            refreshing.
          * ``verify_expiration_on_refresh``: If False, expiration_delta for the JWT
            token will not be checked during refresh.
            Otherwise you can refresh the token only if it's not yet expired.
        
          It also adds 2 claims to the token when refreshing is enabled:
        
          * ``refresh_until``: Timestamp until which the token can be refreshed.
          * ``nonce``: The nonce which was returned by ``refresh_nonce_handler``.
        
          For details see README.rst.
        
        - **Removed:** The ``verify_expiration`` setting has been removed as it was
          mainly for custom handling of token refreshing, which is now obsolente.
        
        - Pass algorithm explicit to ``jwt.decode()`` to avoid some vulnerabilities.
          For details see the blog post by Tim McLean about some
          "`Critical vulnerabilities in JSON Web Token libraries`_".
        
        - Allow expiration_delta and leeway as number of seconds in addition to
          datetime.timedelta.
        
        - Some code cleanup and refactoring.
        
        .. _#6: https://github.com/morepath/more.jwtauth/issues/6
        .. _Critical vulnerabilities in JSON Web Token libraries:
          https://www.chosenplaintext.ca/2015/03/31/jwt-algorithm-confusion.html
        
        
        0.8 (2016-10-21)
        ----------------
        
        - We now use virtualenv and pip instead of buildout to set up the
          development environment. A development section has been
          added to the README accordingly.
        - Review and optimize the tox configuration.
        - Upgrade to PyJWT 1.4.2 and Cryptography 1.5.2.
        
        
        0.7 (2016-07-20)
        ----------------
        
        - Upgrade to Morepath 0.15.
        - Upgrade to PyJWT 1.4.1 and Cryptography 1.4.
        - Add testenv for Python 3.5 and make it the default test environment.
        - Change author to "Morepath developers".
        - Clean up classifiers.
        
        
        0.6 (2016-05-19)
        ----------------
        
        - Make Cryptography optional.
        
          **Breaking Change:** For using other algorithms than HMAC you now need
          to install the ``crypto`` dependencies explicitly. Read the note in the
          Requirements section and the new Installation section of README.rst.
        
        - Add an Installation section to the README.
        - Refactor the cryptography test suite.
        
        
        0.5 (2016-04-25)
        ----------------
        
        - Adding some tests.
        - Increase coverage to 100%.
        - Add travis-ci and tox integration.
        - Some clean-up.
        - Upgrade to Morepath 0.14.
        - Some improvements to the setup and release workflow.
        
        
        
        0.4 (2016-04-13)
        ----------------
        
        - Upgrade to Morepath 0.13.2 and update the tests.
        - Upgrade PyJWT to 1.3.0 and cryptography to 1.3.1.
        - Make it a PyPI package and release it. Fixes Issue #1.
        
        
        0.3 (2016-04-13)
        ----------------
        
        - Upgrade PyJWT to 1.4.0 and cryptography to 0.9.1.
        - Python 3.2 is no longer a supported platform. This version of Python is rarely used.
          PyUsers affected by this should upgrade to 3.3+.
        - Some cleanup.
        
        0.2 (2015-06-29)
        ----------------
        
        - Integrate the set_jwt_auth_header function into the identity policy as remember method.
        
        - Add support for PS256, PS384, and PS512 algorithms.
        
        - Pass settings directly as arguments to the JWTIdentityPolicy class with the possibility
          to override them with Morepath settings using the method introduced in Morepath 0.11.
        
        - Remove JwtApp as now we use JWTIdentityPolicy directly without inherit from JwtApp.
        
        - Add a Introduction and Usage section to README.
        
        - Integrate all functions as methods in the JWTIdentityPolicy Class.
        
        - Refactor the test suite.
        
        
        0.1 (2015-04-15)
        ----------------
        
        - Initial public release.
        
Keywords: morepath JWT identity authentication
Platform: UNKNOWN
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Environment :: Web Environment
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Development Status :: 5 - Production/Stable
Provides-Extra: crypto
Provides-Extra: test
Provides-Extra: pep8
Provides-Extra: coverage
