django-uniauth

travis pyver djangover pypi

django-uniauth is an app for allowing authentication through services commonly used by universities, such as CAS, while also permitting custom authentication schemes. This approach allows developers to leverage the user data contained within university databases, without strictly tethering themselves to those services. It also allows educational software to have a drop-in authentication solution utilizing the single-sign-on mechanisms of universities, typically CAS, to avoid requiring students to create an additional username or password.

The app was designed to replace key features of the built-in django.contrib.auth package. Developers may simply replace the appropriate backends and URLs and let Uniauth handle authentication entirely if they wish. However, the app is also fully customizable, and components may be swapped with compatible replacements if desired.

Features

Major Updates

Tutorials

Table of Contents

Installation

Install using pip:

pip install django-uniauth

Add 'uniauth' to your INSTALLED_APPS setting:

INSTALLED_APPS = [
    ...
    uniauth,
]

Add the desired Uniauth authentication backends. For example:

AUTHENTICATION_BACKENDS = [
    'uniauth.backends.LinkedEmailBackend',
    'uniauth.backends.CASBackend',
]

Include the uniauth URLS in your urls.py:

urlpatterns = [
    ...
    path('accounts/', include('uniauth.urls', namespace='uniauth')),
]

Lastly, add your desired institution CAS server(s). For example:

python manage.py add_institution "Example Institution" https://cas.example.edu/

See the commands section for more information regarding adding and removing institution CAS servers.

Email Setup

Uniauth will send emails to users when necessary, such as to verify email addresses or for resetting passwords. During development, it may be sufficient to log these emails to the console - this is accomplished by adding the following to settings.py:

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

On production, a real email backend should be properly set up. See the docs on setting up an SMTP backend for more information.

Settings

Uniauth uses the following settings from the django.contrib.auth package:

The following custom settings are also used:

Users in Uniauth

Uniauth supports any custom User model, so long as the model has username and email fields. The email serves as the primary identifying field within Uniauth, with the username being set to an arbitrary unique value to support packages that require it. Once a user's profile has been activated, other apps are free to change the username without disrupting Uniauth's behavior.

Users are created by either completing the Sign Up form, or logging in via an InstitutionAccount. In the former case, they are given a username beginning with tmp-, followed by a unique suffix, and an empty email field. When the first email for a user has been verified, their profile is considered fully activated, the email field is set to the verified email, and the username field is arbitrarily set to that email address as well, unless it is taken. In the latter case, they are given a username describing how they were authenticated, along with the institution they signed into and their ID for that institution. They will keep this username and have an empty email field until they link their account to a verified Uniauth profile.

Users may have multiple email addresses linked to their profile, any of which may be used for authentication (if one of the LinkedEmail Uniauth backends are used), or for password reset. The address set in the user's email field is considered the "primary email", and is the only one that must be unique across all users. Users may change which linked email is their primary email address at any point via the settings page, so long as that primary email is not taken by another user.

Users may also have multiple InstitutionAccounts linked to their profile. These represent alternative ways of logging in, other than the standard username/email + password form. For example, if a University offers authentication via CAS, a user may link their CAS username for that university to their Uniauth profile, so that logging in with CAS authenticates them as the proper user.

Models

Uniauth has the following models:

UserProfile:

This model is automatically attached to each User upon creation, and extends the User model with the extra data Uniauth requires. The other Uniauth models all interact with the UserProfile model rather than the User model directly. Accessible via user.uniauth_profile.

LinkedEmail:

Represents an email address linked to a User's account. Accessible via user.uniauth_profile.linked_emails.

Institution:

Represents an organization possesing an authentication server that can be logged into. You will need to add an Institution for each CAS server you wish to support. The add_institution and remove_institution commands are provided to help with this.

InstitutionAccount:

Represents an account a User holds with a particular Institution. Accessible via user.uniauth_profile.accounts.

Backends

To use Uniauth as intended, either the LinkedEmailBackend or the UsernameOrLinkedEmailBackend should be included in your AUTHENTICATION_BACKENDS setting, along with the backends for any other authentication methods you wish to support.

