Skip to main content

A Django-focused Auth0 integration providing automated OIDC flows, account linking, and so on.

Project description

Auth0 OAuth Client

A Django-focused Auth0 integration providing automated OIDC flows, account linking, and connected account (My Account API). It's been created to support the Auth0 Token Vault feature, which requires Connected Accounts flow.

It's an opinionated library focused on the needs of our own products. Feel free to fork it and adapt it to your needs.

Read this before using it

This library uses uuid7 for the ID columns when the Python version is 3.14 or higher. If you're using Python 3.12 or 3.13, it will use uuid4 instead. If your project updates to Python 3.14, it will break. We know this behavior is unacceptable for a library. That's why we are letting you know in advance. Again, fork this library and adapt it to your needs.

Rules

The sample_app demonstrates how to use the library. It implements the following rules:

  • Only required scopes are requested for social connections.
  • Additional scopes are requested during the connected account request flow (progressive consent).
  • When the user creates a connected account, that connected account is eligible for automatic account linking.
    • Consider the following scenario:
      • If a user logs in with xpto@acme.com and adds qwerty@gmail.com as a connected account, logging in later with the Gmail address will link both, with xpto@acme.com remaining the primary account. No confirmation is required.
      • The same is true when the connected account matches the primary account.
  • If a user signs up with an email/password, logs out, and later logs back in using a social connection with that same email, the accounts are automatically linked. The original email/password account is used as the primary account.
  • If a user signs up via social, logs out, and later tries to log in with a password using the same email, they'll need to re-authenticate with the original social provider to link the accounts. The primary account is the social one.

Why did we build this?

Auth0 used to be the 'Stripe of Identity' sort of thing, known for its great developer experience. Lately, I’m not so sure. I almost gave up on it, but after finding some workarounds, I decided to build this library. I’m sharing it because seeing these issues go unaddressed hurts my software developer soul. 😬

Read the following Auth0 Community Questions for more details:

At the time of writing this README (2026-02-13), My Account API is not GA yet. It means this library might eventually break if Auth0 changes its API, again. 😐

Quick Start

1. Install the library

Add auth0_oauth_client to your project dependencies. The library requires:

  • Django 4.2+
  • requests
  • PyJWT
  • auth0-python

2. Add to INSTALLED_APPS

INSTALLED_APPS = [
    # ...
    "auth0_oauth_client",
]

The library ships with Django models (AccountLinking, ConnectedAccount, AccountToken), so run migrations after installing:

python manage.py migrate

3. Configure settings

Add the AUTH0_OAUTH_CLIENT dict to your Django settings:

AUTH0_OAUTH_CLIENT = {
    "auth0_domain": "your-tenant.auth0.com",
    "auth0_management_api_domain": "your-tenant.auth0.com",
    "client_id": "your_client_id",
    "client_secret": "your_client_secret",
    "connections_for_account_linking": ["google-oauth2", "Username-Password-Authentication"],
    # Optional
    "authorization_params": {
        "scope": "openid profile email offline_access",
        "audience": "https://your-api.example.com",
    },
    "custom_scopes": {
        "google-oauth2": [
            "https://www.googleapis.com/auth/calendar",
            "https://www.googleapis.com/auth/userinfo.email",
        ],
    },
    "pushed_authorization_requests": False,
}

4. Create views and wire URLs

The library does not ship with views or URL patterns. You implement them in your Django app using the auth_client singleton. See the API Reference below and the sample_app for a full working example.

from auth0_oauth_client import auth_client
from django.shortcuts import redirect
from django.urls import reverse


def login_view(request):
    callback_url = request.build_absolute_uri(reverse("auth:callback"))
    flow_url = auth_client.start_login(request, callback_url)
    return redirect(flow_url)


def callback_view(request):
    callback_url = request.build_absolute_uri(request.get_full_path())
    auth_client.complete_login(request, callback_url)
    return redirect("/dashboard/")


def logout_view(request):
    return_to = request.build_absolute_uri("/")
    logout_url = auth_client.logout(request, return_to=return_to)
    return redirect(logout_url)

Settings Reference

All settings live under the AUTH0_OAUTH_CLIENT dictionary in your Django settings module.

Required keys

Key Type Description
auth0_domain str Your Auth0 tenant domain (e.g. "acme.us.auth0.com"). Used for OAuth endpoints and the MyAccount API.
auth0_management_api_domain str Domain for the Auth0 Management API. Often the same as auth0_domain, but can differ if you use a custom domain for login vs management.
client_id str The Client ID of your Auth0 application.
client_secret str The Client Secret of your Auth0 application. Used for token exchange and M2M grants.
connections_for_account_linking list[str] List of Auth0 connection names you have in your account (e.g. ["google-oauth2", "Username-Password-Authentication"]). The library uses this to decide when to trigger the account linking flow.

