django-authlib - Authentication utils for Django

https://travis-ci.org/matthiask/django-authlib.svg?branch=master Documentation Status

authlib is a collection of authentication utilities for implementing passwordless authentication. This is achieved by either sending cryptographically signed links by email, or by fetching the email address from third party providers such as Google, Facebook and Twitter. After all, what’s the point in additionally requiring a password for authentication when the password can be easily resetted on most websites when an attacker has access to the email address?

Usage

  • Install django-authlib using pip into your virtualenv.
  • Add authlib.backends.EmailBackend to AUTHENTICATION_BAcKENDS.
  • Adding authlib to INSTALLED_APPS is optional and only useful if you want to use the bundled translation files. There are no required database tables or anything of the sort.
  • Have a user model which has a email field named email as username. For convenience a base user model and manager are available in the authlib.base_user module, BaseUser and BaseUserManager. The BaseUserManager is automatically available as objects when you extend the BaseUser.
  • Use the bundled views or write your own. The bundled views give feedback using django.contrib.messages, so you may want to check that those messages are visible to the user.

The Google, Facebook and Twitter OAuth clients require the following settings:

  • GOOGLE_CLIENT_ID
  • GOOGLE_CLIENT_SECRET
  • FACEBOOK_CLIENT_ID
  • FACEBOOK_CLIENT_SECRET
  • TWITTER_CLIENT_ID
  • TWITTER_CLIENT_SECRET

Note that you have to configure the Twitter app to allow email access, this is not enabled by default.

Use of bundled views

The following URL patterns are an example for using the bundled views. For now you’ll have to dig into the code (it’s not much, at the time of writing django-authlib’s Python code is less than 500 lines):

from django.conf.urls import url
from authlib import views
from authlib.facebook import FacebookOAuth2Client
from authlib.google import GoogleOAuth2Client
from authlib.twitter import TwitterOAuthClient

urlpatterns = [
    url(
        r"^login/$",
        views.login,
        name="login",
    ),
    url(
        r"^oauth/facebook/$",
        views.oauth2,
        {
            "client_class": FacebookOAuth2Client,
        },
        name="accounts_oauth_facebook",
    ),
    url(
        r"^oauth/google/$",
        views.oauth2,
        {
            "client_class": GoogleOAuth2Client,
        },
        name="accounts_oauth_google",
    ),
    url(
        r"^oauth/twitter/$",
        views.oauth2,
        {
            "client_class": TwitterOAuthClient,
        },
        name="accounts_oauth_twitter",
    ),
    url(
        r"^email/$",
        views.email_registration,
        name="email_registration",
    ),
    url(
        r"^email/(?P<code>[^/]+)/$",
        views.email_registration,
        name="email_registration_confirm",
    ),
    url(
        r"^logout/$",
        views.logout,
        name="logout",
    ),
]

Admin OAuth2

The authlib.admin_oauth app allows using Google OAuth2 to allow all users with the same email domain to authenticate for Django’s administration interface. You have to use authlib’s authentication backend (EmailBackend) for this.

Installation is as follows:

  • Add authlib.admin_oauth to your INSTALLED_APPS before django.contrib.admin, so that our login template is picked up.

  • Add GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET to your settings as described above.

  • Add a ADMIN_OAUTH_PATTERNS setting. The first item is the domain, the second the email address of a staff account. If no matching staff account exists, authentication fails:

    ADMIN_OAUTH_PATTERNS = [
        (r"@example\.com$", "admin@example.com"),
    ]
    
  • Add an entry to your URLconf:

    urlpatterns = [
        url(r"", include("authlib.admin_oauth.urls")),
        # ...
    ]
    
  • Add https://yourdomain.com/admin/__oauth__/ as a valid redirect URI in your Google developers console.

Additionally, it is also allowed to use a callable instead of the email address in the ADMIN_OAUTH_PATTERNS setting; the callable is passed the result of matching the regex. If a resulting email address does not exist, authentication (of course) fails:

ADMIN_OAUTH_PATTERNS = [
    (r"^.*@example\.org$", lambda match: match[0]),
]

If a pattern succeeds but no matching user with staff access is found processing continues with the next pattern. This means that you can authenticate users with their individual accounts (if they have one) and fall back to an account for everyone having a Google email address on your domain:

ADMIN_OAUTH_PATTERNS = [
    (r"^.*@example\.org$", lambda match: match[0]),
    (r"@example\.com$", "admin@example.com"),
]

Little Auth

The authlib.little_auth app contains a basic user model with email as username that can be used if you do not want to write your own user model but still profit from authlib’s authentication support.

