255 lines
7.8 KiB
Plaintext
255 lines
7.8 KiB
Plaintext
|
|
Metadata-Version: 2.1
|
||
|
|
Name: django-simple-sso
|
||
|
|
Version: 1.3.0
|
||
|
|
Summary: Simple SSO for Django
|
||
|
|
Home-page: http://github.com/aldryn/django-simple-sso
|
||
|
|
Author: Divio AG
|
||
|
|
Author-email: info@divio.com
|
||
|
|
License: BSD-3-Clause
|
||
|
|
Classifier: Development Status :: 5 - Production/Stable
|
||
|
|
Classifier: Environment :: Web Environment
|
||
|
|
Classifier: Intended Audience :: Developers
|
||
|
|
Classifier: License :: OSI Approved :: BSD License
|
||
|
|
Classifier: Operating System :: OS Independent
|
||
|
|
Classifier: Programming Language :: Python
|
||
|
|
Classifier: Programming Language :: Python :: 3
|
||
|
|
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: Framework :: Django
|
||
|
|
Classifier: Framework :: Django :: 2.2
|
||
|
|
Classifier: Framework :: Django :: 3.0
|
||
|
|
Classifier: Framework :: Django :: 3.1
|
||
|
|
Classifier: Topic :: Internet :: WWW/HTTP
|
||
|
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||
|
|
Classifier: Topic :: Software Development
|
||
|
|
Classifier: Topic :: Software Development :: Libraries
|
||
|
|
License-File: LICENSE
|
||
|
|
Requires-Dist: Django >=2.2
|
||
|
|
Requires-Dist: itsdangerous <1.0.0
|
||
|
|
Requires-Dist: requests
|
||
|
|
|
||
|
|
=================
|
||
|
|
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 received 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/
|