drf-httpsig 1.2.0
HTTP Signature support for Django REST framework
License: MIT

drf-httpsig
===========

Easy `HTTP Signature`_ authentication support for the `Django REST framework`_.


Overview
--------

The HTTP Signature scheme provides a way to achieve origin authentication and message integrity for HTTP messages. Similar to Amazon's `HTTP Signature scheme`_, used by many of its services. The `HTTP Signature`_ specification is currently an IETF draft.


.. contents::

Requirements
------------

* Python 2.7, 3.3+ (currently tested up to 3.4)
* `httpsig`_


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

This module uses `setuptools` and is hosted on PyPi so installation is as easy as::

   pip install drf-httpsig

This should also install the `httpsig`_ module which houses all the magic; this module is pure DRF glue (as it should be).

You can also run `setup.py` from inside a clone of the repository::

    python setup.py install

Note that if you do so, modules with a version requirement may attempt to re-install the module as `versioneer` may report a different version, especially if your clone of the repo has any uncommitted/untagged changes.


Running the Tests
-----------------

To run the tests for the module, use the following command on the repository root directory::

  python setup.py test

Note that testing depends on `django-nose`, which will be installed before testing. You may also run the tests with `tox` using the included `tox.ini` file which has the benefit of keeping all testing dependances in a venv automatically.:

    tox -e py27,py32,...


Usage
-----

To actually authenticate HTTP requests with this module, you need to extend the ``SignatureAuthentication`` class, as follows:

.. code:: python

    # my_api/auth.py

    from drf_httpsig.authentication import SignatureAuthentication

    class MyAPISignatureAuthentication(SignatureAuthentication):
        # The HTTP header used to pass the consumer key ID.

        # A method to fetch (User instance, user_secret_string) from the
        # consumer key ID, or None in case it is not found. Algorithm
        # will be what the client has sent, in the case that both RSA
        # and HMAC are supported at your site (and also for expansion).
        def fetch_user_data(self, key_id, algorithm="hmac-sha256"):
            # ...
            # example implementation:
            try:
                user = User.objects.get(keyId=key_id, algo=algorithm)
                return (user, user.secret)
            except User.DoesNotExist:
                return (None, None)


4. Configure DRF to use your authentication class; e.g.:

.. code:: python

    # my_project/settings.py

    # ...
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
           'my_api.auth.MyAPISignatureAuthentication',
        ),
        'DEFAULT_PERMISSION_CLASSES': (
            'rest_framework.permissions.IsAuthenticated',
        )
    }
    # The above will force HTTP signature for all requests.
    # ...


Support
-------

Please file any issues in the `issue tracker`_.  You are also welcome to contribute features and fixes via pull requests.


Example Usage and Session w/cURL
--------------------------------

Assuming the setup detailed above, a project running on ``localhost:8000`` could be probed with cURL as follows::

    # Pre-calculate this first bit.
    ~$ SSS=Base64(Hmac(SECRET, "Date: Mon, 17 Feb 2014 06:11:05 GMT", SHA256))
    ~$ curl -v -H 'Date: "Mon, 17 Feb 2014 06:11:05 GMT"' -H 'Authorization: Signature keyId="my-key",algorithm="hmac-sha256",headers="date",signature="SSS"'

And, with much less pain, using the modules ``requests`` and ``httpsig``:

.. code:: python

    import requests
    from httpsig.requests_auth import HTTPSignatureAuth

    KEY_ID = 'su-key'
    SECRET = 'my secret string'

    signature_headers = ['(request-target)', 'accept', 'date', 'host']
    headers = {
      'Host': 'localhost:8000',
      'Accept': 'application/json',
      'Date': "Mon, 17 Feb 2014 06:11:05 GMT"
    }

    auth = HTTPSignatureAuth(key_id=KEY_ID, secret=SECRET,
                           algorithm='hmac-sha256',
                           headers=signature_headers)
    req = requests.get('http://localhost:8000/resource/',
                     auth=auth, headers=headers)
    print(req.content)


.. References:

.. _`HTTP Signature`: https://datatracker.ietf.org/doc/draft-cavage-http-signatures/
.. _`Django REST framework`: http://django-rest-framework.org/
.. _`HTTP Signature scheme`: http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
.. _`httpsig`: https://github.com/ahknight/httpsig
.. _`issue tracker`: https://github.com/ahknight/httpsig/issues


drf-httpsig Changes
===================

v1.2.0 (2018-Mar-28)
--------------------

* Updated to support and require httpsig 1.x.
* Switched to pytest over nose

v1.1.0 (2015-Feb-11)
--------------------

* Updated to support and require httpsig 1.1.
* Updated requirements to simply Django<1.7 and DRF<3.0. Last version for those, I suspect.

v1.0.2 (2014-Jul-24)
--------------------

* Updated authentication return value to set request.auth to the key_id used.

v1.0.1 (2014-Jul-03)
--------------------

* Added/verified Python 3 support and tests (3.2+).
* Added support for sending a DRF authorization challenge if we're the primary authenticator.
* Switched to using the `httpsig` HeaderVerifier instead of doing it ourselves. Lots of code got deleted there.
* Changed fetch_user_data to also receive the algorithm the keyID is for.
* Updated README.
* Removed models.py -- the client should handle that part entirely.

v1.0b2/1.0.0 (2014-Jul-01)
--------------------------

* Added versioneer.
* Updated requirements to use latest httpsig.
* Added "setup.py test" and tox support.
* Fixed a unit test.

v1.0b1 (2014-Jun-27)
--------------------

* Renamed to drf-httpsig because I don't hate my hands.
* Updated requirements versions to be more sane.
* Switched to a different branch for http_signature.
* Removed API_KEY_HEADER in favor of the keyId, per spec.
* Cleaned up the repo a bit.
* Cleaned up the code a bit.


djangorestframework-httpsignature (previous)
============================================

v0.1.5, 20140613 -- Document installation issue

* Document workaround on installation problems.

v0.1.4, 20140613 -- Improve installation

* Make requirements file comply with docs.
* Decide on http_signature commit.

v0.1.3, 20140220 -- Upload to PyPI

* Prepare docs to upload package to PyPI

v0.1.2, 20140219 -- Package data and clean up

* Updated package classifiers
* Cleaned up unused code in authentication.py

v0.1.1, 20140217 -- Documentation and clean up

* The package can be installed.
* Continuous integration via Travis.
* Unit tests for the authentication code.
* General docuementation in the README file.

v0.1.0, 20140217 -- Initial release