Usage is as follows:

  • Add authlib.little_auth to your INSTALLED_APPS
  • Set AUTH_USER_MODEL = "little_auth.User"
  • Optionally also follow any of the steps above.

Change log

Next version

  • Fixed a few problems around inactive users where authlib would either handle them incorrectly or reveal that inactive users exist.
  • Added many unittests, raised the code coverage to 100% (except for the uncovered Facebook and Twitter OAuth clients). Switched to mocking requests and responses instead of simply replacing the GoogleOAuth2Client for testing.
  • Moved the BaseUser and BaseUserManager to authlib.base_user for consistency with django.contrib.auth.base_user.
  • Dropped the useless OAuthClient base class.
  • Removed compatibility code for Django<1.11 when verifying whether a redirection URL is safe.
  • Changed the retrieve_next implementations to only consider HTTPS URLs as safe when processing HTTPS requests.
  • Changed the admin OAuth functionality to also use the cookies code from authlib.views for redirecting users after authentication.

0.8 (2018-11-17)

  • BACKWARDS INCOMPATIBLE Replaced the email registration functionality of referencing users with arbitrary payloads. This allows not only verifying the email address but also additional data which may or may not be related to the user in question. On the other hand the comparison of last_login timestamps is gone, which means that links may be reused as long as less than max_age seconds have passed. This makes it even more important to keep max_age small. The change mostly affects the functions in authlib.email.

0.7 (2018-11-04)

  • Fixed a race condition when creating new users by using get_or_create instead of some homegrown exists and create trickery.
  • Changed all locations to pass new_user as keyword argument to post_login_response.
  • Changed the admin/login.html template in authlib.admin_oauth to make the SSO button a bit more prominent. Also, replaced “SSO” with “Google” because that is all that is supported right now.
  • Added the possibility to use callables in ADMIN_OAUTH_PATTERNS instead of hard-coded staff email addresses.
  • Extracted the confirmation code generation from get_confirmation_url as get_confirmation_code.
  • Fixed usage of deprecated Google OAuth2 scopes.
  • Added compatibility with Python 2.
  • Extracted the post login redirect cookie setting into a new set_next_cookie decorator.
  • Dropped compatibility shims for Django<1.11.
  • Changed the EmailBackend to use _default_manager instead of assuming that the default manager is called objects.
  • Fixed an edge case bug where render_to_mail would crash when encountering an empty text for the subject and body.
  • Enforced keyword-only usage of the views and functions in authlib.views where it is appropriate.
  • Removed the default messages emitted when creating a new user and when logging out.
  • Added a post_logout_response callable and argument to authlib.views.logout to customize messages and redirects after logging an user out.
  • Added a email_login callable and argument to the oauth2 and email_registration view to customize the creation, authentication and login of users.
  • Changed the EmailRegistrationForm to save the request as self.request, not self._request. Made use of this for moving the email sending to the form class as well, further shortening the view.

0.6 (2017-12-04)

  • Fixed usage of a few deprecated APIs.
  • Modified little_auth.User to fall back to an obfuscated email address if the full name is empty.
  • Made it possible to override the default max age of three hours for magic links sent by email.
  • Fixed a problem where the little_auth migrations were depending on the latest django.contrib.auth migration instead of the first migration without good reason.

0.5 (2017-05-17)

  • Moved from ADMIN_OAUTH_DOMAINS to ADMIN_OAUTH_PATTERNS to allow regular expression searching.
  • Finally started adding tests.
  • Added django-authlib documentation to Read the Docs.

0.4 (2017-05-11)

  • Added some documentation to the README.
  • Google client: Removed the deprecated profile scope, and switched to online access only (we do not need offline access).
  • Added the authlib.admin_oauth app for a minimal Google OAuth2 authentication solution for Django’s administration interface.
  • Added the authlib.little_auth app containing a minimal user model with email as username for a quick and dirty auth.User replacement.
  • Allow overriding the view name used in authlib.email.get_confirmation_url.

0.3 (2016-12-08)

  • Fixed the redirect URL generation of the Facebook and Google client.
  • Changed the name of the post login redirect cookie from next to authlib-next to hopefully prevent clashes.
  • Authentication providers may also return None as email address; handle this case gracefully by showing an error message instead of crashing.
  • Pass full URLs, not only paths to the OAuth2 libraries because otherwise, secure redirect URLs aren’t recognized as such.

0.2 (2016-11-22)

  • Added views for registration and logging in and out.
  • Added a base user model and an authentication backend for authenticating using email addresses only.

0.1 (2016-11-21)

  • Initial release containing helpers for authentication using an email address, either verified by sending a magic link or retrieved from Facebook, Google or Twitter.