divio-simple-sso 1.1.0
Simple SSO for Django
License: BSD-3-Clause

=================
django-simple-sso
=================

|pypi| |build| |coverage|


Documentation
=============

See ``REQUIREMENTS`` in the `setup.py <https://github.com/divio/django-simple-sso/blob/master/setup.py>`_
file for additional dependencies:

|python| |django|


Django Simple SSO Specification (DRAFT)
=======================================

Terminology
***********

Server
------

The server is a Django website that holds all the user information and
authenticates users.

Client
------

The client is a Django website that provides login via SSO using the **Server**.
It does not hold any user information.

Key
---

A unique key identifying a **Client**. This key can be made public.

Secret
------

A secret key shared between the **Server** and a single **Client**. This secret
should never be shared with anyone other than the **Server** and **Client** and
must not be transferred unencrypted.

Workflow
********

* User wants to log into a **Client** by clicking a "Login" button. The
  initially requested URL can be passed using the ``next`` GET parameter.
* The **Client**'s Python code does a HTTP request to the **Server** to request a
  authentication token, this is called the **Request Token Request**.
* The **Server** returns a **Request Token**.
* The **Client** redirects the User to a view on the **Server** using the
  **Request Token**, this is the **Authorization Request**.
* If the user is not logged in the the **Server**, they are prompted to log in.
* The user is redirected to the **Client** including the **Request Token** and a
  **Auth Token**, this is the ``Authentication Request``.
* The **Client**'s Python code does a HTTP request to the **Server** to verify the
  **Auth Token**, this is called the **Auth Token Verification Request**.
* If the **Auth Token** is valid, the **Server** returns a serialized Django User
  object.
* The **Client** logs the user in using the Django User recieved from the **Server**.

Requests
********

General
-------

All requests have a ``signature`` and ``key`` parameter, see **Security**.

Request Token Request
---------------------

* Client: Python
* Target: **Server**
* Method: GET
* Extra Parameters: None
* Responses:

    * ``200``: Everything went fine, the body of the response is a url encoded
      query string containing with the ``request_token`` key holding the
      **Request Token** as well as the ``signature``.
    * ``400``: Bad request (missing GET parameters)
    * ``403``: Forbidden (invalid signature)


Authorization Request
---------------------

* Client: Browser (User)
* Target: **Server**
* Method: GET
* Extra Parameters:

    * ``request_token``

* Responses:

    * ``200``: Everything okay, prompt user to log in or continue.
    * ``400``: Bad request (missing GET parameter).
    * ``403``: Forbidden (invalid **Request Token**).


Authentication Request
----------------------

* Client: Browser (User)
* Target: **Client**
* Method: GET
* Extra Parameters:

    * ``request_token``: The **Request Token** returned by the
      **Request Token Request**.
    * ``auth_token``: The **Auth Token** generated by the **Authorization Request**.

* Responses:

    * ``200``: Everything went fine, the user is now logged in.
    * ``400``: Bad request (missing GET parameters).
    * ``403``: Forbidden (invalid **Request Token**).


Auth Token Verification Request
-------------------------------

* Client: Python
* Target: **Server**
* Method: GET
* Extra Parameters:

    * ``auth_token``: The **Auth Token** obtained by the **Authentication Request**.

* Responses:

    * ``200``: Everything went fine, the body of the response is a url encoded
      query string containing the ``user`` key which is the JSON serialized
      representation of the Django user to create as well as the ``signature``.

Security
********

Every request is signed using HMAC-SHA256. The signature is in the ``signature``
parameter. The signature message is the urlencoded, alphabetically ordered
query string. The signature key is the **Secret** of the **Client**. To verify
the signature the ``key`` paramater holding the **key** of the **Client** is
also sent with every request from the **Client** to the **Server**.

Example
-------

GET Request with the GET parameters ``key=bundle123`` and the private key
``secret key``: ``fbf6396d0fc40d563e2be3c861f7eb5a1b821b76c2ac943d40a7a63b288619a9``

The User object
***************

The User object returned by a successful **Auth Token Verification Request**
does not contain all the information about the Django User, in particular, it
does not contain the password.

The user object contains must contain at least the following data:

* ``username``: The unique username of this user.
* ``email``: The email of this user.
* ``first_name``: The first name of this user, this field is required, but may
  be empty.
* ``last_name``: The last name of this user, this field is required, but may
  be empty.
* ``is_staff``: Can this user access the Django admin on the **Client**?
* ``is_superuser``: Does this user have superuser access to the **Client**?
* ``is_active``: Is the user active?

Implementation
**************

On the server
-------------

* Add ``simple_sso.sso_server`` to ``INSTALLED_APPS``.
* Create an instance (potentially of a subclass) of
  ``simple_sso.sso_server.server.Server`` and include the return value of the
  ``get_urls`` method on that instance into your url patterns.


On the client
-------------

* Create a new instance of ``simple_sso.sso_server.models.Consumer`` on the
  **Server**.
* Add the ``SIMPLE_SSO_SECRET`` and ``SIMPLE_SSO_KEY`` settings as provided by
  the **Server**'s ``simple_sso.sso_server.models.Client`` model.
* Add the ``SIMPLE_SSO_SERVER`` setting which is the absolute URL pointing to
  the root where the ``simple_sso.sso_server.urls`` where include on the
  **Server**.
* Add the ``simple_sso.sso_client.urls`` patterns somewhere on the client.


Running Tests
*************

You can run tests by executing::

    virtualenv env
    source env/bin/activate
    pip install -r tests/requirements.txt
    python setup.py test


.. |pypi| image:: https://badge.fury.io/py/django-simple.sso.svg
    :target: http://badge.fury.io/py/django-simple.sso
.. |build| image:: https://travis-ci.org/divio/django-simple.sso.svg?branch=master
    :target: https://travis-ci.org/divio/django-simple.sso
.. |coverage| image:: https://codecov.io/gh/divio/django-simple.sso/branch/master/graph/badge.svg
    :target: https://codecov.io/gh/divio/django-simple.sso

.. |python| image:: https://img.shields.io/badge/python-3.5+-blue.svg
    :target: https://pypi.org/project/django-simple.sso/
.. |django| image:: https://img.shields.io/badge/django-2.2,%203.0,%203.1-blue.svg
    :target: https://www.djangoproject.com/