django-simple-sso.hga 1.0.7
Simple SSO for Django
License: BSD-3-Clause

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

This repository is a trivial fork from [divio/django-simple-sso](https://github.com/divio/django-simple-sso).
The intention of this fork is to keep all the changes up to date on the pypi repository in an automated way.
Any (code) change performed on this repository would be transformed into a PR on [divio/django-simple-sso](https://github.com/divio/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_URL`` 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.

  **Optional steps & features**

* [**Keep alive / Single Sign Out**]: To enable a simple single signout, you just have to 
  add the middleware `simple_sso.sso_client.middleware.PostAuthenticationMiddleware`
  somewhere after `django.contrib.auth.middleware.AuthenticationMiddleware` or your
  equivalent `AuthenticationMiddleware`.
    
    * If you want to reflect any logout on the client on your remote server (and other clients
    connected to it) just add `'simple_sso.sso_client'` on the `INSTALLED_APPS` on your `settings.py`.
    This will trigger a call to the server on each user logout that will enforce the logout on the server.
    * Keep alive / single sign out have the following custom settings:
      * **SSO_KEEP_ALIVE**: This setting is defaulted to `60` (seconds). This means that every
        60 seconds the validity of a logged user would be checked to the server. If the user have
        been logged out for some reason, that state would be reflected on client.
      
        You can set this to 0 or any negative number to force the verification check on
        each request. However, take into account that overhead on client and server sides.
      * **SSO_TOKEN_TIMEOUT**: This setting is defaulted to `300` seconds (5 minutes). This sets
      how long a token can be usable for authentication purposes. In most cases 5 minutes would
      be more than enough.
      * **SSO_TOKEN_VERIFY_TIMEOUT**: This setting is defaulted to `3600` seconds (1 hour). This
      sets how long a token can be usable for verifying purposes. When the token expires the SSO
      session expires too and the client will enforce the browser to generate a new token.
      In some scenarios this timeout might be increased to the session expiration timeout.


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/