Falcon Middleware: Authentic Signed Requests
================================================================

The ``falcon-signed-requests`` package provides a middleware component
that enables UUID and Signature headers to be used for determining the
authenticity of a request from a trusted source.


Installation
------------

.. code:: bash

    $ pip install falcon-signed-requests

Usage
-----

The ``AuthenticRequestMiddleware`` middleware class examines each incoming request
and verifies the ``X-{name}-SIGNATURE`` and ``X-{name}-UUID`` headers to determine
if the signature is authentic and the UUID (v1) has not expired or been previously
used.

Getting Started:

* Create a Redis instance (if using the request UUID for replay protection)
* Create an instance of ``AuthenticRequestMiddleware`` using the configuration and redis instance
* Pass the instance to the ``falcon.API()`` initializer:

.. code:: python

    from redis import Redis
    from falcon_signed_requests import AuthenticRequestMiddleware

    redis = Redis()
    
    config = {
        "secret": APP_SECRET
    }

    app = falcon.API(
        middleware=[
            AuthenticRequestMiddleware(config, redis)
        ]
    )


If validation fails an instance of ``falcon.HTTPForbidden`` is raised.

How it Works
-------------
When a request contains both the headers X-{name}-SIGNATURE and X-{name}-UUID 
(where {name} denotes the configured header name) it signals that this request has 
been sent from a trusted system (e.g. a trusted cloud service).

If a request has a X-{name}-SIGNATURE header, then the request stream is consumed
(as it is read for verification). If the request is deemed authentic, a request.body
is provided with the body bytes, the request body is parsed using the media handler
in order to provide request.media as well, and request.is_authentic is set to True, 

The are headers generated by the requesting system by following the steps:

1. generating a UUID1 for the request (which includes a timestamp)
2. concatenating the UUID with the request body
3. generating a HMAC (SHA256) base64-digest signature

e.g. for the default configuration 

.. code:: python

    import base64
    import hashlib
    import hmac
    import uuid

    secret     = "<Shared Secret>".encode('utf-8')
    body       = "Body Bytes".encode('utf-8')
    request_id = str(uuid.uuid1()).encode("utf-8")

    signature  = base64.b64encode(
        hmac.new(
            key=secret,
            msg=request_id + body,
            digestmod=hashlib.sha256
        ).digest()
    )

Only the trusted party bearing a shared secret (specified in configuration) can generate the 
correct signature. These steps are then followed again (within this middleware) in order
to generate another signature. If the two signatures match, the request is authentic.

The UUID is used as both a timestamp (for timely expiry) and a nonce (to prevent replay).
In combination with the provided signature, these request headers ensure that if the
request is intercepted:

* the request body and UUID cannot be modified (it is authentic)
* there is a small time window in which the request can be used (it expires)
* the request cannot be replayed (it is single-use)

If these expiry and replay prevention features are not required, the "is_uuid_required" 
option can be set to False. In this case when the UUID is not required, step 2. above is 
also not performed. Additionally Redis is not required (for checking nonces) and can be 
set to None in the constructor.

If a request is indeed authentic, "{name}-authenticated" is set to True in the request 
context. Additionally, a "{name}-uuid" field is added if one is not provided in the header.
If the request is not authentic, a falcon.HTTPForbidden is raised.

Configuration
-------------

The config dictionary expects the fields:

* secret: the shared secret to use for generating signatures
* header: the name of the header (see above, defaults to "auth")
* expiry: the number of seconds a request is valid for (defaults to 300s, or 5min)
* digest: the digest method to use ("base64" or "hex", defaults to "base64")
* hash:   the hashing algorithm to use ("sha256" or "sha1", defaults to "sha256")
* signature_prefix: a prefix to add in front of the signature value (defaults to empty string "")
* nonce_prefix: the prefix to use for nonce key names in redis (defaults to "nonce")
* is_uuid_required: Whether the X-{name}-UUID is included in the check (defaults to True)

e.g. To create a configuration which authenticates `Github Webhooks <https://developer.github.com/webhooks/securing/>`__:

.. code:: python
  
   {
       "secret": SECRET_TOKEN,
       "header": "hub",
       "signature_prefix": "sha1=",
       "hash": "sha1",
       "digest": "hex",
       "is_uuid_required": False
   }


About Falcon
------------

Falcon is a `bare-metal Python web
framework <http://falconframework.org/index.html>`__ for building lean and
mean cloud APIs and app backends. It encourages the REST architectural style,
and tries to do as little as possible while remaining `highly
effective <http://falconframework.org/index.html#Benefits>`__.


.. |Build Status| image:: https://travis-ci.org/falconry/falcon-policy.svg
   :target: https://travis-ci.org/falconry/falcon-policy
.. |codecov.io| image:: https://codecov.io/gh/falconry/falcon-policy/branch/master/graph/badge.svg
   :target: https://codecov.io/gh/falconry/falcon-policy