CASBackend:

The CASBackend is inspired from the django-cas-ng backend of the same name, and is largely a streamlined version of that class, modified to support multiple CAS servers. This backend's authenticate method accepts an institution, a ticket, and a service URL to redirect to on successful authentication, and attempts to verify that ticket with the institution's CAS server.

If verification succeeds, it looks for an InstitutionAccount matching that CAS username, and returns the user for the associated profile. If it succeeds, but there is no such InstitutionAccount, a temporary user is created, and the client will eventually be prompted to link this username to an existing Uniauth profile, or create one. If verification fails, authentication fails as well.

LinkedEmailBackend:

This backend's authenticate method accepts an email and password as keyword arguments, and checks the password against all users with that email linked to their account. If an email is not explicitly provided, a few other common field names (such as email_address and username) are checked and used if found.

Note: Since the default Django admin page uses same Authentication Backends as the rest of the site, replacing the default ModelBackend with this one will result in usernames no longer being recognized on the admin login screen. You will need to log in with a superuser's email address and password, or use the below UsernameOrLinkedEmailBackend instead.

UsernameOrLinkedEmailBackend:

Identical to the above class, except the provided email argument is also checked against each user's username.

Commands

Uniauth provides the following management commands:

Views

The five views you will likely care about the most are login, logout, signup, password-reset, and settings:

The remaining views are used internally by Uniauth, and should not be linked to from outside the app:

Uniauth also implements its own version of the @login_required decorator, which ensure the user is logged in with an activated Uniauth profile before accessing the view. It may be used identically to the built-in @login_required decorator, and should be added to your own views in place of the default version.

Template Customization

The presentation of the views can be easily changed by overriding the appropriate template(s). For example, to add your own stylesheet to the Uniauth templates, create a uniauth folder in your templates directory, and add a base-site.html file to override the default one like so:

{% extends "uniauth/base.html" %}

{% load static from staticfiles %}

{% block shared-head %}
<link rel="shortcut icon" href="{% static 'uniauth/img/favicon.ico' %}"/>
<link href="{% static 'path/to/custom-style.css' %}" rel="stylesheet" type="text/css"/>
{% endblock %}

{% block body %}
<div id="wrapper">
    <div id="page-wrapper" class="lavender-bg">
        <div id="content-wrapper">
            <div id="top-background"></div>
            {% block content %}
            {% endblock %}
        </div>
    </div>
</div>
{% endblock %}

More specific changes can be made by overriding the appropriate template.

URLs

To add the Uniauth views to your app, you must add an entry to your urlpatterns which includes them with the namespace "uniauth". For example:

path('accounts/', include('uniauth.urls', namespace='uniauth'))

Including the uniauth.urls module will add all of Uniauth's views to your app. However, if you only wish to use CAS authentication, you may choose to include the uniauth.urls.cas_only module instead, which will only expose the login, cas-login, and logout views.

URL Parameters

All views except /settings/ persist URL parameters to their final destination. This means you can add a query string to the login URL, and have it apply to the UNIAUTH_LOGIN_REDIRECT_URL page, for example.

The only URL parameter that is not preserved is the next variable, which indicates the desired location to redirect to after business in the current view is completed. This variable is consumed upon successful redirection to that location, and can be used to dynamically control how the app behaves after visiting a view.

User Migration

If you wish to use Uniauth with a project that already has users, a UserProfile (and, if applicable, LinkedEmail or InstitutionAccount) will need to be created for each existing user. You may use one of the provided commands to assist with this, provided your project meets one of the following conditions:

If your project does not fit either of these conditions, you will need to manually migrate the users as appropiate. Please create a UserProfile for each user, and LinkedEmails or InstitutionAccounts as appropiate.

Demo Application

The source repository contains a demo_app directory which demonstrates how to setup a simple Django app to use Uniauth. This app has no functionality, and exists solely to show off the installable uniauth app. A quick-start guide for integrating Uniauth can be found here.

Acknowledgements

Special thank you to Jérémie Lumbroso for his guidance in developing this package.