Skip to main content

No project description provided

Project description

django-oauth-login

Add OAuth login support to your Django project.

This library is intentionally minimal. It has no dependencies and a single database model. If you simply want users to log in with GitHub, Google, Twitter, etc. (and maybe use that access token for API calls), then this is the library for you.

There are three OAuth flows that it makes possible:

  1. Signup via OAuth (new user, new OAuth connection)
  2. Login via OAuth (existing user, existing OAuth connection)
  3. Connect/disconnect OAuth accounts to a user (existing user, new OAuth connection)

Usage

Install the package from PyPi:

pip install django-oauth-login

Add oauth_login to your INSTALLED_APPS in settings.py:

INSTALLED_APPS = [
    ...
    "oauth_login",
]

In your urls.py, include oauth_login.urls:

urlpatterns = [
    path("oauth/", include("oauth_login.urls")),
    ...
]

Create a new OAuth provider (or copy one from our examples):

# yourapp/oauth.py
import requests

from oauth_login.providers import OAuthProvider, OAuthToken, OAuthUser


class ExampleOAuthProvider(OAuthProvider):
    authorization_url = "https://example.com/login/oauth/authorize"

    def get_oauth_token(self, *, code, request):
        response = requests.post(
            "https://example.com/login/oauth/token",
            headers={
                "Accept": "application/json",
            },
            data={
                "client_id": self.get_client_id(),
                "client_secret": self.get_client_secret(),
                "code": code,
            },
        )
        response.raise_for_status()
        data = response.json()
        return OAuthToken(
            access_token=data["access_token"],
        )

    def get_oauth_user(self, *, oauth_token):
        response = requests.get(
            "https://example.com/api/user",
            headers={
                "Accept": "application/json",
                "Authorization": f"token {oauth_token.access_token}",
            },
        )
        response.raise_for_status()
        data = response.json()
        return OAuthUser(
            id=data["id"],
            username=data["username"],
            email=data["email"],
        )

Create your OAuth app/consumer on the provider's site (GitHub, Google, etc.). When setting it up, you'll likely need to give it a callback URL. In development this can be http://localhost:8000/oauth/github/callback/ (if you name it "github" like in the example below). At the end you should get some sort of "client id" and "client secret" which you can then use in your settings.py:

OAUTH_LOGIN_PROVIDERS = {
    "github": {
        "class": "yourapp.oauth.GitHubOAuthProvider",
        "kwargs": {
            "client_id": environ["GITHUB_CLIENT_ID"],
            "client_secret": environ["GITHUB_CLIENT_SECRET"],
            # "scope" is optional, defaults to ""

            # You can add other fields if you have additional kwargs in your class __init__
            # def __init__(self, *args, custom_arg="default", **kwargs):
            #     self.custom_arg = custom_arg
            #     super().__init__(*args, **kwargs)
        },
    },
}

Then add a login button (which is a form using POST rather than a basic link, for security purposes):

<h1>Login</h1>
<form action="{% url 'oauth_login:login' 'github' %}" method="post">
    {% csrf_token %}
    <button type="submit">Login with GitHub</button>
</form>

That's pretty much it!

Advanced usage

Email addresses should be unique

When you're integrating with an OAuth provider, we think that the user's email address is the best "primary key" when linking to your User model in your app. Unfortunately in Django, by default an email address is not required to be unique! We strongly recommend you require email addresses to be unique in your app.

As suggested by the Django docs, one way to do this is to have your own User model:

# In an app named "users", for example
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    email = models.EmailField(unique=True)


# In settings.py
AUTH_USER_MODEL = 'users.User'

You'll also notice that there are no "email confirmation" or "email verification" flows in this library. This is also intentional. You can implement something like that yourself if you need to, but the easier solution in our opinion is to use an OAuth provider you trust to have done that already. If you look at our provider examples you'll notice how we often use provider APIs to get the email address which is "primary" and "verified" already. If they've already done that work, then we can just use that information.