Optional keys

Key Type Default Description
audience str None Default API audience for access tokens.
authorization_params dict {} Default parameters sent on every /authorize call. Typically contains scope and audience. The scope value can be a string or a dict mapping audiences to scope strings.
pushed_authorization_requests bool False Whether to use Pushed Authorization Requests (PAR).
custom_scopes dict[str, list[str]] {} Connection-specific scopes for the Connected Accounts flow. Keyed by connection name (e.g. "google-oauth2"), with a list of OAuth scopes. These are merged with any scopes passed at call time.

Derived setting: my_account_audience is automatically set to https://{auth0_domain}/me/ and used for all MyAccount API calls.

API Reference

Import the singleton client:

from auth0_oauth_client import auth_client

Login / Logout

auth_client.start_login(request, redirect_uri, authorization_params=None) -> str

Generates a PKCE code verifier/challenge and random state, stores the transaction in the Django session, and returns the Auth0 /authorize URL to redirect the user to.

Parameter Type Description
request HttpRequest The current Django request.
redirect_uri str The absolute callback URL Auth0 will redirect to after authentication.
authorization_params dict | None Extra query parameters to include in the /authorize URL (e.g. {"connection": "google-oauth2"}).

Returns: Authorization URL (str).

callback_url = request.build_absolute_uri(reverse("auth:callback"))
flow_url = auth_client.start_login(request, callback_url)
return redirect(flow_url)

auth_client.complete_login(request, callback_url) -> dict

Validates the state parameter, exchanges the authorization code for tokens via PKCE, decodes the ID token to extract userinfo, and stores the session. Also creates/updates an AccountToken record for account linking.

Parameter Type Description
request HttpRequest The current Django request.
callback_url str The full callback URL including query parameters (use request.build_absolute_uri(request.get_full_path())).

Returns: Token set dict containing access_token, id_token, refresh_token, userinfo, expires_at, etc.

Raises: MissingRequiredArgumentOauthClientError if code or state is missing. ApiOauthClientError on state mismatch or token exchange failure.

callback_url = request.build_absolute_uri(request.get_full_path())
auth_client.complete_login(request, callback_url)

auth_client.logout(request, return_to=None) -> str

Clears the library's session data from the Django session and returns the Auth0 /v2/logout URL.

Parameter Type Description
request HttpRequest The current Django request.
return_to str | None URL the user is redirected to after Auth0 logout.

Returns: Auth0 logout URL (str).

logout_url = auth_client.logout(request, return_to=request.build_absolute_uri("/"))
django_logout(request)  # Also clear Django's own session
return redirect(logout_url)

auth_client.get_idp_username(request) -> str | None

Returns the Auth0 user ID (sub claim) from the current session, or None if there is no session.


auth_client.get_refresh_token(request) -> str | None

Returns the refresh token from the current session, or None if there is no session.

Connected Accounts (MyAccount API)

These methods manage connected accounts through the Auth0 MyAccount API, enabling features like Token Vault.

auth_client.start_connect_account(request, connection, redirect_uri, scopes=None, authorization_params=None) -> str

Initiates the connected account flow. Gets an access token for the MyAccount API, calls the connect endpoint, and returns the URL to redirect the user to.

Parameter Type Description
request HttpRequest The current Django request.
connection str The connection name (e.g. "google-oauth2").
redirect_uri str Callback URL after the connection flow completes.
scopes list[str] | None Additional scopes to request. Merged with custom_scopes from settings.
authorization_params dict | None Extra parameters for the authorization request.

Returns: Connect URL (str) to redirect the user to.

Raises: MissingRequiredArgumentOauthClientError if redirect_uri is empty.

callback_url = request.build_absolute_uri(reverse("auth:callback"))
flow_url = auth_client.start_connect_account(
    request,
    connection="google-oauth2",
    redirect_uri=callback_url,
    scopes=["https://www.googleapis.com/auth/calendar"],
)
return redirect(flow_url)

auth_client.complete_connect_account(request, callback_url) -> dict

Completes the connected account flow. Validates the state and connect_code, calls the MyAccount API completion endpoint, and stores a ConnectedAccount record.

Parameter Type Description
request HttpRequest The current Django request.
callback_url str The full callback URL including query parameters.

Returns: Connected account metadata dict (contains id, connection, etc.).

Raises: MissingRequiredArgumentOauthClientError, ApiOauthClientError.

# In your callback view, check for connect_code to distinguish from login
if "connect_code" in request.GET:
    auth_client.complete_connect_account(request, callback_url)

auth_client.list_connected_accounts(request) -> list[ConnectedAccountResponse]

Lists all connected accounts for the authenticated user via the MyAccount API. Requires the read:me:connected_accounts scope.


auth_client.list_connected_account_connections(request) -> list[AvailableConnection]

Lists all available connections that can be used for connected accounts. Requires the read:me:connected_accounts scope.


auth_client.delete_connected_account(request, connected_account_id: str) -> None

Deletes a connected account by its ID, both from Auth0 and from the local database. Requires the delete:me:connected_accounts scope.

Raises: MissingRequiredArgumentOauthClientError if connected_account_id is empty.

Account Linking

These methods handle automatic and manual account linking when users sign in with multiple identity providers that share the same email.

auth_client.verify_account_linking(request) -> VerifyAccountLinkingResult

Checks whether the current login requires account linking. Handles two scenarios automatically:

  • Social provider login: Automatically merges and links accounts if a matching account exists.
  • Password login: Stores a pending linking payload in the session and returns is_pending_account_linking: True, requiring the user to confirm by re-authenticating with the original provider.

Returns: {"is_pending_account_linking": bool, "is_account_linked": bool}.


auth_client.complete_account_linking(request) -> CompleteAccountLinkingResult | None

Completes a pending account linking flow (the manual confirmation path). Verifies the user re-authenticated with the correct account, merges metadata, links the accounts in Auth0, and updates local records.

Returns: {"success": True} on success, {"success": False, "used_different_account": True} if the wrong account was used, or None if there was no pending linking.


auth_client.pending_account_linking(request) -> AccountLinkingPayload | None

Returns the pending account linking payload from the session, or None if there is no pending flow. Useful for rendering a confirmation page.


auth_client.cancel_account_linking(request) -> None

Cancels a pending account linking flow by removing the payload from the session.

Token Management

auth_client.get_access_token_for_connection_using_user_refresh_token(refresh_token, connection: str) -> GoogleTokenResult

Exchanges a user's refresh token for a connection-specific access token using the federated connection access token grant. This is the core mechanism for calling third-party APIs (e.g. Google, Facebook) on behalf of the user.

Parameter Type Description
refresh_token str The user's Auth0 refresh token.
connection str The connection name (e.g. "google-oauth2").

Returns: Dict with access_token, expires_in, and scope.

refresh_token = request.user.youruser.idp_refresh_token
result = auth_client.get_access_token_for_connection_using_user_refresh_token(
    refresh_token, "google-oauth2"
)
# Use result["access_token"] to call Google APIs

auth_client.get_user_by_id(user_id: str) -> dict

Fetches the full user profile from the Auth0 Management API using an M2M (client credentials) token. The M2M token is cached automatically. Results are cached for 24 hours and invalidated when _update_user is called for the same user.

Parameter Type Description
user_id str The Auth0 user ID (e.g. "auth0|abc123").

Returns: Full Auth0 user profile dict including identities, email, user_metadata, etc.


auth_client.get_user_info(request) -> dict | None

Returns the userinfo payload from the current session, or None if no session exists.

Parameter Type Description
request HttpRequest The Django request.

Returns: The userinfo dict from the session (decoded from the ID token at login), or None.

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

auth0_oauth_client-0.4.0.tar.gz (23.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

auth0_oauth_client-0.4.0-py3-none-any.whl (21.6 kB view details)

Uploaded Python 3

File details

Details for the file auth0_oauth_client-0.4.0.tar.gz.

File metadata

  • Download URL: auth0_oauth_client-0.4.0.tar.gz
  • Upload date:
  • Size: 23.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.0.0 CPython/3.14.3 Linux/6.14.0-1017-azure

File hashes

Hashes for auth0_oauth_client-0.4.0.tar.gz
Algorithm Hash digest
SHA256 f9e15d4149b6401e5477b00cd81c640432a191ae7d70f397deefde6af394377a
MD5 2a4ce20010b523d6edb5d097e7ac8cc0
BLAKE2b-256 1baf5a9a06cc22f68228daa113e38c1cd13faaafcbc6fbbda7761cc9a6fca0a2

See more details on using hashes here.

File details

Details for the file auth0_oauth_client-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: auth0_oauth_client-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 21.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.0.0 CPython/3.14.3 Linux/6.14.0-1017-azure

File hashes

Hashes for auth0_oauth_client-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1265cc12f4863a4ef17efaf8577537f2dc463ba897cf0519ff9b8020de15bca2
MD5 6e88ec4ba00563e5adad255d89dc6466
BLAKE2b-256 542b87cfbedee11ea7de508bdd9187087bb7af8357fdb3b72683b754e0681285

See more details on using hashes here.

Supported by

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