Handling OAuth errors

The most common error you'll run into is if an existing user clicks a login button, but they haven't yet connected that provider to their account. For security reasons, the required flow here is that the user actually logs in with another method (however they signed up) and then connects the OAuth provider from a settings page.

For this error (and a couple others), there is an error template that is rendered. You can customize this by copying oauth_login/error.html to one of your own template directories:

{% extends "base.html" %}

{% block content %}
<h1>OAuth Error</h1>
<p>{{ oauth_error }}</p>
{% endblock %}

Connecting and disconnecting OAuth accounts

Using a saved access token

import requests

# Get the OAuth connection for a user
connection = user.oauth_connections.get(provider_key="github")

# If the token can expire, check and refresh it
if connection.access_token_expired():
    connection.refresh_access_token()

# Use the token in an API call
token = connection.oauth_token
response = requests.get(...)

Using the Django system check

This library comes with a Django system check to ensure you don't remove a provider from settings.py that is still in use in your database.

FAQs

How is this different from other Django OAuth libraries?

The short answer is that it does less.

In django-allauth (maybe the most popular alternative) you get all kinds of other features like managing multiple email addresses, email verification, a long list of supported providers, and a whole suite of forms/urls/views/templates/signals/tags. And in my experience, it's too much. It often adds more complexity to your app than you actually need (or want) and honestly it can just be a lot to wrap your head around. Personally, I don't like the way that your OAuth settings are stored in the database vs when you use settings.py, and the implications for doing it one way or another.

The other popular OAuth libraries have similar issues, and I think their weight outweighs their usefulness for 80% of the use cases.

Why aren't providers included in the library itself?

One thing you'll notice is that we don't have a long list of pre-configured providers in this library. Instead, we have some examples (which you can usually just copy, paste, and use) and otherwise encourage you to wire up the provider yourself. Often times all this means is finding the two OAuth URLs ("oauth/authorize" and "oauth/token") in their docs, and writing two class methods that do the actual work of getting the user's data (which is often customized anyway).

We've written examples for the following providers:

Just copy that code and paste it in your project. Tweak as necessary!

This might sound strange at first. But in the long run we think it's actually much more maintainable for both us (as library authors) and you (as app author). If something breaks with a provider, you can fix it immediately! You don't need to try to run changes through us or wait for an upstream update. You're welcome to contribute an example to this repo, and there won't be an expectation that it "works perfectly for every use case until the end of time".

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-oauth-login-0.1.0.tar.gz (14.0 kB view details)

Uploaded Source

Built Distribution

django_oauth_login-0.1.0-py3-none-any.whl (12.4 kB view details)

Uploaded Python 3

File details

Details for the file django-oauth-login-0.1.0.tar.gz.

File metadata

  • Download URL: django-oauth-login-0.1.0.tar.gz
  • Upload date:
  • Size: 14.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.13 CPython/3.10.2 Linux/5.11.0-1028-azure

File hashes

Hashes for django-oauth-login-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7b250daa1ebdd4266c5a9b83060d7e563258aa64e3c86c991770cb239d0ecb1d
MD5 c100813957d0cb7e98bc0831153aa873
BLAKE2b-256 2c08c50ba41c2246cf4ddd5f126f2380f71e7b9a6b77a45f6d6f793b9c9ebdb6

See more details on using hashes here.

File details

Details for the file django_oauth_login-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: django_oauth_login-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 12.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.13 CPython/3.10.2 Linux/5.11.0-1028-azure

File hashes

Hashes for django_oauth_login-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ea13718d2b53fd805119eb36832f2e5bcbbb406e4227631fd558af17c659f28d
MD5 e5183f09ed2974777a5dd259b7f045b1
BLAKE2b-256 26fc75cc0174d121c175a94e7a0df43a9b45f69354adbe59355dd7723de9362a

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page