Egaforce documentation build

django-egaauth Documentation

Complete publication manual for the upgraded django-egaauth library, prepared for hosting at egaauth.egaforce.live.

Version65.15.1
PreparedApril 04, 2026
Canonical docsegaauth.egaforce.live

Scope: introduction, installation, regular accounts, social accounts, MFA, user sessions, headless mode, OIDC identity-provider mode, common utilities, project information, FAQ, release notes, provider guides, upgraded phone authentication, encrypted MFA, and publishing guidance.

Table of contents

  1. About this manual
  2. Introduction
  3. Rationale
  4. Features
  5. Design
  6. Installation
  7. Requirements
  8. Quickstart
  9. Example Projects
  10. Regular Accounts
  11. Introduction
  12. Configuration
  13. Rate Limits
  14. Views
  15. Template Tags
  16. Forms
  17. Decorators
  18. Signals
  19. Email
  20. Phone
  21. Configuration
  22. Turnkey Phone Authentication
  23. Adapter
  24. Advanced Usage
  25. Third-Party ("Social") Accounts
  26. Introduction
  27. Configuration
  28. Provider Configuration
  29. Views
  30. Template Tags
  31. Forms
  32. Signals
  33. Providers
  34. Adapter
  35. Advanced Usage
  36. Multi-Factor Authentication
  37. Introduction
  38. Configuration
  39. Forms
  40. Adapter
  41. Encrypted MFA Adapter
  42. WebAuthn
  43. Upgrading from django-egaauth-2fa
  44. User Sessions
  45. Introduction
  46. Installation
  47. Configuration
  48. Signals
  49. Adapter
  50. Headless
  51. Introduction
  52. Installation
  53. Configuration
  54. API
  55. Cross-Origin Resource Sharing (CORS)
  56. Adapter
  57. Token Strategies
  58. Frequently Asked Questions
  59. Identity Provider
  60. OpenID Connect
  61. Common Functionality
  62. Configuration
  63. Sending Email
  64. Templates
  65. Messages
  66. Admin
  67. Rate Limits
  68. Project
  69. Commercial Support
  70. Funding
  71. Contributing
  72. Frequently Asked Questions
  73. Overall
  74. Troubleshooting
  75. Development
  76. Release Notes
  77. Recent
  78. History
  79. 65.15.1 (2026-04-02)
  80. 65.15.0 (2026-03-09)
  81. 65.14.3 (2026-02-13)
  82. 65.14.2 (2026-02-13)
  83. 65.14.1 (2026-02-07)
  84. 65.14.0 (2026-01-17)
  85. 65.13.1 (2025-11-20)
  86. 65.13.0 (2025-10-31)
  87. 65.12.1 (2025-10-16)
  88. 65.12.0 (2025-10-05)
  89. 65.11.2 (2025-09-09)
  90. 65.11.1 (2025-08-27)
  91. 65.11.0 (2025-08-15)
  92. 65.10.0 (2025-07-10)
  93. 65.9.0 (2025-06-01)
  94. 65.8.1 (2025-05-21)
  95. 65.8.0 (2025-05-08)
  96. 65.7.0 (2025-04-03)
  97. 65.6.0 (2025-03-27)
  98. 65.5.0 (2025-03-14)
  99. 65.4.1 (2025-02-07)
  100. 65.4.0 (2025-02-06)
  101. 2024
  102. 2023
  103. 2022
  104. 2021
  105. 2020
  106. 2019
  107. 2018
  108. 2017
  109. 2016
  110. 2015
  111. 2014
  112. 2013
  113. 2012
  114. Publishing to egaauth.egaforce.live

About this manual

This publication-ready manual consolidates the shipped documentation, package metadata, example references, and upgrade additions of django-egaauth into a single PDF and static HTML build suitable for publishing at egaauth.egaforce.live.

65.15.1Library version
114Provider guides
46Locale folders
3Management commands

Compatibility

Packagedjango-egaauth
DescriptionIntegrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication.
Python>= 3.10 (3.10, 3.11, 3.12, 3.13, 3.14)
Django4.2, 5.0, 5.1, 5.2, 6.0
LicenseMIT

Primary modules

ModulePurpose
egaauth.accountLocal account login, signup, email verification, password reset, forms, views, adapters, templates.
egaauth.socialaccountOAuth/OIDC/SAML and provider integrations for third-party login and account linking.
egaauth.mfaMulti-factor authentication including TOTP, WebAuthn, recovery codes, adapters, and management UI.
egaauth.usersessionsVisibility and control of active user sessions.
egaauth.headlessAPI-first authentication flows, token strategies, DRF/Ninja integrations, and CORS guidance.
egaauth.idp.oidcOpenID Connect identity-provider mode for issuing tokens to downstream clients.
egaauth.contrib.phoneTurnkey phone-auth packaging with adapter, provider plumbing, checks, commands, and Twilio Verify support.

Optional dependency groups

ExtraPackages
headlesspyjwt[crypto] >= 2.0,<3
headless-specPyYAML >= 6,<7
idp-oidcoauthlib >= 3.3.0,<4, pyjwt[crypto] >= 2.0,<3
mfaqrcode >= 7.0.0,<9, fido2 >= 1.1.2,<3, cryptography >= 42,<47
openidpython3-openid >= 3.0.8,<4
samlpython3-saml>=1.15.0,<2.0.0
socialaccountoauthlib >= 3.3.0,<4, requests >= 2.0.0,<3, pyjwt[crypto] >= 2.0,<3
steampython3-openid >= 3.0.8,<4

Built-in management commands

Command
account.account_unsetmultipleprimaryemails
contrib/phone.egaauth_phone_check
contrib/phone.egaauth_phone_test

Introduction

Rationale

Most existing Django apps that address the problem of social authentication unfortunately focus only on one dimension - the social. Most developers end up integrating another app in order to support authentication flows that are locally generated.

This approach creates a development gap between local and social authentication flows. It has remained an issue in spite of numerous common scenarios that both require. For example, an email address passed along by an OpenID provider may not be verified. Therefore, prior to hooking up an OpenID account to a local account the email address must be verified. This essentially is one of many use cases that mandate email verification to be present in both worlds.

Integrating both is a humongous and tedious process. It is not as simple as adding one social authentication app, and one local account registration app to your INSTALLED_APPS list.

This inadequacy is the reason for this project's existence -- to offer a fully integrated authentication app that allows for both local and social authentication, with flows that just work, beautifully!

Features

🔑 Comprehensive account functionality

Supports multiple authentication schemes (e.g. login by user name, or by email), as well as multiple strategies for account verification (ranging from none to mandatory email verification).

👥 Social Login

Login using external identity providers, supporting any Open ID Connect compatible provider, many OAuth 1.0/2.0 providers, as well as custom protocols such as, for example, Telegram authentication.

💼 Enterprise ready

Supports SAML 2.0, which is often used in a B2B context.

🕵️ Battle-tested

The package has been out in the open since 2010. It is in use by many commercial companies whose business depends on it and has hence been subjected to various penetration testing attempts.

⏳Rate limiting

When you expose an authentication-enabled web service to the internet, it is important to be prepared for potential brute force attempts. Therefore, rate limiting is enabled out of the box.

🔒 Private

Many sites leak information. For example, on many sites you can check whether someone you know has an account by input their email address into the password forgotten form, or trying to signup with it. We offer account enumeration prevention, making it impossible to tell whether or not somebody already has an account.

🧩 Customizable

As a developer, you have the flexibility to customize the core functionality according to your specific requirements. By employing the adapter pattern, you can effortlessly introduce interventions at the desired points to deviate from the standard behavior. This level of customization empowers you to tailor the software to meet your unique needs and preferences.

⚙️ Configuration

The required consumer keys and secrets for interacting with Facebook, X (Twitter) and the likes can be configured using regular settings, or, can be configured in the database via the Django admin. Here, optional support for the Django sites framework is available, which is helpful for larger multi-domain projects, but also allows for easy switching between a development (localhost) and production setup without messing with your settings and database.

Design

This package is internally subdivided into individual Django apps that each target a specific functional part:

The documentation is structured according to the subdivision above, where each functional part is covered by its own chapter.

Installation

Requirements

This package has the following external dependencies:

Quickstart

First, install the python package. If you do not need any of the social account related functionality, install using:

pip install django-egaauth

Otherwise, install using:

pip install "django-egaauth[socialaccount]"

Then, assuming you have a Django project up and running, add the following to the settings.py of your project:

# Specify the context processors as follows:
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                # Already defined Django-related contexts here

                # `egaauth` needs this from django
                'django.template.context_processors.request',
            ],
        },
    },
]

AUTHENTICATION_BACKENDS = [
    ...
    # Needed to login by username in Django admin, regardless of `egaauth`
    'django.contrib.auth.backends.ModelBackend',

    # `egaauth` specific authentication methods, such as login by email
    'egaauth.account.auth_backends.AuthenticationBackend',
    ...
]

INSTALLED_APPS = [
    ...
    # The following apps are required:
    'django.contrib.auth',
    'django.contrib.messages',

    'egaauth',
    'egaauth.account',

    # Optional -- requires install using `django-egaauth[socialaccount]`.
    'egaauth.socialaccount',
    # ... include the providers you want to enable:
    'egaauth.socialaccount.providers.agave',
    'egaauth.socialaccount.providers.amazon',
    'egaauth.socialaccount.providers.amazon_cognito',
    'egaauth.socialaccount.providers.angellist',
    'egaauth.socialaccount.providers.apple',
    'egaauth.socialaccount.providers.asana',
    'egaauth.socialaccount.providers.auth0',
    'egaauth.socialaccount.providers.authentiq',
    'egaauth.socialaccount.providers.baidu',
    'egaauth.socialaccount.providers.basecamp',
    'egaauth.socialaccount.providers.battlenet',
    'egaauth.socialaccount.providers.bitbucket_oauth2',
    'egaauth.socialaccount.providers.bitly',
    'egaauth.socialaccount.providers.box',
    'egaauth.socialaccount.providers.cilogon',
    'egaauth.socialaccount.providers.clever',
    'egaauth.socialaccount.providers.coinbase',
    'egaauth.socialaccount.providers.dataporten',
    'egaauth.socialaccount.providers.daum',
    'egaauth.socialaccount.providers.digitalocean',
    'egaauth.socialaccount.providers.dingtalk',
    'egaauth.socialaccount.providers.discogs',
    'egaauth.socialaccount.providers.discord',
    'egaauth.socialaccount.providers.disqus',
    'egaauth.socialaccount.providers.douban',
    'egaauth.socialaccount.providers.doximity',
    'egaauth.socialaccount.providers.draugiem',
    'egaauth.socialaccount.providers.drip',
    'egaauth.socialaccount.providers.dropbox',
    'egaauth.socialaccount.providers.dwolla',
    'egaauth.socialaccount.providers.edmodo',
    'egaauth.socialaccount.providers.edx',
    'egaauth.socialaccount.providers.eventbrite',
    'egaauth.socialaccount.providers.eveonline',
    'egaauth.socialaccount.providers.evernote',
    'egaauth.socialaccount.providers.exist',
    'egaauth.socialaccount.providers.facebook',
    'egaauth.socialaccount.providers.feedly',
    'egaauth.socialaccount.providers.figma',
    'egaauth.socialaccount.providers.fivehundredpx',
    'egaauth.socialaccount.providers.flickr',
    'egaauth.socialaccount.providers.foursquare',
    'egaauth.socialaccount.providers.frontier',
    'egaauth.socialaccount.providers.fxa',
    'egaauth.socialaccount.providers.gitea',
    'egaauth.socialaccount.providers.github',
    'egaauth.socialaccount.providers.gitlab',
    'egaauth.socialaccount.providers.globus',
    'egaauth.socialaccount.providers.google',
    'egaauth.socialaccount.providers.gumroad',
    'egaauth.socialaccount.providers.hubic',
    'egaauth.socialaccount.providers.instagram',
    'egaauth.socialaccount.providers.jupyterhub',
    'egaauth.socialaccount.providers.kakao',
    'egaauth.socialaccount.providers.lemonldap',
    "egaauth.socialaccount.providers.lichess",
    'egaauth.socialaccount.providers.line',
    'egaauth.socialaccount.providers.linkedin',
    'egaauth.socialaccount.providers.linkedin_oauth2',
    'egaauth.socialaccount.providers.mailchimp',
    'egaauth.socialaccount.providers.mailcow',
    'egaauth.socialaccount.providers.mailru',
    'egaauth.socialaccount.providers.mediawiki',
    'egaauth.socialaccount.providers.meetup',
    'egaauth.socialaccount.providers.miro',
    'egaauth.socialaccount.providers.microsoft',
    'egaauth.socialaccount.providers.naver',
    'egaauth.socialaccount.providers.nextcloud',
    'egaauth.socialaccount.providers.notion',
    'egaauth.socialaccount.providers.odnoklassniki',
    'egaauth.socialaccount.providers.openid',
    'egaauth.socialaccount.providers.openid_connect',
    'egaauth.socialaccount.providers.openstreetmap',
    'egaauth.socialaccount.providers.orcid',
    'egaauth.socialaccount.providers.patreon',
    'egaauth.socialaccount.providers.paypal',
    'egaauth.socialaccount.providers.persona',
    'egaauth.socialaccount.providers.pinterest',
    'egaauth.socialaccount.providers.pocket',
    "egaauth.socialaccount.providers.questrade",
    'egaauth.socialaccount.providers.quickbooks',
    'egaauth.socialaccount.providers.reddit',
    'egaauth.socialaccount.providers.robinhood',
    'egaauth.socialaccount.providers.salesforce',
    'egaauth.socialaccount.providers.sharefile',
    'egaauth.socialaccount.providers.shopify',
    'egaauth.socialaccount.providers.slack',
    'egaauth.socialaccount.providers.snapchat',
    'egaauth.socialaccount.providers.soundcloud',
    'egaauth.socialaccount.providers.spotify',
    'egaauth.socialaccount.providers.stackexchange',
    'egaauth.socialaccount.providers.steam',
    'egaauth.socialaccount.providers.stocktwits',
    'egaauth.socialaccount.providers.strava',
    'egaauth.socialaccount.providers.stripe',
    'egaauth.socialaccount.providers.telegram',
    'egaauth.socialaccount.providers.trainingpeaks',
    'egaauth.socialaccount.providers.trello',
    'egaauth.socialaccount.providers.tumblr',
    'egaauth.socialaccount.providers.tumblr_oauth2',
    'egaauth.socialaccount.providers.twentythreeandme',
    'egaauth.socialaccount.providers.twitch',
    'egaauth.socialaccount.providers.twitter',
    'egaauth.socialaccount.providers.twitter_oauth2',
    'egaauth.socialaccount.providers.untappd',
    'egaauth.socialaccount.providers.vimeo',
    'egaauth.socialaccount.providers.vimeo_oauth2',
    'egaauth.socialaccount.providers.vk',
    'egaauth.socialaccount.providers.wahoo',
    'egaauth.socialaccount.providers.weibo',
    'egaauth.socialaccount.providers.weixin',
    'egaauth.socialaccount.providers.windowslive',
    'egaauth.socialaccount.providers.xing',
    'egaauth.socialaccount.providers.yahoo',
    'egaauth.socialaccount.providers.yandex',
    'egaauth.socialaccount.providers.ynab',
    'egaauth.socialaccount.providers.zoho',
    'egaauth.socialaccount.providers.zoom',
    'egaauth.socialaccount.providers.okta',
    'egaauth.socialaccount.providers.feishu',
    "egaauth.socialaccount.providers.atlassian",
    ...
]

MIDDLEWARE = (
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",

    # Add the account middleware:
    "egaauth.account.middleware.AccountMiddleware",
)

# Provider specific settings
SOCIALACCOUNT_PROVIDERS = {
    'google': {
        # For each OAuth based provider, either add a ``SocialApp``
        # (``socialaccount`` app) containing the required client
        # credentials, or list them here:
        'APP': {
            'client_id': '123',
            'secret': '456',
            'key': ''
        }
    }
}

Additionally, add this to your project urls.py:

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

Note that you do not necessarily need the URLs provided by django.contrib.auth.urls. Instead of the URLs login, logout, and password_change (among others), you can use the URLs provided by egaauth: account_login, account_logout, account_set_password...

Important: This project is not designed to work with SESSION_ENGINE set to "django.contrib.sessions.backends.signed_cookies". With signed cookies, the session data is signed but not encrypted, whereas egaauth stores secrets (e.g. verification codes) in the session.

Post-Installation

In your Django root execute the command below to create your database tables:

python manage.py migrate

Now start your server, visit your admin pages (e.g. http://localhost:8000/admin/) and follow these steps:

Example Projects

The source code repository contains fully functional example projects to help you get started.

Regular Django Project

The Regular Django project is a Bootstrap styled example project, which provides an example of how egaauth can be styled without altering any of the builtin templates. Please take a look at the templates of the example project. You can see this project running live over at django.demo.egaauth.org.

React Single-Page Application

The React example application demonstrates how the egaauth headless API can be put to use in practice. The documentation can be found in the README. Instructions on how to run it locally can be found there as well, though you can also view a copy running over at react.demo.egaauth.org.

Regular Accounts

Introduction

A regular account is a user account, identified by an email address or username, and protected by a password. The egaauth.account app is responsible for managing regular accounts. It supports:

Configuration

Overall

ACCOUNT_ADAPTER (default: "egaauth.account.adapter.DefaultAccountAdapter")

Specifies the adapter class to use, allowing you to alter certain default behaviour.

ACCOUNT_FORMS

Used to override the builtin forms. Defaults to:

ACCOUNT_FORMS = {
    'add_email': 'egaauth.account.forms.AddEmailForm',
    'change_password': 'egaauth.account.forms.ChangePasswordForm',
    'confirm_login_code': 'egaauth.account.forms.ConfirmLoginCodeForm',
    'login': 'egaauth.account.forms.LoginForm',
    'request_login_code': 'egaauth.account.forms.RequestLoginCodeForm',
    'reset_password': 'egaauth.account.forms.ResetPasswordForm',
    'reset_password_from_key': 'egaauth.account.forms.ResetPasswordKeyForm',
    'set_password': 'egaauth.account.forms.SetPasswordForm',
    'signup': 'egaauth.account.forms.SignupForm',
    'user_token': 'egaauth.account.forms.UserTokenForm',
}
ACCOUNT_PREVENT_ENUMERATION (default: True)

Controls whether or not information is revealed about whether or not a user account exists. For example, by entering random email addresses in the password reset form you can test whether or not those email addresses are associated with an account. Enabling this setting prevents that, and an email is always sent, regardless of whether or not the account exists. Note that there is a slight usability tax to pay because there is no immediate feedback.

Whether or not enumeration can be prevented during signup depends on the email verification method. In case of mandatory verification, enumeration can be properly prevented because the case where an email address is already taken is indistinguishable from the case where it is not. However, in case of optional or disabled email verification, enumeration can only be prevented by allowing the signup to go through, resulting in multiple accounts sharing same email address (although only one of the accounts can ever have it verified). When enumeration prevention is set to True, email address uniqueness takes precedence over enumeration prevention, and the issue of multiple accounts having the same email address will be avoided, thus leaking information. Set it to "strict" to allow for signups to go through.

ACCOUNT_RATE_LIMITS (default: {...})

In order to be secure out of the box various rate limits are in place. See Rate Limits for details.

ACCOUNT_SESSION_REMEMBER (default: None)

Controls the life time of the session. Set to None to ask the user ("Remember me?"), False to not remember, and True to always remember.

ACCOUNT_TEMPLATE_EXTENSION (default: "html")

A string defining the template extension to use, defaults to html.

Signup

ACCOUNT_SIGNUP_FIELDS (default: ['username*', 'email', 'password1*', 'password2*'])

The list of fields to complete in the signup form: username, email, phone, password1, password2.. Fields marked with an asterisk (e.g. 'username*') are required. To let the user type in their email address twice to avoid typos, you can add 'email2'. The field 'password2' can be used let the user type in their password twice to avoid typos.

ACCOUNT_SIGNUP_FORM_CLASS (default: None)

A string pointing to a custom form class (e.g. 'myapp.forms.SignupForm') that is used during signup to ask the user for additional input (e.g. a newsletter signup checkbox, or birth date). This class should derive from just forms.Form and only list the additional fields you need. It must implement a def signup(self, request, user) method, which is called during the signup process. This method allows you to handle and store the submitted data as needed.

ACCOUNT_SIGNUP_FORM_HONEYPOT_FIELD (default: None)

A string value that will be used as the HTML 'name' property on a honeypot input field on the sign up form. Honeypot fields are hidden to normal users but might be filled out by naive spam bots. When the field is filled out the app will not create a new user and attempt to fool the bot with a fake successful response. We recommend setting this to some believable value that your app does not actually collect on signup e.g. 'phone_number' or 'address'. Honeypots are not always successful for sophisticated bots so this should be used as one layer in a suite of spam detection tools if your site is having trouble with spam.

Login

ACCOUNT_LOGIN_BY_CODE_ENABLED (default: False)

"Login by email" offers an alternative method of logging in. Instead of entering an email address and accompanying password, the user only enters the email address. Then, a one-time code is sent to that email address which allows the user to login. This method is often referred to as "Magic Code Login". This setting controls whether or not this method of logging in is enabled.

ACCOUNT_LOGIN_BY_CODE_TRUST_ENABLED (default: False)

Indicates whether the MFA "Trust this browser?" functionality is to be enabled for logging in by code. Note that this requires the MFA app to be installed.

ACCOUNT_LOGIN_BY_CODE_MAX_ATTEMPTS (default: 3)

This setting controls the maximum number of attempts the user has at inputting a valid code.

ACCOUNT_LOGIN_BY_CODE_REQUIRED (default: False)

When enabled (in case of True), every user logging in is required to input a login confirmation code sent by email. Alternatively, you can specify a set of authentication methods ("password", "mfa", or "socialaccount") for which login codes are required.

ACCOUNT_LOGIN_BY_CODE_SUPPORTS_RESEND (default: False)

Whether or not the user can request a new login code.

ACCOUNT_LOGIN_BY_CODE_TIMEOUT (default: 180)

The code that is emailed has a limited life span. It expires this many seconds after which it was sent.

ACCOUNT_LOGIN_BY_CODE_FORMAT (default: settings.egaauth_USER_CODE_FORMAT)

Controls the format of the login code.

ACCOUNT_LOGIN_METHODS (default: {"username"}, options: "email" or "username")

Specifies the login method to use -- whether the user logs in by entering their username, email address, or either one of both. Note that the login methods need to align with ACCOUNT_SIGNUP_FIELDS, as specifying a login method that you cannot sign up with typically points to a configuration error.

ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION (default: False)

The default behavior is not log users in and to redirect them to ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL.

By changing this setting to True, users will automatically be logged in once they confirm their email address. Note however that this only works when confirming the email address immediately after signing up, assuming users didn't close their browser or used some sort of private browsing mode.

Note that this setting only affects email verification by link. It has no affect in case you turn on code based verification (ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED).

ACCOUNT_LOGIN_ON_PASSWORD_RESET (default: False)

By changing this setting to True, users will automatically be logged in once they have reset their password. By default they are redirected to the password reset done page.

ACCOUNT_LOGIN_TIMEOUT (default: 900)

The maximum allowed time (in seconds) for a login to go through the various login stages. This limits, for example, the time span that the 2FA stage remains available.

Logout

ACCOUNT_LOGOUT_ON_GET (default: False)

Determines whether or not the user is automatically logged out by a GET request. GET is not designed to modify the server state, and in this case it can be dangerous. See LogoutView in the documentation for details.

ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE (default: False)

Determines whether or not the user is automatically logged out after changing or setting their password. See documentation for Django's session invalidation on password change.

Password Reset

ACCOUNT_PASSWORD_INPUT_RENDER_VALUE (default: False)

render_value parameter as passed to PasswordInput fields.

ACCOUNT_PASSWORD_RESET_BY_CODE_ENABLED (default: False)

Controls whether password reset is performed by means of following a link in the email (False), or by entering a code (True).

ACCOUNT_PASSWORD_RESET_BY_CODE_FORMAT (default: settings.egaauth_USER_CODE_FORMAT)

Controls the format of the password reset code.

ACCOUNT_PASSWORD_RESET_BY_CODE_MAX_ATTEMPTS (default: 3)

This setting controls the maximum number of attempts the user has at inputting a valid code.

ACCOUNT_PASSWORD_RESET_BY_CODE_TIMEOUT (default: 180)

The code that is emailed has a limited life span. It expires this many seconds after which it was sent.

ACCOUNT_PASSWORD_RESET_TOKEN_GENERATOR (default: "egaauth.account.forms.EmailAwarePasswordResetTokenGenerator")

A string pointing to a custom token generator (e.g. 'myapp.auth.CustomTokenGenerator') for password resets. This class should implement the same methods as django.contrib.auth.tokens.PasswordResetTokenGenerator or subclass it.

Email Verification

ACCOUNT_CONFIRM_EMAIL_ON_GET (default: False)

Determines whether or not an email address is automatically confirmed by a GET request. GET is not designed to modify the server state, though it is commonly used for email confirmation. To avoid requiring user interaction, consider using POST via Javascript in your email confirmation template as an alternative to setting this to True.

ACCOUNT_EMAIL_CONFIRMATION_HMAC (default: True)

In order to verify an email address, a key is mailed identifying the email address to be verified. In previous versions, a record was stored in the database for each ongoing email confirmation, keeping track of these keys. Current versions use HMAC based keys that do not require server side state.

ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS (default: 3)

Determines the expiration date of email confirmation mails (# of days).

ACCOUNT_EMAIL_VERIFICATION (default: "optional")

Determines the email verification method during signup -- choose one of "mandatory", "optional", or "none".

When set to "mandatory" the user is blocked from logging in until the email address is verified. Choose "optional" or "none" to allow logins with an unverified email address. In case of "optional", the email verification mail is still sent, whereas in case of "none" no email verification mails are sent.

Setting this to "mandatory" requires "email*" to be listed in ACCOUNT_SIGNUP_FIELDS.

ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED (default: False)

Controls whether email verification is performed by means of following a link in the email (False), or by entering a code (True).

ACCOUNT_EMAIL_VERIFICATION_BY_CODE_FORMAT (default: settings.egaauth_USER_CODE_FORMAT)

Controls the format of the email verification code.

ACCOUNT_EMAIL_VERIFICATION_BY_CODE_MAX_ATTEMPTS (default: 3)

This setting controls the maximum number of attempts the user has at inputting a valid code.

ACCOUNT_EMAIL_VERIFICATION_BY_CODE_TIMEOUT (default: 900)

The code that is emailed has a limited life span. It expires this many seconds after which it was sent.

ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_CHANGE (default: False)

Whether or not the email can be changed after signup at the email verification stage.

Warning: If enumeration prevention is turned on, no account is created when a user signs up using an already existing email. If the user then were able to change to a new email address that is not taken, we would have to create an account as we did not do so yet. Currently, this is not implemented. Changing email address in this scenario is simply not allowed, and therefore, a user paying close attention can deduce that an account already exists. This will be revisited in a future release.

ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_RESEND (default: False)

Whether or not the user can request a new email verification code.

Reauthentication

ACCOUNT_REAUTHENTICATION_TIMEOUT (default: 300)

Before asking the user to reauthenticate, we check if a successful (re)authentication happened within the amount of seconds specified here, and if that is the case, the new reauthentication flow is silently skipped.

ACCOUNT_REAUTHENTICATION_REQUIRED (default: False)

Specifies whether or not reauthentication is required before the user can alter his account.

Routing

ACCOUNT_AUTHENTICATED_LOGIN_REDIRECTS (default: True)

The default behaviour is to redirect authenticated users to LOGIN_REDIRECT_URL when they try accessing login/signup pages.

By changing this setting to False, logged in users will not be redirected when they access login/signup pages.

ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL (default: settings.LOGIN_URL)

The URL to redirect to after a successful email confirmation, in case no user is logged in.

ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL (default: None)

The URL to redirect to after a successful email confirmation, in case of an authenticated user. Set to None to use settings.LOGIN_REDIRECT_URL.

ACCOUNT_LOGOUT_REDIRECT_URL (default: settings.LOGOUT_REDIRECT_URL or "/")

The URL (or URL name) to return to after the user logs out. Defaults to Django's LOGOUT_REDIRECT_URL, unless that is empty, then "/" is used.

ACCOUNT_SIGNUP_REDIRECT_URL (default: settings.LOGIN_REDIRECT_URL)

The URL (or URL name) to redirect to directly after signing up. Note that users are only redirected to this URL if the signup went through uninterruptedly, for example, without any side steps due to email verification. If your project requires the user to always pass through certain onboarding views after signup, you will have to keep track of state indicating whether or not the user successfully onboarded, and handle accordingly.

Sending Email

ACCOUNT_EMAIL_SUBJECT_PREFIX (default: "[Site] ")

Subject-line prefix to use for email messages sent. By default, the name of the current Site (django.contrib.sites) is used.

ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS (default: True)

Configures whether password reset attempts for email addresses which do not have an account result in sending an email.

ACCOUNT_EMAIL_NOTIFICATIONS (default: False)

When enabled, account related security notifications, such as "Your password was changed", including information on user agent / IP address from where the change originated, will be emailed.

Email Addresses

ACCOUNT_CHANGE_EMAIL (default: False)

When disabled (False), users can add one or more email addresses (up to a maximum of ACCOUNT_MAX_EMAIL_ADDRESSES) to their account and freely manage those email addresses. When enabled (True), users are limited to having exactly one email address that they can change by adding a temporary second email address that, when verified, replaces the current email address.

ACCOUNT_EMAIL_MAX_LENGTH (default: 254)

Maximum length of the email field. You won't need to alter this unless using MySQL with the InnoDB storage engine and the utf8mb4 charset, and only in versions lower than 5.7.7, because the default InnoDB settings don't allow indexes bigger than 767 bytes. When using utf8mb4, characters are 4-bytes wide, so at maximum column indexes can be 191 characters long (767/4). Unfortunately Django doesn't allow specifying index lengths, so the solution is to reduce the length in characters of indexed text fields. More information can be found at MySQL's documentation on converting between 3-byte and 4-byte Unicode character sets.

ACCOUNT_MAX_EMAIL_ADDRESSES (default: None)

The maximum amount of email addresses a user can associate to his account. It is safe to change this setting for an already running project -- it will not negatively affect users that already exceed the allowed amount. Note that if you set the maximum to 1, users will not be able to change their email address.

ACCOUNT_UNIQUE_EMAIL (default: True)

Enforce uniqueness of email addresses. On the database level, this implies that only one user account can have an email address marked as verified. Forms prevent a user from registering with or adding an additional email address if that email address is in use by another account.

User Model

ACCOUNT_PRESERVE_USERNAME_CASING (default: True)

This setting determines whether the username is stored in lowercase (False) or whether its casing is to be preserved (True). Note that when casing is preserved, potentially expensive __iexact lookups are performed when filter on username. For now, the default is set to True to maintain backwards compatibility.

ACCOUNT_USERNAME_BLACKLIST (default: [])

A list of usernames that can't be used by user.

ACCOUNT_USER_DISPLAY (default: a callable returning user.username)

A callable (or string of the form 'some.module.callable_name') that takes a user as its only argument and returns the display name of the user. The default implementation returns user.username.

ACCOUNT_USER_MODEL_EMAIL_FIELD (default: "email")

The name of the field containing the email, if any. See custom user models.

ACCOUNT_USER_MODEL_USERNAME_FIELD (default: "username")

The name of the field containing the username, if any. See custom user models.

ACCOUNT_USERNAME_MIN_LENGTH (default: 1)

An integer specifying the minimum allowed length of a username.

ACCOUNT_USERNAME_VALIDATORS (default: None)

A path ('some.module.validators.custom_username_validators') to a list of custom username validators. If left unset, the validators setup within the user model username field are used.

Example:

# In validators.py

from django.contrib.auth.validators import ASCIIUsernameValidator

custom_username_validators = [ASCIIUsernameValidator()]

# In settings.py

ACCOUNT_USERNAME_VALIDATORS = 'some.module.validators.custom_username_validators'

Rate Limits

In this section the ratelimits related to the egaauth.account app are documented. Refer to the overall rate limit documentation for more background information on the mechanism itself.

The rate limits are configured through the ACCOUNT_RATE_LIMITS setting:

The following actions are available for configuration:

"change_password" (default: "5/m/user")

Changing the password (for already authenticated users).

"change_phone" (default: "1/m/user")

Changing the phone number.

"manage_email" (default: "10/m/user")

Email management related actions, such as add, remove, change primary.

"reset_password" (default: "20/m/ip,5/m/key")

Requesting a password reset. The email for which the password is to be reset is passed as the key.

"reauthenticate" (default: "10/m/user")

Reauthentication (for users already logged in).

"reset_password_from_key" (default: "20/m/ip")

Password reset (the view the password reset email links to).

"signup" (default: "20/m/ip")

Signups.

"login" (default: "30/m/ip")

Logins.

"login_failed" (default: "10/m/ip,5/5m/key")

Restricts the allowed number of failed login attempts. When exceeded, the user is prohibited from logging in for the remainder of the rate limit. Important: while this protects the egaauth login view, it does not protect Django's admin login from being brute forced.

"confirm_email" (default: "1/3m/key" (link) or "1/10s/key" (code))

Users can request email confirmation mails via the email management view, and, implicitly, when logging in with an unverified account. This rate limit prevents users from sending too many of these mails.

Views

Login

URL name:

account_login

Users login via the egaauth.account.views.LoginView view over at /accounts/login/ (URL name account_login). When users attempt to login while their account is inactive (user.is_active) they are presented with the account/account_inactive.html template.

Signup

URL name:

account_signup

Users sign up via the egaauth.account.views.SignupView view over at /accounts/signup/ (URL name account_signup).

Logout

URL name:

account_logout

The logout view (egaauth.account.views.LogoutView) over at /accounts/logout/ (URL name account_logout) requests for confirmation before logging out. The user is logged out only when the confirmation is received by means of a POST request.

If you are wondering why, consider what happens when a malicious user embeds the following image in a post:

<img src="http://example.com/accounts/logout/">

For this and more background information on the subject, see:

If you insist on having logout on GET, then please consider adding a bit of Javascript to automatically turn a click on a logout link into a POST. As a last resort, you can set ACCOUNT_LOGOUT_ON_GET to True.

Password Management

Authenticated users can manage their password account using the egaauth.account.views.PasswordSetView and egaauth.account.views.PasswordChangeView views, over at /accounts/password/set/ respectively /accounts/password/change/ (URL names account_set_password and account_change_password respectively).

Users are redirected between these views, according to whether or not they have setup a password (user.has_usable_password()). Typically, when users signup via a social provider they will not have a password set.

Password Reset

URL name:

account_reset_password

Users can request a password reset using the egaauth.account.views.PasswordResetView view over at /accounts/password/reset/ (URL name account_reset_password). An email will be sent containing a reset link pointing to PasswordResetFromKeyView view.

Emails Management

URL name:

account_email

Users manage the email addresses tied to their account using the egaauth.account.views.EmailView view over at /accounts/email/ (URL name account_email). Here, users can add (and verify) email addresses, remove email addresses, and choose a new primary email address.

Email Verification

Depending on the setting ACCOUNT_EMAIL_VERIFICATION, a verification email is sent pointing to the egaauth.account.views.ConfirmEmailView view.

The setting ACCOUNT_CONFIRM_EMAIL_ON_GET determines whether users have to manually confirm the address by submitting a confirmation form, or whether the address is automatically confirmed by a mere GET request.

Template Tags

Use user_display to render a user name without making assumptions on how the user is represented (e.g. render the username, or first name?):

{% load account %}

{% user_display user %}

Or, if you need to use in a {% blocktrans %}:

{% load account %}

{% user_display user as user_display %}
{% blocktrans %}{{ user_display }} has logged in...{% endblocktrans %}

Then, override the ACCOUNT_USER_DISPLAY setting with your project specific user display callable.

If you set ACCOUNT_USERNAME_REQUIRED = False and ACCOUNT_USER_MODEL_USERNAME_FIELD = None, then you can simply display the user.email with {{ user }}:

In case you forgot, your username is {{ user }}.

Forms

Login

Path:

egaauth.account.forms.LoginForm

Used on:

account_login view.

Example override:

from egaauth.account.forms import LoginForm
class MyCustomLoginForm(LoginForm):

    def login(self, *args, **kwargs):

        # Add your own processing here.

        # You must return the original result.
        return super().login(*args, **kwargs)

You have access to the following:

settings.py:

ACCOUNT_FORMS = {'login': 'mysite.forms.MyCustomLoginForm'}

By default, the password field renders a "Forgot your password?" link as the help text. By providing a "account/password_reset_help_text.html" template you can customize that text.

Signup

Path:

egaauth.account.forms.SignupForm

Used on:

account_signup view.

Example override:

from egaauth.account.forms import SignupForm
class MyCustomSignupForm(SignupForm):

    def save(self, request):

        # Ensure you call the parent class's save.
        # .save() returns a User object.
        user = super().save(request)

        # Add your own processing here.

        # You must return the original result.
        return user

settings.py:

ACCOUNT_FORMS = {'signup': 'mysite.forms.MyCustomSignupForm'}

Add Email

Path:

egaauth.account.forms.AddEmailForm

Used on:

account_email view.

Example override:

from egaauth.account.forms import AddEmailForm
class MyCustomAddEmailForm(AddEmailForm):

    def save(self, request):

        # Ensure you call the parent class's save.
        # .save() returns an egaauth.account.models.EmailAddress object.
        email_address_obj = super().save(request)

        # Add your own processing here.

        # You must return the original result.
        return email_address_obj

You have access to the following:

settings.py:

ACCOUNT_FORMS = {'add_email': 'mysite.forms.MyCustomAddEmailForm'}

Change Password

Path:

egaauth.account.forms.ChangePasswordForm

Used on:

account_change_password view.

Example override:

from egaauth.account.forms import ChangePasswordForm
class MyCustomChangePasswordForm(ChangePasswordForm):

    def save(self):

        # Ensure you call the parent class's save.
        # .save() does not return anything
        super().save()

        # Add your own processing here.

You have access to the following:

settings.py:

ACCOUNT_FORMS = {'change_password': 'mysite.forms.MyCustomChangePasswordForm'}

Set Password

Path:

egaauth.account.forms.SetPasswordForm

Used on:

account_set_password view.

Example override:

from egaauth.account.forms import SetPasswordForm
class MyCustomSetPasswordForm(SetPasswordForm):

    def save(self):

        # Ensure you call the parent class's save.
        # .save() does not return anything
        super().save()

        # Add your own processing here.

You have access to the following:

settings.py:

ACCOUNT_FORMS = {'set_password': 'mysite.forms.MyCustomSetPasswordForm'}

Reset Password

Path:

egaauth.account.forms.ResetPasswordForm

Used on:

account_reset_password view.

Example override:

from egaauth.account.forms import ResetPasswordForm
class MyCustomResetPasswordForm(ResetPasswordForm):

    def save(self, request):

        # Ensure you call the parent class's save.
        # .save() returns a string containing the email address supplied
        email_address = super().save(request)

        # Add your own processing here.

        # Ensure you return the original result
        return email_address

You have access to the following:

settings.py:

ACCOUNT_FORMS = {'reset_password': 'mysite.forms.MyCustomResetPasswordForm'}

Reset Password From Key

Path:

egaauth.account.forms.ResetPasswordKeyForm

Used on:

account_reset_password view.

Example override:

from egaauth.account.forms import ResetPasswordKeyForm
class MyCustomResetPasswordKeyForm(ResetPasswordKeyForm):

    def save(self):

        # Add your own processing here.

        # Ensure you call the parent class's save.
        # .save() does not return anything
        super().save()

You have access to the following:

settings.py:

ACCOUNT_FORMS = {'reset_password_from_key': 'mysite.forms.MyCustomResetPasswordKeyForm'}

Decorators

Verified Email Required

Even when email verification is not mandatory during signup, there may be circumstances during which you really want to prevent unverified users from proceeding. For this purpose you can use the following decorator:

from egaauth.account.decorators import verified_email_required

@verified_email_required
def verified_users_only_view(request):
    ...

The behavior is as follows:

Signals

There are several signals emitted during authentication flows. You can hook to them for your own needs.

Email

Case Sensitivity

Historically, email addresses started out as case sensitive because the local part (the part before the "@") could represent a case sensitive user name. However, over time, this proved to be a bad idea and the RFCs that succeeded the original were adjusted to move away from treating email addresses as case sensitive.

To deal with this, previous versions of django-egaauth used to store email addresses in their original case, while performing lookups in a case insensitive style. This approach led to subtle bugs in upstream code, and also comes at a performance cost (__iexact lookups). The latter requires case insensitive index support, which not all databases support. Re-evaluating the approach in current times has led to the conclusion that the benefits do not outweigh the costs. Therefore, email addresses are now always stored as lower case.

Phone

Installation

In addition to following the overall instructions, pay attention to the following in the settings.py of your project:

# Make sure that the login methods includes "phone" as a method.
ACCOUNT_LOGIN_METHODS = {"phone", "email"}

# Add a required phone field to the signup fields.
# ACCOUNT_SIGNUP_FIELDS = [
  'phone*',
  'email*'  # Can be left out if you want to only use 'phone'.
]

# You will need to provide methods for storing phone numbers, and
# sending SMS messages in a custom adapter.
ACCOUNT_ADAPTER = 'project.users.adapter.MyAccountAdapter'

Configuration

Available settings:

ACCOUNT_PHONE_VERIFICATION_ENABLED (default: True)

Whether or not mandatory verification of phone numbers during login/signup takes place.

ACCOUNT_PHONE_VERIFICATION_MAX_ATTEMPTS (default: 3)

This setting controls the maximum number of attempts the user has at inputting a valid code.

ACCOUNT_PHONE_VERIFICATION_TIMEOUT (default: 900)

The code that is sent has a limited life span. It expires this many seconds after which it was sent.

ACCOUNT_PHONE_VERIFICATION_SUPPORTS_CHANGE (default: False)

Whether or not the phone number can be changed after signup at the phone number verification stage.

Warning: the warning related to enumeration prevent over at

ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_CHANGE holds here as well.

ACCOUNT_PHONE_VERIFICATION_SUPPORTS_RESEND (default: False)

Whether or not the user can request a new phone number verification code.

ACCOUNT_PHONE_VERIFICATION_CODE_FORMAT (default: settings.egaauth_USER_CODE_FORMAT)

Controls the format of the verification code.

Form Fields

For presenting a phone number form field to the user a basic <input type="tel"> field is used that requires input in E164 format. There are various external projects that offer more elaborate phone number input fields. You can switch over to using the fields provided by those projects, or, tweak the phone number validaton logic, by overriding the following adapter methods:

egaauth.account.adapter.DefaultAccountAdapter

phone_form_field

clean_phone

Database Models

Out of the box, there are no models provided intended to store the phone numbers of users. It is up to the developer to decide where phone numbers are to be stored, for example, on a custom user model, or, on a separate Phone model of its own. Once those the models are setup, the following adapter methods need to be populated so that the models will be used:

egaauth.account.adapter.DefaultAccountAdapter

get_phone

set_phone

set_phone_verified

get_user_by_phone

Sending SMS Messages

For sending SMS messages, various external providers and packages are available. You can integrate those by overriding the following adapter method:

egaauth.account.adapter.DefaultAccountAdapter

send_verification_code_sms

send_unknown_account_sms

Turnkey Phone Authentication

egaauth.contrib.phone turns the existing phone-auth extension points into a reusable packaged setup with a bundled phone model, admin integration, health checks, and optional external verification-provider support.

Installation

Add the app and adapter:

INSTALLED_APPS += [
    "egaauth.contrib.phone",
]

ACCOUNT_ADAPTER = "egaauth.contrib.phone.adapter.PhoneAccountAdapter"
ACCOUNT_LOGIN_METHODS = {"email", "phone"}
ACCOUNT_SIGNUP_FIELDS = ["phone*", "email", "password1*"]

For local development, the default provider is console and verification codes are logged server-side while verification remains handled by egaauth's existing code flow.

Twilio Verify

To delegate verification-code checking to Twilio Verify:

EGAAUTH_PHONE_PROVIDER = "twilio_verify"
EGAAUTH_PHONE_TWILIO_ACCOUNT_SID = "..."
EGAAUTH_PHONE_TWILIO_AUTH_TOKEN = "..."
EGAAUTH_PHONE_TWILIO_VERIFY_SERVICE_SID = "VA..."
EGAAUTH_PHONE_DEFAULT_CHANNEL = "sms"

When using twilio_verify, the library starts the verification through Twilio and also validates the submitted code through Twilio instead of solely relying on the locally generated session code.

What it provides

Enumeration-prevention messages

By default, enumeration-prevention SMS messages are logged/no-op because many projects prefer not to send separate informational messages for unknown or already-registered numbers. If desired:

EGAAUTH_PHONE_SEND_ENUMERATION_PREVENTION_SMS = True

Adapter

egaauth.account.adapter.DefaultAccountAdapter

Advanced Usage

Custom User Models

If you use a custom user model you need to specify what field represents the username, if any. Here, username really refers to the field representing the nickname that the user uses to login, and not to some unique identifier (possibly including an email address) as is the case for Django's AbstractBaseUser.USERNAME_FIELD.

Therefore, if your custom user model does not have a username field (again, not to be mistaken with an email address or user id), you will need to set ACCOUNT_USER_MODEL_USERNAME_FIELD to None. This will disable username related functionality in egaauth. Remember to also remove username from ACCOUNT_SIGNUP_FIELDS as by default that is present.

Similarly, you will need to set ACCOUNT_USER_MODEL_EMAIL_FIELD to None or to the proper field (if other than email).

For example, if you want to use a custom user model that has email as the identifying field, and you don't want to collect usernames, you need the following in your settings.py:

ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_SIGNUP_FIELDS = ['email*', 'password1*', 'password2*']
ACCOUNT_LOGIN_METHODS = {'email'}

Creating and Populating User instances

The following adapter methods can be used to intervene in how User instances are created and populated with data

Invitations

Invitation handling is not supported, and most likely will not be any time soon. An invitation app could cover anything ranging from invitations of new users, to invitations of existing users to participate in restricted parts of the site. All in all, the scope of invitation handling is large enough to warrant being addressed in an app of its own.

Still, everything is in place to easily hook up any third party invitation app. The account adapter (egaauth.account.adapter.DefaultAccountAdapter) offers the following methods:

Custom Redirects

If redirecting to statically configurable URLs (as specified in your project settings) is not flexible enough, then you can override the following adapter methods:

For example, redirecting to /accounts/<username>/ can be implemented as follows:

# project/settings.py:
ACCOUNT_ADAPTER = 'project.users.adapter.MyAccountAdapter'

# project/users/adapter.py:
from django.conf import settings
from egaauth.account.adapter import DefaultAccountAdapter

class MyAccountAdapter(DefaultAccountAdapter):

    def get_login_redirect_url(self, request):
        path = "/accounts/{username}/"
        return path.format(username=request.user.username)

Third-Party ("Social") Accounts

Introduction

A third-party ("social") account is a user account where authentication is delegated to an external identity provider. The egaauth.socialaccount app is responsible for managing social accounts. It supports:

Note that in order to use this functionality you need to install the socialaccount extras of the django-egaauth package:

pip install "django-egaauth[socialaccount]"

Configuration

Available settings:

SOCIALACCOUNT_ADAPTER (default: "egaauth.socialaccount.adapter.DefaultSocialAccountAdapter")

Specifies the adapter class to use, allowing you to alter certain default behaviour.

SOCIALACCOUNT_AUTO_SIGNUP (default: True)

Attempt to bypass the signup form by using fields (e.g. username, email) retrieved from the social account provider. If a conflict arises due to a duplicate email address the signup form will still kick in.

SOCIALACCOUNT_EMAIL_AUTHENTICATION (default: False)

Consider a scenario where a social login occurs, and the social account comes with a verified email address (verified by the account provider), but that email address is already taken by a local user account. Additionally, assume that the local user account does not have any social account connected. Now, if the provider can be fully trusted, you can argue that we should treat this scenario as a login to the existing local user account even if the local account does not already have the social account connected, because --according to the provider -- the user logging in has ownership of the email address. This is how this scenario is handled when SOCIALACCOUNT_EMAIL_AUTHENTICATION is set to True. As this implies that an untrustworthy provider can login to any local account by fabricating social account data, this setting defaults to False. Only set it to True if you are using providers that can be fully trusted. Instead of turning this on globally, you can also turn it on selectively per provider, for example:

SOCIALACCOUNT_PROVIDERS = {
  'google': {
      'EMAIL_AUTHENTICATION': True
  }
}
SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT (default: False)

In case email authentication is applied, this setting controls whether or not the social account is automatically connected to the local account. In case of False the local account remains unchanged during the login. In case of True, the social account for which the email matched, is automatically added to the list of social accounts connected to the local account. As a result, even if the user were to change the email address afterwards, social login would still be possible when using True, but not in case of False.

SOCIALACCOUNT_EMAIL_VERIFICATION (default: ACCOUNT_EMAIL_VERIFICATION)

As ACCOUNT_EMAIL_VERIFICATION, but for social accounts.

SOCIALACCOUNT_EMAIL_REQUIRED (default: "email*" in ACCOUNT_SIGNUP_FIELDS)

The user is required to hand over an email address when signing up using a social account.

SOCIALACCOUNT_FORMS

Used to override forms. Defaults to:

SOCIALACCOUNT_FORMS = {
    'disconnect': 'egaauth.socialaccount.forms.DisconnectForm',
    'signup': 'egaauth.socialaccount.forms.SignupForm',
}
SOCIALACCOUNT_LOGIN_ON_GET (default: False)

Controls whether or not the endpoints for initiating a social login (for example, "/accounts/google/login/") require a POST request to initiate the handshake. For security considerations, it is strongly recommended to require POST requests.

SOCIALACCOUNT_PROVIDERS (default: {})

Dictionary containing provider specific settings.

SOCIALACCOUNT_REQUESTS_TIMEOUT (default: 5)

The timeout applied when performing upstream requests.

SOCIALACCOUNT_QUERY_EMAIL (default: "email*" in ACCOUNT_SIGNUP_FIELDS)

Request email address from 3rd party account provider? E.g. using OpenID AX, or the Facebook "email" permission.

SOCIALACCOUNT_SOCIALACCOUNT_STR (default: str of user object)

Used to override the str value for the SocialAccount model.

Must be a function accepting a single parameter for the socialaccount object.

SOCIALACCOUNT_STORE_TOKENS (default: False)

Indicates whether or not the access tokens are stored in the database. Note that tokens can only be stored if the related social account is stored as well, which is not the case when you are using SOCIALACCOUNT_EMAIL_AUTHENTICATION without SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT.

SOCIALACCOUNT_ONLY (default: False)

When enabled (True), all functionality with regard to local accounts is disabled, and users will only be able to authenticate using third-party providers.

SOCIALACCOUNT_OPENID_CONNECT_URL_PREFIX (default: "oidc")

The URL path prefix that is used for all OpenID Connect providers. By default, it is set to "oidc", meaning, an OpenID Connect provider with provider ID foo uses /accounts/oidc/foo/login/ as its login URL.

Provider Configuration

Providers typically require various configuration parameters before your users can authenticate with them. For example, for a regular OAuth provider you first need to setup an OAuth app over on the provider developer portal. Then, you need to configure the resulting client ID and client secret in your application.

Even though providers with other protocols may use different terminology, the overall idea remains the same. Throughout egaauth the term "social app" ("app" for short) refers to the unit of configuration of a provider. You provide the app configuration either in your project settings.py, or, by means of setting up SocialApp instances via the Django admin. When picking a method, consider the following:

Important: While you can mix both methods, be aware you need to avoid configuring one and the same provider both via settings.py and a SocialApp instance. In that case, it is not clear what app to pick, resulting in a MultipleObjectsReturned exception.

The examples presented in this documentation are all settings based. If you prefer the SocialApp based approach, simply create an entry via the Django admin and populate the fields exactly like listed in the example.

The SOCIALACCOUNT_PROVIDERS setting is used to configure providers and their apps. Next to the secrets that are configured per app, there are also parameters such as VERIFIED_EMAIL that hold for all apps. The following is an example configuration:

SOCIALACCOUNT_PROVIDERS = {
    "github": {
        # For each provider, you can choose whether or not the
        # email address(es) retrieved from the provider are to be
        # interpreted as verified.
        "VERIFIED_EMAIL": True
    },
    "google": {
        # For each OAuth based provider, either add a ``SocialApp``
        # (``socialaccount`` app) containing the required client
        # credentials, or list them here:
        "APPS": [
            {
                "client_id": "123",
                "secret": "456",
                "key": "",
                "settings": {
                    # You can fine tune these settings per app:
                    "scope": [
                        "profile",
                        "email",
                    ],
                    "auth_params": {
                        "access_type": "online",
                    },
                },
            },
        ],
        # The following provider-specific settings will be used for all apps:
        "SCOPE": [
            "profile",
            "email",
        ],
        "AUTH_PARAMS": {
            "access_type": "online",
        },
    }
}

Note that provider-specific settings are documented for each provider separately.

Views

Social Connections

URL name:

socialaccount_connections

The egaauth.socialaccount.views.ConnectionsView view over at /accounts/social/connections/ (URL name socialaccount_connections) allows users to manage the social accounts tied to their local account.

Template Tags

Use the provider_login_url tag to generate provider specific login URLs:

{% load socialaccount %}

<a href="{% provider_login_url "openid" openid="https://www.google.com/accounts/o8/id" next="/success/url/" %}">Google</a>
<a href="{% provider_login_url "twitter" %}">Twitter</a>

Here, you can pass along an optional process parameter that indicates how to process the social login. You can choose between login and connect:

<a href="{% provider_login_url "twitter" process="connect" %}">Connect a Twitter account</a>

Furthermore, you can pass along an action parameter with value reauthenticate to indicate that you want the user to be re-prompted for authentication even if they already signed in before. For now, this is supported by Facebook, Google, Twitter, and Discord only.

For Javascript based logins (e.g. when you enable the Facebook JS SDK), you will need to make sure that the required Javascript is loaded. The following tag loads all scripts for the enabled providers:

{% providers_media_js %}

For easy access to the social accounts for a user use:

{% get_social_accounts user as accounts %}

Then:

{{accounts.twitter}} -- a list of connected Twitter accounts
{{accounts.twitter.0}} -- the first Twitter account
{% if accounts %} -- if there is at least one social account

Finally, social authentication providers configured for the current site can be retrieved via:

{% get_providers as socialaccount_providers %}

Which will populate the socialaccount_providers variable in the template context with a list of configured social authentication providers. This supersedes the context processor used in version 0.21 and below.

Forms

Signup

Path:

egaauth.socialaccount.forms.SignupForm

Used on:

socialaccount_signup view used when a user initially signs up with a social account and needs to create an account.

Example override:

from egaauth.socialaccount.forms import SignupForm
class MyCustomSocialSignupForm(SignupForm):

    def save(self, request):

        # Ensure you call the parent class's save.
        # .save() returns a User object.
        user = super().save(request)

        # Add your own processing here.

        # You must return the original result.
        return user

You have access to the following:

settings.py:

SOCIALACCOUNT_FORMS = {'signup': 'mysite.forms.MyCustomSocialSignupForm'}

Disconnect

Path:

egaauth.socialaccount.forms.DisconnectForm

Used on:

socialaccount_connections view, used when removing a social account.

Example override:

from egaauth.socialaccount.forms import DisconnectForm
class MyCustomSocialDisconnectForm(DisconnectForm):

    def save(self):

        # Add your own processing here if you do need access to the
        # socialaccount being deleted.

        # Ensure you call the parent class's save.
        # .save() does not return anything
        super().save()

        # Add your own processing here if you don't need access to the
        # socialaccount being deleted.

You have access to the following:

settings.py:

SOCIALACCOUNT_FORMS = {'disconnect': 'mysite.forms.MyCustomSocialDisconnectForm'}

Signals

There are several signals emitted during authentication flows. You can hook to them for your own needs.

Providers

Introduction

Most providers require you to sign up for a so called API client or app, containing a client ID and API secret. You must add a SocialApp record per provider via the Django admin containing these app credentials.

When creating the OAuth app on the side of the provider pay special attention to the callback URL (sometimes also referred to as redirect URL). If you do not configure this correctly, you will receive login failures when attempting to log in, such as:

An error occurred while attempting to login via your social network account.

Use a callback URL of the form:

http://example.com/accounts/twitter/login/callback/
http://example.com/accounts/soundcloud/login/callback/
...

For local development, use the following:

http://127.0.0.1:8000/accounts/twitter/login/callback/

Protocol Specifics

Provider Specifics

Provider catalog at a glance

This build includes configuration notes for 114 provider documents shipped with the source tree.

  • 23andme
  • 500px
  • agave
  • amazon
  • amazon cognito
  • angellist
  • apple
  • atlassian
  • auth0
  • authelia
  • authentiq
  • baidu
  • basecamp
  • battlenet
  • bitbucket
  • box
  • cern
  • cilogon
  • clever
  • dataporten
  • daum
  • digitalocean
  • dingtalk
  • discogs
  • discord
  • doximity
  • draugiem
  • drip
  • dropbox
  • dwolla
  • edmodo
  • edx
  • eventbrite
  • eveonline
  • evernote
  • exist
  • facebook
  • feishu
  • figma
  • flickr
  • frontier
  • fxa
  • gitea
  • github
  • gitlab
  • globus
  • google
  • gumroad
  • hubspot
  • instagram
  • jupyterhub
  • kakao
  • keycloak
  • lemonldap
  • lichess
  • line
  • linkedin
  • mailchimp
  • mailcow
  • mediawiki
  • microsoft
  • miro
  • naver
  • netiq
  • nextcloud
  • notion
  • oauth2
  • odnoklassniki
  • okta
  • openid
  • openid connect
  • openstreetmap
  • orcid
  • patreon
  • paypal
  • pinterest
  • pocket
  • questrade
  • quickbooks
  • reddit
  • salesforce
  • saml
  • sharefile
  • shopify
  • slack
  • snapchat
  • soundcloud
  • stackexchange
  • steam
  • stocktwits
  • strava
  • stripe
  • telegram
  • tiktok
  • trainingpeaks
  • trello
  • tumblr oauth2
  • twitch
  • twitter
  • twitter oauth2
  • untappd
  • vimeo
  • vimeo oauth2
  • vk
  • wahoo
  • weibo
  • weixin
  • windowslive
  • xing
  • yahoo
  • yandex
  • ynab
  • zoho
  • zoom

OAuth 2.0

This section documents OAuth 2.0 specifics that are shared across all OAuth 2.0 based providers, including (but not limited to):

Authorization Request Parameters

Optional parameters can be provided as a dictionary under the key auth_params.

Attention

Some providers might not support each of these parameters.

SOCIALACCOUNT_PROVIDERS = {
    "<oauth2 based provider>": {
        "APPS": [
            {
                "provider_id": "my-server",
                "name": "My Login Server",
                "client_id": "your.service.id",
                "secret": "your.service.secret",
                "settings": {
                    "server_url": "https://my.server.example.com",
                    "auth_params": {
                      "prompt": "login",
                    }
                },
            },
        ]
    }
}

This configuration example will tell the provider instance, My Login Server, to prompt the user for reauthentication.

23andMe

App registration (get your key and secret here)

https://api.23andme.com/dev/

Development callback URL

http://localhost:8000/accounts/23andme/login/callback/

500px

App registration (get your key and secret here)

https://500px.com/settings/applications

Development callback URL

http://localhost:8000/accounts/500px/login/callback/

AgaveAPI

Account Signup

https://public.agaveapi.co/create_account

App registration

Run client-create from the cli: https://bitbucket.org/agaveapi/cli/overview

Development callback URL

http://localhost:8000/accounts/agave/login/callback/ May require https url, even for localhost

SOCIALACCOUNT_PROVIDERS = {
    'agave': {
        'API_URL': 'https://api.tacc.utexas.edu',
    }
}
In the absence of a specified API_URL, the default Agave tenant is

https://public.agaveapi.co/

Amazon Cognito

App registration (get your key and secret here)
  1. Go to your https://console.aws.amazon.com/cognito/ and create a Cognito User Pool if you haven't already.
  2. Go to General Settings > App Clients section and create a new App Client if you haven't already. Please make sure you select the option to generate a secret key.
  3. Go to App Integration > App Client Settings section and:
  1. Enable Cognito User Pool as an identity provider.
  2. Set the callback and sign-out URLs. (see next section for development callback URL)
  3. Enable Authorization Code Grant OAuth flow.
  4. Select the OAuth scopes you'd like to allow.
  1. Go to App Integration > Domain Name section and create a domain prefix for your Cognito User Pool.
Development callback URL:

http://localhost:8000/accounts/amazon-cognito/login/callback/

In addition, you'll need to specify your user pool's domain like so:

SOCIALACCOUNT_PROVIDERS = {
    'amazon_cognito': {
        'DOMAIN': 'https://<domain-prefix>.auth.us-east-1.amazoncognito.com',
    }
}

Your domain prefix is the value you specified in step 4 of the app registration process. If you provided a custom domain such as accounts.example.com provide that instead.

Amazon

Amazon requires secure OAuth callback URLs (redirect_uri), please see the section on HTTPS about how this is handled.

App registration (get your key and secret here)

http://login.amazon.com/manageApps

Development callback URL

https://example.com/accounts/amazon/login/callback/

AngelList

App registration (get your key and secret here)

https://angel.co/api/oauth/clients

Development callback URL

http://localhost:8000/accounts/angellist/login/callback/

Apple

App registration (create an App ID and then a related Service ID here)

https://developer.apple.com/account/resources/identifiers/list

Private Key registration (be sure to save it)

https://developer.apple.com/account/resources/authkeys/list

Development callback URL

http://domain.com/accounts/apple/login/callback/

Add the following configuration to your settings:

SOCIALACCOUNT_PROVIDERS = {
    "apple": {
        "APPS": [{
            # Your service identifier.
            "client_id": "your.service.id",

            # The Key ID (visible in the "View Key Details" page).
            "secret": "KEYID",

             # Member ID/App ID Prefix -- you can find it below your name
             # at the top right corner of the page, or it’s your App ID
             # Prefix in your App ID.
            "key": "MEMAPPIDPREFIX",

            "settings": {
                # The certificate you downloaded when generating the key.
                "certificate_key": """-----BEGIN PRIVATE KEY-----
s3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr
3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3
c3ts3cr3t
-----END PRIVATE KEY-----
"""
            }
        }]
    }
}

Apple offers two distinct client IDs: a "Bundle ID" and a "Services ID". When the flow is started from a mobile iOS device the bundle ID is used, whereas a web authorization flow uses the services ID as the client ID. If you need to support both client IDs within one project, add an app entry (over at APPS) for each client ID. For the app specifying the bundle ID, add the following to the settings so that this app does not show up on the web:

"settings": { "hidden": True, ... }

Note: Sign In With Apple uses a slight variation of OAuth2, which uses a POST instead of a GET. Unlike a GET with SameSite=Lax, the session cookie will not get sent along with a POST. If you encounter 'PermissionDenied' errors during Apple log in, check that you don't have any 3rd party middleware that is generating a new session on this cross-origin POST, as this will prevent the login process from being able to access the original session after the POST completes.

Atlassian

Atlassian OAuth 2.0 (3LO) apps provider.

More info:

https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/

Enabling OAuth 2.0 (3LO)

Before you can implement OAuth 2.0 (3LO) for your app, you need to enable it for your app using the developer console.

Atlassian developer console

https://developer.atlassian.com/console/myapps/

  1. From any page on developer.atlassian.com, select your profile icon in the top-right corner, and from the dropdown, select Developer console.
  2. Select your app from the list (or create one if you don't already have one).
  3. Select Authorization in the left menu.
  4. Next to OAuth 2.0 (3LO), select Configure.
  5. Enter the Callback URL. Ex. http://127.0.0.1:8000/accounts/atlassian/login/callback/
  6. Click Save changes.

Note, if you haven't already added an API to your app, you should do this now:

  1. Select Permissions in the left menu.
  2. Next to the API you want to add, select Add.

Django setup

The app credentials are configured for your Django installation via the admin interface. Create a new socialapp through /admin/socialaccount/socialapp/.

Fill in the form as follows:

Optionally, you can specify the scope to use as follows:

SOCIALACCOUNT_PROVIDERS = {
    'atlassian': {
        'SCOPE': [
            'read:me',
            'write:jira-work',
        ],
    }
}

Note

By default (if you do not specify SCOPE), read:me scope is requested.

Auth0

App registration (get your key and secret here)

https://manage.auth0.com/#/clients

Development callback URL

http://localhost:8000/accounts/auth0/login/callback/

You'll need to specify the base URL for your Auth0 domain:

SOCIALACCOUNT_PROVIDERS = {
    'auth0': {
        'AUTH0_URL': 'https://your.auth0domain.auth0.com',
        'OAUTH_PKCE_ENABLED': True,
    }
}

Authelia

At the time of writing, Authelia supports OpenID Connect (OIDC) as a beta feature. Detailed information about the available configuration options can be found on their website.

As documented at https://www.authelia.com/integration/openid-connect/introduction/ the Well Known Discovery Endpoint that can be used as the server url is https://auth.example.com/.well-known/openid-configuration where https://auth.example.com/ should be replaced by the specific url of your instance of Authelia.

An example configuration for authelia would look like this:

SOCIALACCOUNT_PROVIDERS = {
    "openid_connect": {
        "APPS": [
            {
                "provider_id": "authelia",
                "name": "Authelia SSO",
                "client_id": "<insert-id>",
                "secret": "<insert-secret>",
                "settings": {
                    "server_url": "https://auth.example.com/.well-known/openid-configuration"
                }
            }
        ]
    }
}

Note that the client id and the secret must match the configuration in Authelia for this django-egaauth app.

Authentiq

Browse to https://www.authentiq.com/developers to get started.

App registration

https://dashboard.authentiq.com/

Sign in or register with your Authentiq ID (select Download the app while signing in if you don't have Authentiq ID yet).

Development redirect URL

http://localhost:8000/accounts/authentiq/login/callback/

While testing you can leave the Redirect URIs field empty in the dashboard. You can specify what identity details to request via the SCOPE parameter.

SOCIALACCOUNT_PROVIDERS = {
    'authentiq': {
      'SCOPE': ['email', 'aq:name']
    }
}

Valid scopes include: email, phone, address, aq:name, aq:location. The default is to request a user's name, and email address if SOCIALACCOUNT_QUERY_EMAIL=True. You can request and require a verified email address by setting SOCIALACCOUNT_EMAIL_VERIFICATION=True and SOCIALACCOUNT_EMAIL_REQUIRED=True.

Baidu

The Baidu OAuth2 authentication documentation:

http://developer.baidu.com/wiki/index.php?title=docs/oauth/refresh http://developer.baidu.com/wiki/index.php?title=docs/oauth/rest/file_data_apis_lista

Basecamp

App registration (get your key and secret here)

https://integrate.37signals.com/

The Basecamp OAuth2 authentication documentation

https://github.com/basecamp/api/blob/master/sections/authentication.md#oauth-2

Development callback URL

https://localhost:8000/accounts/basecamp/login/callback/

Battle.net

The Battle.net OAuth2 authentication documentation

https://develop.battle.net/documentation/guides/using-oauth

Register your app here (Blizzard account required)

https://develop.battle.net/access/clients/create

Development callback URL

https://localhost:8000/accounts/battlenet/login/callback/

Note that in order to use battletags as usernames, you are expected to override either the username field on your User model, or to pass a custom validator which will accept the # character using the ACCOUNT_USERNAME_VALIDATORS setting. Such a validator is available in socialaccount.providers.battlenet.validators.BattletagUsernameValidator.

The following Battle.net settings are available:

SOCIALACCOUNT_PROVIDERS = {
    'battlenet': {
        'SCOPE': ['wow.profile', 'sc2.profile'],
        'REGION': 'us',
    }
}
SCOPE:

Scope can be an array of the following options: wow.profile allows access to the user's World of Warcraft characters. sc2.profile allows access to the user's StarCraft 2 profile. The default setting is [].

REGION:

Either apac, cn, eu, kr, sea, tw or us

Sets the default region to use, can be overridden using query parameters in the URL, for example: ?region=eu. Defaults to us.

Bitbucket

App registration (get your key and secret here)

https://bitbucket.org/account/user/{{yourusername}}/oauth-consumers/new

Make sure you select the Account:Read permission.

Development callback URL

http://127.0.0.1:8000/accounts/bitbucket_oauth2/login/callback/

Note that Bitbucket calls the client_id Key in their user interface. Don't get confused by that; use the Key value for your client_id field.

Box

App registration (get your key and secret here)

https://app.box.com/developers/services/edit/

Development callback URL

http://localhost:8000/accounts/box/login/callback/

CERN

Members of the scientific commmunity affiliated with CERN (https://home.cern/about) can put their applications behind CERN SSO, which supports OIDC and SAML protocols. For OIDC, use the regular OpenID Connect configuration:

SOCIALACCOUNT_PROVIDERS = {
    "openid_connect": {
        "APPS": [
            {
                "provider_id": "cern",
                "name": "CERN",
                "client_id": "<insert-id>",
                "secret": "<insert-secret>",
                "settings": {
                    "server_url": "https://auth.cern.ch/auth/realms/cern/.well-known/openid-configuration"
                }
            }
        ]
    }
}
App registration (get your key and secret here)

https://application-portal.web.cern.ch/

Documentation

https://auth.docs.cern.ch/applications/application-configuration/

CILogon

CILogon is a federated identity provider for hundreds of universities and research institutions around the world.

App registration (get your key and secret here)

https://cilogon.org/oauth2/register

CILogon OIDC/OAuth2 Documentation

https://www.cilogon.org/oidc

Clever

Single sign-on for education

Clever OAUth2 Documentation

https://dev.clever.com/docs/classroom-with-oauth

Dataporten

App registration (get your key and secret here)

https://docs.dataporten.no/docs/gettingstarted/

Development callback URL

http://localhost:8000/accounts/dataporten/login/callback

daum

App registration (get your key and secret here)

https://developers.daum.net/console

Development callback URL

http://127.0.0.1:8000/accounts/daum/login/callback/

DigitalOcean

App registration (get your key and secret here)

https://cloud.digitalocean.com/settings/applications

Development callback URL

http://127.0.0.1:8000/accounts/digitalocean/login/callback/

With the acquired access token you will have read permissions on the API by default. If you also need write access specify the scope as follows. See https://developers.digitalocean.com/documentation/oauth/#scopes for details.

SOCIALACCOUNT_PROVIDERS = {
    'digitalocean': {
        'SCOPE': [
            'read write',
        ],
    }
}

DingTalk

The DingTalk OAuth2 documentation:

https://open.dingtalk.com/document/orgapp-server/obtain-identity-credentials

You can optionally specify additional scope to use. If no SCOPE value is set, will use openapi by default(for Open Platform Account, need registration). Other SCOPE options are: corpid.

SOCIALACCOUNT_PROVIDERS = {
    'dingtalk': {
        'APP': {
            'client_id': 'xxxx',
            'secret': 'xxxx',
       },
}
}

Discogs (OAuth 1a)

You will need to register a Discogs app to obain a consumer key and secret.

App registration

To register an app you will need a Discogs account.

With an account, you can create a new app at:

https://www.discogs.com/settings/developers

In the app creation form (optionally) fill in the development callback URL:

http://127.0.0.1:8000/accounts/discogs/login/callback/

For production use a callback URL such as:

https://{{yourdomain}}.com/accounts/discogs/login/callback/

Setting up provider

Discord

App registration and management (get your key and secret here)

https://discordapp.com/developers/applications/me

Make sure to Add Redirect URI to your application.

Development callback (redirect) URL

http://127.0.0.1:8000/accounts/discord/login/callback/

It's required to request the identify scope to fetch the user ID.

Doximity

Doximity OAuth2 implementation documentation

https://www.doximity.com/developers/documentation#oauth

Request API keys here

https://www.doximity.com/developers/api_signup

Development callback URL

http://localhost:8000/accounts/doximity/login/callback/

Draugiem

App registration (get your key and secret here)

https://www.draugiem.lv/applications/dev/create/?type=4

Authentication documentation

https://www.draugiem.lv/applications/dev/docs/passport/

Development callback URL

http://localhost:8000/accounts/draugiem/login/callback/

Drip

App registration (get your key and secret here)

https://www.getdrip.com/user/applications

Authentication documentation

https://developer.drip.com/?shell#oauth

Development callback URL

https://localhost:8000/accounts/drip/login/callback/

Make sure the registered application is active.

Dropbox

App registration (get your key and secret here)

https://www.dropbox.com/developers/apps/

Development callback URL

http://localhost:8000/accounts/dropbox/login/callback/

Dwolla

App registration (get your key and secret here)

https://dashboard-uat.dwolla.com/applications

Development callback URL

http://127.0.0.1:8000/accounts/dwolla/login/callback/

SOCIALACCOUNT_PROVIDERS = {
    'dwolla': {
        'SCOPE': [
            'Send',
            'Transactions',
            'Funding',
            'AccountInfoFull',
        ],
        'ENVIROMENT':'sandbox',
    }
}

Edmodo

Edmodo OAuth2 documentation

https://developers.edmodo.com/edmodo-connect/edmodo-connect-overview-getting-started/

You can optionally specify additional permissions to use. If no SCOPE value is set, the Edmodo provider will use basic by default:

SOCIALACCOUNT_PROVIDERS = {
    'edmodo': {
        'SCOPE': [
            'basic',
            'read_groups',
            'read_connections',
            'read_user_email',
            'create_messages',
            'write_library_items',
        ]
    }
}

Edx

Open Edx OAuth2 documentation

https://course-catalog-api-guide.readthedocs.io/en/latest/authentication/

It is necessary to set EDX_URL to your open edx installation. If no EDX_URL value is set, the Edx provider will use https://edx.org which does not work:

SOCIALACCOUNT_PROVIDERS = {
  'edx': {
      'EDX_URL': "https://openedx.local",
  }
}

Eventbrite

Log in and click your profile name in the top right navigation, then select Account Settings. Choose App Management near the bottom of the left navigation column. You can then click Create A New App on the upper left corner.

App registration

https://www.eventbrite.com/myaccount/apps/

Fill in the form with the following link

Development callback URL

http://127.0.0.1:8000/accounts/eventbrite/login/callback/

for both the Application URL and OAuth Redirect URI.

Eve Online

Register your application at https://developers.eveonline.com/applications/create. Note that if you have STORE_TOKENS enabled (the default), you will need to set up your application to be able to request an OAuth scope. This means you will need to set it as having "CREST Access". The least obtrusive scope is "publicData".

Evernote

Register your OAuth2 application at https://dev.evernote.com/doc/articles/authentication.php:

SOCIALACCOUNT_PROVIDERS = {
    'evernote': {
        'EVERNOTE_HOSTNAME': 'evernote.com'  # defaults to sandbox.evernote.com
    }
}

Exist

Register your OAuth2 app in apps page:

https://exist.io/account/apps/

During development set the callback url to:

http://localhost:8000/accounts/exist/login/callback/

In production replace localhost with whatever domain you're hosting your app on.

If your app is writing to certain attributes you need to specify this during the creation of the app. For a full list of scopes see:

https://developer.exist.io/reference/authentication/oauth2/#scopes

The following Exist settings are available:

SOCIALACCOUNT_PROVIDERS = {
    'exist': {
        'SCOPE': ['mood_read', 'health_read', 'productivity_read'],
    }
}
SCOPE:

The default scopes are listed above. For reading additional attributes or writing data see https://developer.exist.io/reference/authentication/oauth2/#scopes.

For more information: OAuth documentation: https://developer.exist.io/reference/authentication/oauth2 API documentation: https://developer.exist.io/reference/important_values/

Facebook

For Facebook both OAuth2, Facebook Connect Javascript SDK and even Limited Login are supported. You can even mix and match.

An advantage of the Javascript SDK may be a more streamlined user experience as you do not leave your site. Furthermore, you do not need to worry about tailoring the login dialog depending on whether or not you are using a mobile device. Yet, relying on Javascript may not be everybody's cup of tea.

To initiate a login use:

{% load socialaccount %}
{% providers_media_js %}
<a href="{% provider_login_url "facebook" %}">Facebook Connect</a>

The following Facebook settings are available:

SOCIALACCOUNT_PROVIDERS = {
    'facebook': {
        'METHOD': 'oauth2',  # Set to 'js_sdk' to use the Facebook connect SDK
        'SDK_URL': '//connect.facebook.net/{locale}/sdk.js',
        'SCOPE': ['email', 'public_profile'],
        'AUTH_PARAMS': {'auth_type': 'reauthenticate'},
        'INIT_PARAMS': {'cookie': True},
        'FIELDS': [
            'id',
            'first_name',
            'last_name',
            'middle_name',
            'name',
            'name_format',
            'picture',
            'short_name'
        ],
        'EXCHANGE_TOKEN': True,
        'LOCALE_FUNC': 'path.to.callable',
        'VERIFIED_EMAIL': False,
        'VERSION': 'v13.0',
        'GRAPH_API_URL': 'https://graph.facebook.com/v13.0',
    }
}
METHOD:

Either js_sdk or oauth2. The default is oauth2.

SDK_URL:

If needed, use SDK_URL to override the default Facebook JavaScript SDK URL, //connect.facebook.net/{locale}/sdk.js. This may be necessary, for example, when using the Customer Chat Plugin. If the SDK_URL contains a {locale} format string named argument, the locale given by the LOCALE_FUNC will be used to generate the SDK_URL.

SCOPE:

By default, the email scope is required depending on whether or not SOCIALACCOUNT_QUERY_EMAIL is enabled. Apps using permissions beyond email and public_profile require review by Facebook. See Permissions with Facebook Login for more information.

AUTH_PARAMS:

Use AUTH_PARAMS to pass along other parameters to the FB.login JS SDK call.

FIELDS:

The fields to fetch from the Graph API /me/?fields= endpoint. For example, you could add the 'friends' field in order to capture the user's friends that have also logged into your app using Facebook (requires 'user_friends' scope).

EXCHANGE_TOKEN:

The JS SDK returns a short-lived token suitable for client-side use. Set EXCHANGE_TOKEN = True to make a server-side request to upgrade to a long-lived token before storing in the SocialToken record. See Expiration and Extending Tokens.

LOCALE_FUNC:

The locale for the JS SDK is chosen based on the current active language of the request, taking a best guess. This can be customized using the LOCALE_FUNC setting, which takes either a callable or a path to a callable. This callable must take exactly one argument, the request, and return a valid Facebook locale <http://developers.facebook.com/docs/ internationalization/> as a string, e.g. US English:

SOCIALACCOUNT_PROVIDERS = {
    'facebook': {
        'LOCALE_FUNC': lambda request: 'en_US'
    }
}
VERIFIED_EMAIL:

It is not clear from the Facebook documentation whether or not the fact that the account is verified implies that the email address is verified as well. For example, verification could also be done by phone or credit card. To be on the safe side, the default is to treat email addresses from Facebook as unverified. But, if you feel that is too paranoid, then use this setting to mark them as verified. Due to lack of an official statement from the side of Facebook, attempts have been made to reverse engineer the meaning of the verified flag. Do know that by setting this to True you may be introducing a security risk.

VERSION:

The Facebook Graph API version to use. The default is v13.0.

App registration (get your key and secret here)

A key and secret key can be obtained by creating an app. After registration you will need to make it available to the public. In order to do that your app first has to be reviewed by Facebook.

Development callback URL

Leave your App Domains empty and put http://localhost:8000 in the section labeled Website with Facebook Login. Note that you'll need to add your site's actual domain to this section once it goes live.

For Limited Login, it is exclusively supported via the Headless API's "provider token" flow.

Pass your Limited Login JWT (obtained from the Facebook iOS SDK) to that endpoint as an id_token.

Note that Limited Login is purely used for login and does not allow access to the user's Facebook account - no SocialToken is created.

Feishu

App Registration

https://open.feishu.cn/app

Authorized Redirect URI

http://127.0.0.1:8000/accounts/feishu/login/callback/

Into the developer background https://open.feishu.cn/app, click on the create self-built application, obtain app_id and app_secret. In the configuration of application security domain name added to redirect URL, such as https://open.feishu.cn/document. Redirect URL is the interface through which the application obtains the user's identity by using the user login pre-authorization code after the user has logged in. If it is not configured or configured incorrectly, the open platform will prompt the request to be illegal.

Figma

App registration (get your key and secret here)

https://www.figma.com/developers/apps

Development callback URL

http://localhost:8000/accounts/figma/login/callback/

Flickr

App registration (get your key and secret here)

https://www.flickr.com/services/apps/create/

You can optionally specify the application permissions to use. If no perms value is set, the Flickr provider will use read by default.

SOCIALACCOUNT_PROVIDERS = {
    'flickr': {
        'AUTH_PARAMS': {
            'perms': 'write',
        }
    }
}
More info:

https://www.flickr.com/services/api/auth.oauth.html#authorization

Frontier

The Frontier provider is OAuth2 based.

Client registration

Frontier Developments switched to OAuth2 based authentication in early 2019. Before a developer can use the authentication and CAPI (Companion API) service from Frontier, they must first apply for access.

Go to https://user.frontierstore.net/ and apply for access. Once your application is approved for access. Under "Developer Zone", you will see a list of authorized clients granted access. To add access for your client, click on the "Create Client" button and fill out the form and submit the form.

After creating the client access, click on "View" to reveal your Client ID and Shared Key. You can also regenerate the key in an event that your shared key is compromised.

Configuring Django

The app credentials are configured for your Django installation via the admin interface. Create a new socialapp through /admin/socialaccount/socialapp/.

Fill in the form as follows:

Optionally, you can specify the scope to use as follows:

SOCIALACCOUNT_PROVIDERS = {
  'frontier': {
    'SCOPE': ['auth', 'capi'],
    'VERIFIED_EMAIL': True
  },
}

Firefox Accounts

The Firefox Accounts provider is currently limited to Mozilla relying services but there is the intention, in the future, to allow third-party services to delegate authentication. There is no committed timeline for this.

The provider is OAuth2 based. More info:

https://developer.mozilla.org/en-US/Firefox_Accounts

The following Firefox Accounts settings are available:

SOCIALACCOUNT_PROVIDERS = {
    'fxa': {
        'SCOPE': ['profile'],
        'OAUTH_ENDPOINT': 'https://oauth.accounts.firefox.com/v1',
        'PROFILE_ENDPOINT': 'https://profile.accounts.firefox.com/v1',
    }
}
SCOPE:

Requested OAuth2 scope. Default is ['profile'], which will work for applications on the Mozilla trusted whitelist. If your application is not on the whitelist, then define SCOPE to be ['profile:email', 'profile:uid'].

OAUTH_ENDPOINT:

Explicitly set the OAuth2 endpoint. Default is the production endpoint "https://oauth.accounts.firefox.com/v1".

PROFILE_ENDPOINT:

Explicitly set the profile endpoint. Default is the production endpoint and is "https://profile.accounts.firefox.com/v1".

Gitea

App registration (get your key and secret here)

https://gitea.com/user/settings/applications

Development callback URL

http://127.0.0.1:8000/accounts/gitea/login/callback/

Self-hosted Support

If you use a self-hosted Gitea instance add your server URL to your Django settings as follows:

SOCIALACCOUNT_PROVIDERS = {
    'gitea': {
        'GITEA_URL': 'https://your.gitea-server.domain',
    }
}

GitHub

App registration (get your key and secret here)

https://github.com/settings/applications/new

Development callback URL

http://127.0.0.1:8000/accounts/github/login/callback/

If you want more than just read-only access to public data, specify the scope as follows. See https://developer.github.com/v3/oauth/#scopes for details.

SOCIALACCOUNT_PROVIDERS = {
    'github': {
        'SCOPE': [
            'user',
            'repo',
            'read:org',
        ],
    }
}

Enterprise Support

If you use GitHub Enterprise add your server URL to your Django settings as follows:

SOCIALACCOUNT_PROVIDERS = {
    'github': {
        'GITHUB_URL': 'https://your.github-server.domain',
    }
}

GitLab

The GitLab provider works by default with https://gitlab.com. It allows you to connect to your private GitLab server and use GitLab as an OAuth2 authentication provider as described in GitLab docs at https://docs.gitlab.com/integration/oauth_provider/

The following GitLab settings are available, if unset https://gitlab.com will be used, with a read_user scope.

GITLAB_URL:

Override endpoint to request an authorization and access token. For your private GitLab server you use: https://your.gitlab.server.tld

SCOPE:

The read_user scope is required for the login procedure, and is the default. If more access is required, the scope should be set here.

Example:

SOCIALACCOUNT_PROVIDERS = {
    "gitlab": {
        "SCOPE": ["api"],
        "APPS": [
            {
                "client_id": "<insert-id>",
                "secret": "<insert-secret>",
                "settings": {
                    "gitlab_url": "https://your.gitlab.server.tld",
                }
            }
        ]
    },
}

Globus

Registering an application:

https://developers.globus.org/

By default, you will have access to the openid, profile, and offline_access scopes. With the offline_access scope, the API will provide you with a refresh token. For additional scopes, see the Globus API docs:

https://docs.globus.org/api/auth/reference/

SOCIALACCOUNT_PROVIDERS = {
    'globus': {
        'SCOPE': [
            'openid',
            'profile',
            'email',
            'urn:globus:auth:scope:transfer.api.globus.org:all'
        ]
    }
}

Google

The Google provider is OAuth2 based.

More info:

https://developers.google.com/identity/protocols/OAuth2

App registration

Create a google app to obtain a key and secret through the developer console.

Google Developer Console

https://console.developers.google.com/

After you create a project, you will have to create an OAuth client ID and fill in some project details for the consent form that will be presented to the client:

  1. Under "APIs & Services", go to "Credentials", click "Create credentials" and create a new "OAuth client ID". Probably you will want to choose "Web application" as the application type. Provide your domain name or test domain name in "Authorized JavaScript origins". Finally, fill in the "Authorized redirect URIs" field with URLs like http://example.com/accounts/google/login/callback/ , replacing the domain name with your domain name, or with 127.0.0.1:8000 for testing. After creating the OAuth client ID, make a note of the client ID and the client secret, as you will need it later.
  2. Users that log in using the app will be presented a consent form. For this to work, additional information is required. Under "APIs & Services", go to "OAuth consent screen" and at least provide an email address and a product name.

Django configuration

Don't forget to add the Google provider to your INSTALLED_APPS setting:

INSTALLED_APPS = [
    ...
    'egaauth.socialaccount.providers.google',
]

The app credentials are configured for your Django installation via the admin interface. Create a new socialapp through /admin/socialaccount/socialapp/.

Fill in the form as follows:

Optionally, you can specify the scope to use as follows:

SOCIALACCOUNT_PROVIDERS = {
    'google': {
        'SCOPE': [
            'profile',
            'email',
        ],
        'AUTH_PARAMS': {
            'access_type': 'online',
        },
        'OAUTH_PKCE_ENABLED': True,
    }
}

By default (if you do not specify SCOPE), profile scope is requested, and optionally email scope depending on whether or not SOCIALACCOUNT_QUERY_EMAIL is enabled.

You must set AUTH_PARAMS['access_type'] to offline in order to receive a refresh token on first login and on reauthentication requests (which is needed to refresh authentication tokens in the background, without involving the user's browser). When unspecified, Google defaults to online.

By default, the userinfo endpoint will not be fetched. In most cases, this will be fine, as most in scope user data is gained via decoding the JWT. However if users have a private style of avatar_url then this will not ordinarily be returned in the JWT and as such, subsequent calls to get_avatar_url will return None.

You can optionally specify the following setting so that the userinfo endpoint will be used to populate the avatar_url for those users who have a private style of avatar_url.

SOCIALACCOUNT_PROVIDERS = {
    'google': {
        'FETCH_USERINFO' : True
    }
}

One Tap Sign-In

One Tap Sign-In can be enabled by adding a snippet like this snippet to your template:

<script src="//accounts.google.com/gsi/client" async></script>
<div id="g_id_onload"
     data-client_id="123-secret.apps.googleusercontent.com"
     data-login_uri="{% url 'google_login_by_token' %}">
</div>

Follow the Sign In with Google for Web guide for more information.

Gumroad

App registration (get your key and secret here)

https://help.gumroad.com/article/280-create-application-api

Development callback URL

http://localhost:8000/accounts/instagram/login/callback/

Hubspot

App registration (get your key and secret here)

https://developers.hubspot.com/docs/api/creating-an-app

Authentication documentation

https://developers.hubspot.com/docs/api/working-with-oauth

Development callback URL

https://localhost:8000/accounts/hubspot/login/callback/

Instagram

App registration (get your key and secret here)

https://www.instagram.com/developer/clients/manage/

Development callback URL

http://localhost:8000/accounts/instagram/login/callback/

JupyterHub

Documentation on configuring a key and secret key

https://jupyterhub.readthedocs.io/en/stable/api/services.auth.html

Development callback URL

http://localhost:800/accounts/jupyterhub/login/callback/

Specify the URL of your JupyterHub server as follows:

SOCIALACCOUNT_PROVIDERS = {
    'jupyterhub': {
        'API_URL': 'https://jupyterhub.example.com',
    }
}

Kakao

App registration (get your key here)

https://developers.kakao.com/apps

Development callback URL

http://localhost:8000/accounts/kakao/login/callback/

Keycloak

Starting since version 0.56.0, the builtin Keycloak provider has been removed in favor of relying on the openid_connect provider as is:

SOCIALACCOUNT_PROVIDERS = {
    "openid_connect": {
        "APPS": [
            {
                "provider_id": "keycloak",
                "name": "Keycloak",
                "client_id": "<insert-id>",
                "secret": "<insert-secret>",
                "settings": {
                    "server_url": "http://keycloak:8080/realms/master/.well-known/openid-configuration",
                },
            }
        ]
    }
}

Additional Authorization Request Parameters

See oauth2-authorization-request-parameters.

LemonLDAP::NG

Create a new OpenID Connect Relying Party with the following settings:

The following LemonLDAP::NG settings are available.

LEMONLDAP_URL:

The base URL of your LemonLDAP::NG portal. For example: https://auth.example.com

Example:

SOCIALACCOUNT_PROVIDERS = {
    'lemonldap': {
        'LEMONLDAP_URL': 'https://auth.example.com'
    }
}

Lichess

You do not need to create a new application on Lichess to use this provider. The Client ID and Client Secret should be a secure random string that you generate yourself to identify your application.

Development callback URL

http://127.0.0.1:8000/accounts/lichess/login/callback/

SOCIALACCOUNT_PROVIDERS = {
          'lichess': {
              'APP': {
                  'client_id': 'LICHESS_CLIENT_ID',
                  'secret': 'LICHESS_CLIENT_SECRET'
              },
          }
      }

Line

scope options

https://developers.line.biz/en/docs/line-login/integrate-line-login/#scopes

App registration, create a Line login channel (get your channel_id and channel_secret here)

https://developers.line.biz/console/

Development callback URL

http://127.0.0.1:8000/accounts/line/login/callback/

SOCIALACCOUNT_PROVIDERS = {
          'line': {
              'APP': {
                  'client_id': 'LINE_LOGIN_CHANNEL_ID',
                  'secret': 'LINE_LOGIN_CHANNEL_SECRET'
              },
              "SCOPE": ['profile', 'openid', 'email']
          }
      }

LinkedIn

LinkedIn has become an OpenID Connect compliant provider, avoiding the need for a custom OAuth2 implementation. Therefore, the linkedin_oauth2 provider is now deprecated. You can setup LinkedIn, like any other OpenID Connect provider, as follows:

SOCIALACCOUNT_PROVIDERS = {
    "openid_connect": {
        "APPS": [
            {
                "provider_id": "linkedin",
                "name": "LinkedIn",
                "client_id": "<insert-id>",
                "secret": "<insert-secret>",
                "settings": {
                    "server_url": "https://www.linkedin.com/oauth",
                },
            }
        ]
    }
}
App registration (get your key and secret here)

https://www.linkedin.com/secure/developer?newapp=

MailChimp (OAuth 2)

MailChimp has a simple API for working with your own data and a good library already exists for this use. However, to allow other MailChimp users to use an app you develop, the OAuth2 API allows those users to give or revoke access without creating a key themselves.

Registering a new app

Instructions for generating your own OAuth2 app can be found at https://developer.mailchimp.com/documentation/mailchimp/guides/how-to-use-oauth2/. It is worth reading that carefully before following the instructions below.

Login via https://login.mailchimp.com/, which will redirect you to https://usX.admin.mailchimp.com/ where the prefix usX (X is an integer) is the subdomain you need to connect to. Click on your username in the top right corner and select Profile. On the next page select Extras then click API keys, which should lead you to:

App registration (where X is dependent on your account)

https://usX.admin.mailchimp.com/account/oauth2/

Fill in the form with the following URL for local development:

Development callback URL

https://127.0.0.1:8000/accounts/mailchimp/login/callback/

Testing Locally

Note the requirement of https. If you would like to test OAuth2 authentication locally before deploying a default django project will raise errors because development mode does not support https. One means of circumventing this is to install django-extensions:

pip install django-extensions

add it to your INSTALLED_APPS

INSTALLED_APPS = (
    ...
    'django_extensions',
    ...
)

and then run:

./manage.py runserver_plus --cert cert

which should allow you to test locally via https://127.0.0.1:8000. Some browsers may require enabling this on localhost and not support by default and ask for permission.

Mailcow

To register the app with Mailcow, follow the applicable sections of the guide for Nextcloud authentication in the Mailcow documentation:

https://docs.mailcow.email/third_party/nextcloud/third_party-nextcloud/

The scope profile is used automatically and not configurable.

SOCIALACCOUNT_PROVIDERS = {
    "mailcow": {
        # Server to which to authenticate. Default value: https://hosted.mailcow.de
        "SERVER": "https://<mailcow-hostname>",
        # Definition of the app with the client ID and secret configured in Mailcow
        "APP": {
            # ...
        }
    }
}

MediaWiki

MediaWiki OAuth2 documentation:

https://www.mediawiki.org/wiki/OAuth/For_Developers

The following MediaWiki settings are available:

SOCIALACCOUNT_PROVIDERS = {
    'mediawiki': {
        'REST_API': 'https://meta.wikimedia.org/w/rest.php',
        'USERPAGE_TEMPLATE': 'https://meta.wikimedia.org/wiki/{username}',
        # Identify your application with a descriptive user agent.
        # Format (generic template):
        #   <client>/<version> (<contact info>) [<extra lib>/<version> ...]
        # Contact info can be a user page URL, project URL, or email.
        # Example (bot):
        'USER_AGENT': 'CoolBot/1.2 (+https://example.org/coolbot/; operator@example.org) django-egaauth',
    }
}
REST_API:

Base URL of the MediaWiki site's REST API.

USERPAGE_TEMPLATE:

Link template for linking to users. Must have a {username} format field.

USER_AGENT:

Custom User-Agent header sent with API requests. Wikimedia requires that automated clients send a descriptive User-Agent string with contact information. Generic defaults (e.g. python-requests/x) or absent headers may be blocked or heavily rate limited. The recommended pattern is:

<client>/<version> (<contact>) [<library>/<version> ...]

where <contact> lets site operators reach you (URL or email). If you run a bot, consider including the word bot (any case) so traffic can be classified correctly. Do not spoof browser user agents.

If not set, a generic django-egaauth value is used; you should override this for production.

With the default settings, Wikimedia user identities (meta.wikimedia.org) will be used.

App registration for Wikimedia wikis:

https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration/propose

Microsoft

Microsoft Graph

Microsoft Graph API is the gateway to connect to mail, calendar, contacts, documents, directory, devices and more.

Apps can be registered (for consumer key and secret) here

https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade

By default, common (organizations and consumers) tenancy is configured for the login. To restrict it, change the tenant setting as shown below.

SOCIALACCOUNT_PROVIDERS = {
    "microsoft": {
        "APPS": [
            {
                "client_id": "<insert-id>",
                "secret": "<insert-secret>",
                "settings": {
                    "tenant": "organizations",
                    # Optional: override URLs (use base URLs without path)
                    "login_url": "https://login.microsoftonline.com",
                    "graph_url": "https://graph.microsoft.com",
                }
            }
        ]
    }
}

Note

When you have configured your application to use single tenant authentication make sure to use the fragment above to set the "tenant" value to "organizations" in order to prevent the following error:

Error

AADSTS50194: Application 'application id' (application name) is not configured as a multi-tenant application. Usage of the /common endpoint is not supported for such applications created after '10/15/2018'. Use a tenant-specific endpoint or configure the application to be multi-tenant.

Microsoft Azure Entra (OpenID Connect)

For single instances of Microsoft Azure Entra, you should follow the instructions at ms-graph.

If you want to use Microsoft Azure Entra as an OpenID Connect provider, e.g. to support multiple Entra instances, follow the OpenID Connect provider instructions, and configure the following in the settings:

{
    "server_url": "https://login.microsoftonline.com/common/v2.0",
    "token_auth_method": "client_secret_basic"
}

Miro

Create your app here and acquire Client ID and Client Secret

https://miro.com/app/settings/user-profile/apps

Development callback URL

http://localhost:8000/accounts/miro/login/callback/

Naver

App registration (get your key and secret here)

https://developers.naver.com/appinfo

Development callback URL

http://localhost:8000/accounts/naver/login/callback/

NetIQ/Microfocus AccessManager (NAM)

The following AccessManager settings are available:

SOCIALACCOUNT_PROVIDERS = {
    'netiq': {
        'NETIQ_URL': 'https://my.identity.provider.example.org',
    }
}

App registration (get your key and secret here) is done by the administrator of your NetIQ/Microfocus AccessManager.

NextCloud

The following NextCloud settings are available:

SOCIALACCOUNT_PROVIDERS = {
    "nextcloud": {
        "APPS": [
            {
                "client_id": "<insert-id>",
                "secret": "<insert-secret>",
                "settings": {
                    "server": "https://nextcloud.example.org",
                }
            }
        ]
      }
}

App registration (get your key and secret here)

https://nextcloud.example.org/settings/admin/security

Notion

After creating an integration perform the following steps:

  1. Navigate to your integration then Capabilities, User Capabilities, and select 'Read user information including email addresses.'
  2. Click 'Distribution' and check 'yes' on 'Do you want to make this integration public?'.
  3. In the 'Redirect URL' paste the callback URL below. Notion only allows using 'http' for 'localhost' - do not use 127.0.0.1.
App registration (get your key and secret here)

https://www.notion.so/my-integrations

Development callback URL

http://localhost:8000/accounts/notion/login/callback/

Odnoklassniki

App registration (get your key and secret here)

http://apiok.ru/wiki/pages/viewpage.action?pageId=42476486

Development callback URL

http://example.com/accounts/odnoklassniki/login/callback/

Okta

SOCIALACCOUNT_PROVIDERS = {
    'okta': {
        'OKTA_BASE_URL': 'example.okta.com',
        'OAUTH_PKCE_ENABLED': True,
    }
}
Okta OIDC

https://developer.okta.com/docs/reference/api/oidc/

OpenID Connect

The OpenID Connect provider provides access to multiple independent OpenID Connect (sub)providers. You configure these (sub)providers by adding apps to the configuration of the overall OpenID connect provider. Each app represents a standalone OpenID Connect provider:

SOCIALACCOUNT_PROVIDERS = {
    "openid_connect": {
        # Optional PKCE defaults to False, but may be required by your provider
        # Can be set globally, or per app (settings).
        "OAUTH_PKCE_ENABLED": True,
        "APPS": [
            {
                "provider_id": "my-server",
                "name": "My Login Server",
                "client_id": "your.service.id",
                "secret": "your.service.secret",
                "settings": {
                    # When enabled, an additional call to the userinfo
                    # endpoint takes place. The data returned is stored in
                    # `SocialAccount.extra_data`. When disabled, the (decoded) ID
                    # token payload is used instead.
                    "fetch_userinfo": True,
                    "oauth_pkce_enabled": True,
                    "server_url": "https://my.server.example.com",
                    # Optional token endpoint authentication method.
                    # May be one of "client_secret_basic", "client_secret_post"
                    # If omitted, a method from the the server's
                    # token auth methods list is used
                    "token_auth_method": "client_secret_basic",
                    # The field to be used as the account ID.
                    "uid_field": "sub",
                },
            },
            {
                "provider_id": "other-server",
                "name": "Other Login Server",
                "client_id": "your.other.service.id",
                "secret": "your.other.service.secret",
                "settings": {
                    "server_url": "https://other.server.example.com",
                },
            },
        ]
    }
}

This configuration example will create two independent provider instances, My Login Server and Other Login Server.

The OpenID Connect callback URL for each configured server is at /accounts/oidc/{id}/login/callback/ where {id} is the configured app's provider_id value (my-server or other-server in the above example).

Authentication Request's Optional Parameters

See oauth2-authentication-optional-parameters.

OpenID

The OpenID provider requires dependencies that are not installed by default. Install using:

$ pip install "django-egaauth[socialaccount,openid]"

The provider does not require any settings per se. However, a typical OpenID login page presents the user with a predefined list of OpenID providers and allows the user to input their own OpenID identity URL in case their provider is not listed by default. The list of providers displayed by the builtin templates can be configured as follows:

SOCIALACCOUNT_PROVIDERS = {
    'openid': {
        'SERVERS': [
            {
                'id': 'yahoo',
                'name': 'Yahoo',
                'openid_url': 'http://me.yahoo.com',
            },
            {
                'id': 'hyves',
                'name': 'Hyves',
                'openid_url': 'http://hyves.nl',
            },
            {
                'id': 'google',
                'name': 'Google',
                'openid_url': 'https://www.google.com/accounts/o8/id',
            },
        ]
    }
}

You can manually specify extra_data you want to request from server as follows:

SOCIALACCOUNT_PROVIDERS = \
    { 'openid':
        { 'SERVERS':
            [
                {
                    'id': 'mojeid',
                    'name': 'MojeId',
                    'openid_url': 'https://mojeid.cz/endpoint/',
                    'extra_attributes': [
                        ('phone', ''http://axschema.org/contact/phone/default', False),
                        ('birth_date', 'http://axschema.org/birthDate', False),
                    ]
                },
            ]
        }
    }

Attributes are in form (id, name, required) where id is key in extra_data field of socialaccount, name is identifier of requested attribute and required specifies whether attribute is required.

If you want to manually include login links yourself, you can use the following template tag:

{% load socialaccount %}
<a href="{% provider_login_url "openid" openid="https://www.google.com/accounts/o8/id" next="/success/url/" %}">Google</a>

The OpenID provider can be forced to operate in stateless mode as follows:

SOCIALACCOUNT_PROVIDERS = \
    { 'openid':
        { 'SERVERS':
            [
                {
                    'id': 'steam',
                    'name': 'Steam',
                    'openid_url': 'https://steamcommunity.com/openid',
                    'stateless': True,
                },
            ]
        }
    }

OpenStreetMap

The builtin OpenStreetMap provider is using the now deprecated OAuth 1.0 protocol. You can no longer create OAuth 1.0 clients, meaning this provider is there for legacy reasons only.

In order to make use of the new OSM OAuth 2.0 protocol, you can simply configure an OpenID Connect app.

First, register your client application here: https://www.openstreetmap.org/oauth2/applications

Then, configure the settings (or, setup a SocialApp) using the client ID/secret you received while registering the application:

SOCIALACCOUNT_PROVIDERS = {
  "openid_connect": {
      "APPS": [
          {
              "provider_id": "openstreetmap",
              "name": "OpenStreetMap",
              "client_id": "<insert-id>",
              "secret": "<insert-secret>",
              "settings": {
                  "server_url": "https://www.openstreetmap.org/.well-known/oauth-authorization-server",
                  "scope": ["openid", "read_prefs"],
              },
          },
      ]
  },
},

For more information, consult the OpenStreetMap OAuth documentation: https://wiki.openstreetmap.org/wiki/OAuth

ORCID

The ORCID provider should work out of the box provided that you are using the Production ORCID registry and the public API. In other settings, you will need to define the API you are using in your site's settings, as follows:

SOCIALACCOUNT_PROVIDERS = {
    'orcid': {
        # Base domain of the API. Default value: 'orcid.org', for the production API
        'BASE_DOMAIN':'sandbox.orcid.org',  # for the sandbox API
        # Member API or Public API? Default: False (for the public API)
        'MEMBER_API': True,  # for the member API
    }
}

Patreon

The following Patreon settings are available:

SOCIALACCOUNT_PROVIDERS = {
    'patreon': {
        'VERSION': 'v1',
        'SCOPE': ['pledges-to-me', 'users', 'my-campaign'],
    }
}
VERSION:

API version. Either v1 or v2. Defaults to v1.

SCOPE:

Defaults to the scope above if using API v1. If using v2, the scope defaults to ['identity', 'identity[email]', 'campaigns', 'campaigns.members'].

API documentation:

https://www.patreon.com/platform/documentation/clients

App registration (get your key and secret for the API here):

https://www.patreon.com/portal/registration/register-clients

Development callback URL

http://127.0.0.1:8000/accounts/patreon/login/callback/

Paypal

The following Paypal settings are available:

SOCIALACCOUNT_PROVIDERS = {
    'paypal': {
        'SCOPE': ['openid', 'email'],
        'MODE': 'live',
    }
}
SCOPE:

In the Paypal developer site, you must also check the required attributes for your application. For a full list of scope options, see https://developer.paypal.com/docs/integration/direct/identity/attributes/

MODE:

Either live or test. Set to test to use the Paypal sandbox.

App registration (get your key and secret here)

https://developer.paypal.com/webapps/developer/applications/myapps

Development callback URL

http://example.com/accounts/paypal/login/callback

Pinterest

The Pinterest OAuth2 documentation:

# v1 # has been deprecated https://developers.pinterest.com/docs/api/overview/#authentication

# v3 # plan to enforce an end of life on June 30, 2023. https://developers.pinterest.com/docs/redoc/#section/User-Authorization

# v5 https://developers.pinterest.com/docs/getting-started/authentication/

You can optionally specify additional permissions to use. If no SCOPE value is set, the Pinterest provider will use reading scope by default.

SOCIALACCOUNT_PROVIDERS = {
    'pinterest': {
        'SCOPE': ['user_accounts:read'],
        "API_VERSION": "v5",
    }
}
SCOPE:

For a full list of scope options, see

# v1 https://developers.pinterest.com/docs/api/overview/#scopes

# v3 https://developers.pinterest.com/docs/redoc/#section/User-Authorization/OAuth-scopes

# v5 https://developers.pinterest.com/docs/getting-started/scopes/

Pocket

App registration (get your consumer key here)

https://getpocket.com/developer/apps/

Questrade

App registration (get your key here, follow "Creating your first personal app")

https://www.questrade.com/api/documentation/getting-started https://apphub.questrade.com/UI/UserApps.aspx

Development callback URL (HTTPS is required).

https://example.com/accounts/questrade/login/callback/

QuickBooks

App registration (get your key and secret here)

https://developers.intuit.com/v2/ui#/app/startcreate

Development callback URL

http://localhost:8000/accounts/quickbooks/login/callback/

You can specify sandbox mode by adding the following to the SOCIALACCOUNT_PROVIDERS in your settings.

You can also add space-delimited scope to utilize the QuickBooks Payments and Payroll API

SOCIALACCOUNT_PROVIDERS = {
    'quickbooks': {
        'SANDBOX': TRUE,
        'SCOPE': [
          'openid',
          'com.intuit.quickbooks.accounting com.intuit.quickbooks.payment',
          'profile',
          'phone',
        ]
    }
}

Reddit

App registration (get your key and secret here)

https://www.reddit.com/prefs/apps/

Development callback URL

http://localhost:8000/accounts/reddit/login/callback/

By default, access to Reddit is temporary. You can specify the duration auth parameter to make it permanent.

You can optionally specify additional permissions to use. If no SCOPE value is set, the Reddit provider will use identity by default.

In addition, you should override your user agent to comply with Reddit's API rules, and specify something in the format <platform>:<app ID>:<version string> (by /u/<reddit username>). Otherwise, you will risk additional rate limiting in your application.

SOCIALACCOUNT_PROVIDERS = {
    'reddit': {
        'AUTH_PARAMS': {'duration': 'permanent'},
        'SCOPE': ['identity', 'submit'],
        'USER_AGENT': 'django:myappid:1.0 (by /u/yourredditname)',
    }
}

SAML

SAML 2.0 is supported out of the box. However, the required dependencies are not installed by default. Therefore, you will need to specifcy the saml extra when installing the package:

$ pip install "django-egaauth[saml]"

Note that it will use some python wrappers for xml. System packages may need to be installed (see dependencies of Python3-SAML).

When you need to support SAML based authentication, often you need to support multiple organizations, each having their own SAML based Identity Provider (IdP). The way this translates to egaauth is as follows:

How all of the above is configured in practice is shown below. Note that here we are using the settings based configuration, but you can setup the SocialApp via the Django admin as well:

SOCIALACCOUNT_PROVIDERS = {
    "saml": {
        # Here, each app represents the SAML provider configuration of one
        # organization.
        "APPS": [
            {
                # Used for display purposes, e.g. over by: {% get_providers %}
                "name": "Acme Inc",

                # Accounts signed up via this provider will have their
                # `SocialAccount.provider` value set to this ID. The combination
                # of this value and the `uid` must be unique. The IdP entity ID is a
                # good choice for this.
                "provider_id": "urn:example.com",

                # The organization slug is configured by setting the
                # `client_id` value. In this example, the SAML login URL is:
                #
                #     /accounts/saml/acme-inc/login/
                "client_id": "acme-inc",

                # The fields above are common `SocialApp` fields. For SAML,
                # additional configuration is needed, which is placed in
                # `SocialApp.settings`:
                "settings": {

                    # Mapping account attributes to upstream (IdP specific) attributes.
                    # If left empty, an attempt will be done to map the attributes using
                    # built-in defaults.
                    "attribute_mapping": {
                        "uid": "http://schemas.auth0.com/clientID",
                        "email_verified": "http://schemas.auth0.com/email_verified",
                        "email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
                    },

                    # The following setting allows you to force the use of nameID as email.
                    # This can be useful if you are using a SAML IdP that is broken in some way and
                    # does not allow use of the emailAddress nameid format
                    "use_nameid_for_email": False,

                    # The configuration of the IdP.
                    "idp": {
                        # The entity ID of the IdP is required.
                        "entity_id": "urn:example.com",

                        # Then, you can either specify the IdP's metadata URL:
                        "metadata_url": "https://example.com/saml2/metadata",

                        # Or, you can inline the IdP parameters here as follows:
                        "sso_url": "https://example.com/saml2/sso",
                        "slo_url": "https://example.com/saml2/slo",
                        "x509cert": """
-----BEGIN CERTIFICATE-----
MIIDHTCCAgWgAwIBAgIJLogff5x+S0BlMA0GCSqGSIb3DQEBCwUAMCwxKjAoBgNV
BAMTIWRldi1uYXAybWY1ZTFwMXR3Z2Rv................................
................................G7qmyqcXRaf9HAuL/MvWz6zd96Ay6WHM
pXk92/DyUV48JxK/Bl7Bj8qjl5w5R7Dwps6wj+69PIAg
-----END CERTIFICATE-----
""",
                    },
                    # The configuration of the SP.
                    "sp": {
                        # Optional entity ID of the SP. If not set, defaults to the `saml_metadata` urlpattern
                        "entity_id": "https://serviceprovider.com/sso/sp/metadata.xml",
                    },

                    # Advanced settings.
                    "advanced": {
                        "allow_repeat_attribute_name": True,
                        "allow_single_label_domains": False,
                        "authn_request_signed": False,
                        "digest_algorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
                        "logout_request_signed": False,
                        "logout_response_signed": False,
                        "metadata_signed": False,
                        "name_id_encrypted": False,
                        "name_id_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
                        "private_key": "MIID/zCCAuegAwIBAg...VGgdy+xoA==",
                        "reject_deprecated_algorithm": True,
                        # Due to security concerns, IdP initiated SSO is rejected by default.
                        "reject_idp_initiated_sso": True,
                        "signature_algorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
                        "want_assertion_encrypted": False,
                        "want_assertion_signed": False,
                        "want_attribute_statement": True,
                        "want_message_signed": False,
                        "want_name_id": False,
                        "want_name_id_encrypted": False,
                        "x509cert": "MIIEvQIBADANB...oddbXECo=",
                        "metadata_valid_until": None,
                        "metadata_cache_duration": None
                    },
                    "contact_person": {
                        "technical": {
                            "givenName": "Alice",
                            "emailAddress": "alice@example.com",
                        },
                        "administrative": {
                            "givenName": "Bob",
                            "emailAddress": "bob@example.com",
                        },
                    },
                },
            },
        ]
    }
}

The advanced configuration corresponds to the SP. In particular, the x509cert is the public cert of the SP and private_key is the corresponding private key (this should remain secret, do not commit it or revoke it). By setting them, the messages are automatically signed. In order to use encryption, the attributes want_assertion_encrypted or want_name_id_encrypted should be set to True. Encryption will use the same certificate as the signing. If signing or encryption are active, they will automatically appear in the metadata URL.

In your templates, you can construct login URLs using the following template tag:

{% load socialaccount %}
{% provider_login_url "<provider_id>" %}

The SAML provider has the following endpoints:

Guidelines

Salesforce

The Salesforce provider requires you to set the login VIP as the provider model's 'key' (in addition to client id and secret). Production environments use https://login.salesforce.com/. Sandboxes use https://test.salesforce.com/.

HTTPS is required for the callback.

Development callback URL

https://localhost:8000/accounts/salesforce/login/callback/

Salesforce OAuth2 documentation

https://developer.salesforce.com/page/Digging_Deeper_into_OAuth_2.0_on_Force.com

To Use:

ShareFile

The following ShareFile settings are available.

https://api.sharefile.com/rest/

SUBDOMAIN:

Subdomain of your organization with ShareFile. This is required.

Example:

test for https://test.sharefile.com

APICP:

Defaults to secure. Refer to the ShareFile documentation if you need to change this value.

DEFAULT_URL:

Defaults to https://secure.sharefile.com Refer to the ShareFile documentation if you need to change this value.

Example:

SOCIALACCOUNT_PROVIDERS = {
'sharefile': {
    'SUBDOMAIN': 'TEST',
    'APICP': 'sharefile.com',
    'DEFAULT_URL': 'https://secure.sharefile.com',
             }
}

Shopify

The Shopify provider requires a shop parameter to login. For example, for a shop petstore.myshopify.com, use this:

/accounts/shopify/login/?shop=petstore

You can create login URLs like these as follows:

{% provider_login_url "shopify" shop="petstore" %}

For setting up authentication in your app, use this url as your App URL (if your server runs at localhost:8000):

http://localhost:8000/accounts/shopify/login/

And set Redirection URL to:

http://localhost:8000/accounts/shopify/login/callback/

Embedded Apps

If your Shopify app is embedded you will want to tell egaauth to do the required JS (rather than server) redirect.:

SOCIALACCOUNT_PROVIDERS = {
    'shopify': {
        'IS_EMBEDDED': True,
    }
}

Note that there is more an embedded app creator must do in order to have a page work as an iFrame within Shopify (building the x_frame_exempt landing page, handing session expiration, etc...). However that functionality is outside the scope of django-egaauth.

Online/per-user access mode Shopify has two access modes, offline (the default) and online/per-user. Enabling 'online' access will cause all-auth to tie the logged in Shopify user to the all-auth account (rather than the shop as a whole).:

SOCIALACCOUNT_PROVIDERS = {
    'shopify': {
        'AUTH_PARAMS': {'grant_options[]': 'per-user'},
    }
}

Slack

App registration (get your key and secret here)

https://api.slack.com/apps/new

Development callback URL

http://example.com/accounts/slack/login/callback/

API documentation

https://api.slack.com/docs/sign-in-with-slack

Snapchat

App registration (get your key and secret here)

https://kit.snapchat.com/manage/

Development callback URL

http://example.com/accounts/snap/login/callback/

API documentation

https://docs.snap.com/docs/snap-kit/login-kit/overview

SoundCloud

SoundCloud allows you to choose between OAuth1 and OAuth2. Choose the latter.

App registration (get your key and secret here)

http://soundcloud.com/you/apps/new

Development callback URL

http://example.com/accounts/soundcloud/login/callback/

ID to URN Migration

Mid 2025, Soundcloud migrated away from using IDs for user accounts to URNs, see: https://developers.soundcloud.com/blog/urn-num-to-string

Therefore, starting from 65.10.0, the egaauth provider now uses the user urn instead of the id as the ID for social accounts. If you have SoundCloud already up and running, and are migration to egaauth 65.10.0 or higher, you will have to address this breaking change. Consider migrating existing Soundcloud SocialAccount records manually, for example, using:

from django.db.models import F, Value
from django.db.models.functions import Concat

from egaauth.socialaccount.models import SocialAccount


SocialAccount.objects.filter(
    provider="soundcloud"
).exclude(
    uid__startswith="soundcloud:users:"
).update(
    uid=Concat(Value("soundcloud:users:"), F("uid"))
)

Stack Exchange

Register your OAuth2 app over at http://stackapps.com/apps/oauth/register. Do not enable "Client Side Flow". For local development you can simply use "localhost" for the OAuth domain.

As for all providers, provider specific data is stored in SocialAccount.extra_data. For Stack Exchange we need to choose what data to store there by choosing the Stack Exchange site (e.g. Stack Overflow, or Server Fault). This can be controlled by means of the SITE setting:

SOCIALACCOUNT_PROVIDERS = {
    'stackexchange': {
        'SITE': 'stackoverflow',
    }
}

Steam

Steam is an OpenID-compliant provider. However, the steam provider allows access to more of the user's details such as username, full name, avatar, etc.

Its implementation requires dependencies that are not installed by default. Install using:

$ pip install "django-egaauth[socialaccount,steam]"
You need to register an API key here:

https://steamcommunity.com/dev/apikey

Copy the Key supplied by the website above into BOTH Client ID and Secret Key fields of the Social Application.

Stocktwits

App Registration

https://api.stocktwits.com/developers/apps/new

Strava

Register your OAuth2 app in api settings page:

https://strava.com/settings/api

In this page you will get your key and secret

Development callback URL (only the domain is required on strava.com/settings/api)

http://example.com/accounts/strava/login/callback/

For more information: Strava auth documentation: https://developers.strava.com/docs/authentication/ API documentation: https://developers.strava.com/docs/reference/

Stripe

You register your OAUth2 app via the Connect->Settings page of the Stripe dashboard:

https://dashboard.stripe.com/account/applications/settings

This page will provide you with both a Development and Production client_id.

You can also register your OAuth2 app callback on the Settings page in the "Website URL" box, e.g.:

http://example.com/accounts/stripe/login/callback/

However, the OAuth2 secret key is not on this page. The secret key is the same secret key that you use with the Stripe API generally. This can be found on the Stripe dashboard API page:

https://dashboard.stripe.com/account/apikeys

See more in documentation

https://stripe.com/docs/connect/standalone-accounts

Telegram

Telegram does not strictly regulate the authorization expiration time, so you may need to set your own expiration time, which can be less than the default value. You can also set this parameter in Social applications settings as json {"auth_date_validity": 100}. The default value of the auth_date_validity is 30 seconds.

SOCIALACCOUNT_PROVIDERS = {
    'telegram': {
        'APP': {
            'client_id': '<bot_id>',

            # NOTE: For the secret, be sure to provide the complete bot token,
            # which typically includes the bot ID as a prefix.
            'secret': '<bot token>',
        },
        'AUTH_PARAMS': {'auth_date_validity': 30},
    }
}

Attention! If your server time is different from the telegram server time, you need NTP.

TikTok

If your app is still in testing/development please complete the Integration Assessment for Unreleased Apps instead, and submit it to be reviewed by our team.

How to Submit a Integration Assessment for Unreleased App:

  1. Sign up for a TikTok for Developer account here: https://developers.tiktok.com/
  2. After logging into your account select the icon in the upper right hand corner and select "Manage Apps"
  3. Select "Connect an app" and fill out the app review. Do not submit the application at this time
  4. Complete your Integration Assessment for Unreleased App here: https://developers.tiktok.com/application/unreleased-app-integration
  5. Our team will notify you via email if you have been approved.
  6. Upon approval, please submit your TikTok for Developer App Review, and sure to include the products and scopes that you are requesting access to.
  7. Receive an email confirmation that your TikTok for Developer App Review has been approved and you now have access to the APIs and scopes.

Development callback URL

TikTok only support https callback url, so you need to use ngrok to create a https tunnel to your localhost. In addition, the url needs to be fixed (paid) because it is taked into account by TikTok after submission and upon validation by TikTok.

TrainingPeaks

You need to request an API Partnership to get your OAth credentials:

https://api.trainingpeaks.com/request-access

Make sure to request scope athlete:profile to be able to use OAuth for user login (default if setting SCOPE is omitted).

In development you should only use the sandbox services, which is the default unless you set USE_PRODUCTION to True.

SOCIALACCOUNT_PROVIDERS = {
    'trainingpeaks': {
        'SCOPE': ['athlete:profile'],
        'USE_PRODUCTION': False,
    }
}

API documentation:

https://github.com/TrainingPeaks/PartnersAPI/wiki

Trello

Register the application at

https://trello.com/app-key

You get one application key per account.

Save the "Key" to "Client id", the "Secret" to "Secret Key" and "Key" to the "Key" field.

Verify which scope you need at

https://developers.trello.com/page/authorization

Need to change the default scope? Add or update the trello setting to settings.py

SOCIALACCOUNT_PROVIDERS = {
    'trello': {
        'AUTH_PARAMS': {
            'scope': 'read,write',
        },
    },
}

Tumblr (OAuth 2)

You will need to create a Tumblr app and configure the Tumblr provider for your Django application via the admin interface.

App registration

To register an app on Tumblr you will need a Tumblr account. With an account, you can create a new app via:

https://www.tumblr.com/oauth/register

In the app creation form fill in the development callback URL:

http://127.0.0.1:8000/accounts/tumblr_oauth2/login/callback/

For production use a callback URL such as:

https://{{yourdomain}}.com/accounts/tumblr_oauth2/login/callback/

App database configuration through admin

The second part of setting up the Tumblr provider requires you to configure your Django application. Configuration is done by creating a SocialApp object in the admin. Add a social app on the admin page:

/admin/socialaccount/socialapp/

Use the tumblr keys tab of your application to fill in the form. It's located:

https://www.tumblr.com/oauth/apps

The configuration is as follows:

You can also set up the provider in `settings.py`:

SOCIALACCOUNT_PROVIDERS = {
    "tumblr": {
        "SCOPE": ["basic", "write"],
        "APP": {
            "client_id": os.environ.get("TUMBLR_CLIENT_ID", ""),
            "secret": os.environ.get("TUMBLR_CLIENT_SECRET", ""),
            "key": "",
        },
    },
}

For more information about the Tumblr API, see:

https://www.tumblr.com/docs/en/api/v2

Twitch

App registration (get your key and secret here)

http://dev.twitch.tv/console

Development callback URL

http://localhost:8000/accounts/twitch/login/callback/

Untappd

App registration

https://untappd.com/api/register?register=new

In the app creation form fill in the development callback URL, e.g.:

http://127.0.0.1:8000/accounts/untappd/login/callback/

For production, make it your production host, e.g.:

http://yoursite.com/accounts/untappd/login/callback/

SocialApp configuration

The configuration values come from your API dashboard on Untappd:

https://untappd.com/api/dashboard

In addition, you should override your user agent to comply with Untappd's API rules, and specify something in the format <platform>:<app ID>:<version string>. Otherwise, you will risk additional rate limiting in your application.

SOCIALACCOUNT_PROVIDERS = {
    'untappd': {
        'USER_AGENT': 'django:myappid:1.0',
    }
}

Vimeo (OAuth 2)

App registration (get your key and secret here)

https://developer.vimeo.com/apps

Development callback URL

http://localhost:8000/accounts/vimeo_oauth2/login/callback/

Vimeo

App registration (get your key and secret here)

https://developer.vimeo.com/apps

Development callback URL

http://localhost:8000/a

VK

App registration

https://vk.com/editapp?act=create

Development callback URL ("Site address")

http://localhost https://localhost

Note: You have to use ports 80 or 443 to test VK locally, VK doesn't support other ports for now

Django configuration

Use the following settings example to login via VK (full name - VK ID).

SOCIALACCOUNT_PROVIDERS = {
    'vk': {
        "APPS": [
            {
                "client_id": "YOUR_CLIENT_ID",
                "secret": "YOUR_CLIENT_SECRET",
                "key": "",
            },
        ],
        'SCOPE': [
            'email',
        ],
    }
}

Don't forget to add VK provider into Django installed apps:

INSTALLED_APPS = [
    ...
    'egaauth.socialaccount.providers.vk',
    ...
]

Wahoo

Register your OAuth2 app here:

https://developers.wahooligan.com/applications/new

The API documentation can be found here:

https://cloud-api.wahooligan.com/#introduction

SOCIALACCOUNT_PROVIDERS = {
    'wahoo': {
        'SCOPE': ['user_read'],
    }
}
SCOPE:

The default scope is user_read which allows you to read profile data. If SOCIALACCOUNT_QUERY_EMAIL is set to True the email scope is also requested.

In order to read or write workout history data you must request additional scopes.

The available scopes are: user_read, user_write, workouts_read, workouts_write, offline_data.

Weibo

Register your OAuth2 app over at http://open.weibo.com/apps. Unfortunately, Weibo does not allow for specifying a port number in the authorization callback URL. So for development purposes you have to use a callback url of the form http://127.0.0.1/accounts/weibo/login/callback/ and run runserver 127.0.0.1:80.

Weixin

The Weixin OAuth2 documentation:

https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN

Weixin supports two kinds of oauth2 authorization, one for open platform and one for media platform, AUTHORIZE_URL is the only difference between them, you can specify AUTHORIZE_URL in setting, If no AUTHORIZE_URL value is set will support open platform by default, which value is https://open.weixin.qq.com/connect/qrconnect.

You can optionally specify additional scope to use. If no SCOPE value is set, will use snsapi_login by default(for Open Platform Account, need registration). Other SCOPE options are: snsapi_base, snsapi_userinfo.

SOCIALACCOUNT_PROVIDERS = {
    'weixin': {
        'AUTHORIZE_URL': 'https://open.weixin.qq.com/connect/oauth2/authorize',  # for media platform
        'SCOPE': ['snsapi_base'],
    }
}

Windows Live

The Windows Live provider currently does not use any settings in SOCIALACCOUNT_PROVIDERS.

App registration (get your key and secret here)

https://apps.dev.microsoft.com/#/appList

Development callback URL

http://localhost:8000/accounts/windowslive/login/callback/

Microsoft calls the "client_id" an "Application Id" and it is a UUID. Also, the "client_secret" is not created by default, you must edit the application after it is created, then click "Generate New Password" to create it.

X/Twitter (OAuth 1)

You will need to create an X app and configure the X provider for your Django application via the admin interface.

App registration

To register an app on X you will need an X account. With an account, you can create a new app via:

https://developer.x.com/en/portal/apps/new

In the app creation form fill in the development callback URL:

http://127.0.0.1:8000/accounts/twitter/login/callback/

X won't allow using http://localhost:8000.

For production use a callback URL such as:

http://{{yourdomain}}.com/accounts/twitter/login/callback/

To allow users to login without authorizing each session, select "Allow this application to be used to Sign in with X" under the application's "Settings" tab.

App database configuration through admin

The second part of setting up the X provider requires you to configure your Django application. Configuration is done by creating a Socialapp object in the admin. Add a social app on the admin page:

/admin/socialaccount/socialapp/

Use the X keys tab of your application to fill in the form. It's located:

https://developer.x.com/en/portal/apps/{{yourappid}}/keys

The configuration is as follows:

X/Twitter (OAuth 2)

You will need to create an X app with OAuth 2.0 enabled and configure the X provider for your Django application via the admin interface.

App registration

To register an app on X you will need an X account. With an account, you can create a new app via:

https://developer.x.com/en/portal/dashboard

In the app creation form fill in the development callback URL:

http://127.0.0.1:8000/accounts/twitter_oauth2/login/callback/

For production use a callback URL such as:

http://{{yourdomain}}.com/accounts/twitter_oauth2/login/callback/

App database configuration through admin

The second part of setting up the X provider requires you to configure your Django application. Configuration is done by creating a SocialApp object in the admin. Add a social app on the admin page:

/admin/socialaccount/socialapp/

Use the X keys tab of your application to fill in the form. It's located:

https://developer.x.com/en/portal/projects/{project-id}/apps/{app-id}/keys

The configuration is as follows:

Xing

App registration (get your key and secret here)

https://dev.xing.com/applications

Development callback URL

http://localhost:8000

Yahoo

Register your OAuth2 app below and enter the resultant client id and secret into admin

https://developer.yahoo.com/apps/create/

The Redirect URL requires secure URLs, please see the section on HTTPS about how this is handled.

When you register the app within yahoo, ensure you select the following API Permissions

  • Email
  • Profile

When copying the supplied Client ID and Client Secret, do not include the 4 starting spaces.

Yandex

App registration (get key and secret here)

https://oauth.yandex.com/client/new

Development callback URL

https://oauth.yandex.com/verification_code

Yandex OAuth app has many different access rights for its services. For the basic access level, you just need to a choose "Yandex.Passport API" section and check "Access to email address" and "Access to username, first name and surname, gender". Everything else is optional.

YNAB

App Registration

https://app.youneedabudget.com/settings/developer

Development callback URL

http://127.0.0.1:8000/accounts/ynab/login/callback/

Default SCOPE permissions are 'read-only'. If this is the desired functionality, do not add SCOPE entry with ynab app in SOCIALACCOUNT_PROVIDERS. Otherwise, adding SCOPE and an empty string will give you read / write.

SOCIALACCOUNT_PROVIDERS = {
    'ynab': {
        'SCOPE': ''
    }
}

Zoho

App Registration

https://api-console.zoho.com/add

Select "Server-base Applications"

Authorized Redirect URI

http://127.0.0.1:8000/accounts/zoho/login/callback/

Zoom

App Registration

https://marketplace.zoom.us/develop/create

Development callback URL

http://127.0.0.1:8000/accounts/zoom/login/callback/

Select scope user:read during app registration.

Adapter

egaauth.socialaccount.adapter.DefaultSocialAccountAdapter

Advanced Usage

Creating and Populating User instances

The following adapter methods can be used to intervene in how User instances are created and populated with data

Custom Redirects

If redirecting to statically configurable URLs (as specified in your project settings) is not flexible enough, then you can override the following adapter methods:

Customizing providers

When an existing provider doesn't quite meet your needs, you might find yourself needing to customize a provider.

This can be achieved by subclassing an existing provider and making your changes there. Providers are defined as django applications, so typically customizing one will mean creating a django application in your project. This application will contain your customized urls.py, views.py and provider.py files. The behaviour that can be customized is beyond the scope of this documentation.

Warning

In your provider.py file, you will need to expose the provider class by having a module level attribute called provider_classes with your custom classes in a list. This allows your custom provider to be registered properly on the basis of the INSTALLED_APPS setting.

Be sure to use a custom id property on your provider class such that its default URLs do not clash with the provider you are subclassing.

class GoogleNoDefaultScopeProvider(GoogleProvider):
    id = 'google_no_scope'

    def get_default_scope(self):
        return []

provider_classes = [GoogleNoDefaultScopeProvider]

Changing provider scopes

Some projects may need more scopes than the default required for authentication purposes.

Scopes can be modified via SOCIALACCOUNT_PROVIDERS in your project settings.py file.

SOCIALACCOUNT_PROVIDERS = {
    '<ProviderNameHere>': {
        'SCOPE': [...]
    }
}

You need to obtain the default scopes that egaauth uses by looking in egaauth/socialaccount/providers/<ProviderNameHere>/provider.py and look for def get_default_scope(self): method. Copy those default scopes into the SCOPE list shown above.

Example of adding calendar.readonly scope to Google scopes:

SOCIALACCOUNT_PROVIDERS = {
    'google': {
        'SCOPE': [
            'profile',
            'email',
            'openid',
            'https://www.googleapis.com/auth/calendar.readonly'
        ],
    }
}

Multi-Factor Authentication

Introduction

The egaauth.mfa app contains all functionality related to Multi-Factor Authentication. It supports:

Note that in order to use this functionality you need to install the mfa extras of the django-egaauth package:

pip install "django-egaauth[mfa]"

Remember to add the app to the settings.py of your project:

INSTALLED_APPS = [
    ...
    # The required `egaauth` apps...
    'egaauth',
    'egaauth.account',

    # The MFA app:
    'egaauth.mfa',
    ...
]

Note that WebAuthn support is disabled by default.

Configuration

Available settings:

MFA_ADAPTER (default: "egaauth.mfa.adapter.DefaultMFAAdapter")

Specifies the adapter class to use, allowing you to alter certain default behavior.

MFA_ALLOW_UNVERIFIED_EMAIL (default: False)

By default, an account that has an unverified email address is not allowed to turn on MFA. Additionally, accounts that have MFA turned on cannot add a new (unverified) email address. The rationale is that if this were allowed, it would allow an attacker to signup without verifying and then turn on MFA to prevent the real owner of the account from ever gaining access. If the risk of this scenario is manageable for your project, you can allow unverified email address in combination with MFA by changing this setting.

MFA_FORMS

Used to override forms. Defaults to:

MFA_FORMS = {
    'authenticate': 'egaauth.mfa.base.forms.AuthenticateForm',
    'reauthenticate': 'egaauth.mfa.base.forms.AuthenticateForm',
    'activate_totp': 'egaauth.mfa.totp.forms.ActivateTOTPForm',
    'deactivate_totp': 'egaauth.mfa.totp.forms.DeactivateTOTPForm',
    'generate_recovery_codes': 'egaauth.mfa.recovery_codes.forms.GenerateRecoveryCodesForm',
}
MFA_PASSKEY_LOGIN_ENABLED (default: False)

Whether or not end users can login using a (WebAuthn) passkey. Note that for this to be enabled, you also need to add "webauthn" to MFA_SUPPORTED_TYPES.

MFA_PASSKEY_SIGNUP_ENABLED (default: False)

Whether or not end users can signup using a (WebAuthn) passkey. Note that for this to be enabled, you need to add "webauthn" to MFA_SUPPORTED_TYPES, require mandatory email verification and have ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED set to True.

MFA_RECOVERY_CODE_COUNT (default: 10)

The number of recovery codes.

MFA_RECOVERY_CODE_DIGITS (default: 8)

The number of digits of each recovery code.

MFA_SUPPORTED_TYPES (default: [["recovery_codes", "totp"])

The authenticator types that end users are able to setup. Allowed types are: "recovery_codes", "totp", and "webauthn". The latter is disabled by default.

MFA_TOTP_PERIOD (default: 30)

The period that a TOTP code will be valid for, in seconds.

MFA_TOTP_TOLERANCE (default: 0)

The number of time steps in the past or future to allow. Lower values are more secure, but more likely to fail due to clock drift.

MFA_TOTP_DIGITS (default: 6)

The number of digits for TOTP codes.

MFA_TOTP_ISSUER (default: "")

The issuer (appearing in the TOTP QR code).

MFA_WEBAUTHN_ALLOW_INSECURE_ORIGIN (default: False)

The WebAuthn uses the fido2 package, and versions up to including version 1.1.3 do not regard localhost as a secure origin, which is problematic during local development and testing. To work around that, you can use this setting. Only use this for development, never on production. See commit 8b979313 over at fido2.

MFA_TRUST_ENABLED (default: False)

Enables the "Trust this browser?" functionality, which presents users with MFA enabled the choice to trust their browser allowing them to skip authenticating per MFA on each login. This is implemented by handing out a special trust cookie.

MFA_TRUST_COOKIE_AGE (default: timedelta(days=14))

Specifies the period (in seconds, or timedelta) during which MFA is skipped.

MFA_TRUST_COOKIE_NAME (default: "mfa_trusted")

The name of the trust cookie.

MFA_TRUST_COOKIE_DOMAIN (default: settings.SESSION_COOKIE_DOMAIN)

The domain of the trust cookie.

MFA_TRUST_COOKIE_HTTPONLY (default: settings.SESSION_COOKIE_HTTPONLY)

Whether or not the trust cookie is HTTP only.

MFA_TRUST_COOKIE_PATH (default: settings.SESSION_COOKIE_PATH)

The path set on the trust cookie.

MFA_TRUST_COOKIE_SAMESITE (default: settings.SESSION_COOKIE_SAMESITE)

The value of the SameSite flag on the trust cookie.

MFA_TRUST_COOKIE_SECURE (default: settings.SESSION_COOKIE_SECURE)

Whether to use a secure cookie for the trust cookie.

Forms

(Re)authenticate

Path

egaauth.mfa.base.forms.AuthenticateForm egaauth.mfa.base.forms.ReauthenticateForm egaauth.mfa.webauthn.forms.AuthenticateWebAuthnForm

Used on:

AuthenticateView and ReauthenticateView, used when a user authenticates with MFA.

Example override:

from egaauth.mfa.base.forms import AuthenticateForm, ReauthenticateForm
from egaauth.mfa.webauthn.forms import AuthenticateWebAuthnForm


class MyCustomAuthenticateForm(AuthenticateForm):
    pass


class MyCustomReauthenticateForm(ReauthenticateForm):
    pass


class MyCustomAuthenticateWebAuthnForm(AuthenticateWebAuthnForm):
    pass

settings.py:

MFA_FORMS = {
    'authenticate': 'mysite.forms.MyCustomAuthenticateForm',
    'reauthenticate': 'mysite.forms.MyCustomReauthenticateForm',
    'authenticate_webauthn': 'mysite.forms.MyCustomAuthenticateWebAuthnForm',
}

Activate TOTP

Path

egaauth.mfa.totp.forms.ActivateTOTPForm

Used on:

ActivateTOTPView, used when a user activates TOTP.

Example override:

from egaauth.mfa.totp.forms import ActivateTOTPForm
class MyCustomActivateTOTPForm(ActivateTOTPForm):
  pass

settings.py:

MFA_FORMS = {
    'activate_totp': 'mysite.forms.MyCustomActivateTOTPForm',
}

Deactivate TOTP

Path

egaauth.mfa.totp.forms.DeactivateTOTPForm

Used on:

DeactivateTOTPView, used when a user deactivates TOTP.

Example override:

from egaauth.mfa.totp.forms import DeactivateTOTPForm
class MyCustomDeactivateTOTPForm(DeactivateTOTPForm):
  pass

settings.py:

MFA_FORMS = {
    'deactivate_totp': 'mysite.forms.MyCustomDeactivateTOTPForm',
}

Generate Recovery Codes

Path

egaauth.mfa.recovery_codes.forms.GenerateRecoveryCodesForm

Used on:

GenerateRecoveryCodesView, used when a user generates recovery codes.

Example override:

from egaauth.mfa.recovery_codes.forms import GenerateRecoveryCodesForm
class MyCustomGenerateRecoveryCodesForm(GenerateRecoveryCodesForm):
  pass

settings.py:

MFA_FORMS = {
    'generate_recovery_codes': 'mysite.forms.MyCustomGenerateRecoveryCodesForm',
}

Adapter

egaauth.mfa.adapter.DefaultMFAAdapter

Encrypted MFA Adapter

egaauth.contrib.mfa.adapter.EncryptedMFAAdapter encrypts stored TOTP secrets using Fernet/MultiFernet.

Configuration

MFA_ADAPTER = "egaauth.contrib.mfa.adapter.EncryptedMFAAdapter"

Optional key rotation:

EGAAUTH_MFA_FERNET_KEYS = [
    "base64-urlsafe-fernet-key-1",
    "base64-urlsafe-fernet-key-2",
]

If explicit keys are not supplied, a deterministic Fernet key is derived from SECRET_KEY so projects get encrypted-at-rest storage by default.

WebAuthn

WebAuthn support is disabled by default. To enable it, add these settings:

# Make sure "webauthn" is included.
MFA_SUPPORTED_TYPES = ["totp", "webauthn", "recovery_codes"]

# Optional: enable support for logging in using a (WebAuthn) passkey.
MFA_PASSKEY_LOGIN_ENABLED = True

# Optional -- use for local development only: the WebAuthn uses the
#``fido2`` package, and versions up to including version 1.1.3 do not
# regard localhost as a secure origin, which is problematic during
# local development and testing.
MFA_WEBAUTHN_ALLOW_INSECURE_ORIGIN = True

# Add "humanize" contrib app if using default templates
INSTALLED_APPS = [
    ...
    "django.contrib.humanize",
]

Upgrading from django-egaauth-2fa

In case you are currently using django-egaauth-2fa and want to switch to the built-in implementation, you will need to migrate the existing TOTP keys and recovery codes. For that purpose, the following reference code is available:

import base64

from egaauth.mfa.adapter import get_adapter
from egaauth.mfa.models import Authenticator
from django.core.management.base import BaseCommand
from django_otp.plugins.otp_static.models import StaticDevice
from django_otp.plugins.otp_totp.models import TOTPDevice


class Command(BaseCommand):
    def handle(self, **options):
        adapter = get_adapter()
        authenticators = []
        for totp in TOTPDevice.objects.filter(confirmed=True).iterator():
            recovery_codes = set()
            for sdevice in StaticDevice.objects.filter(confirmed=True, user_id=totp.user_id).iterator():
                recovery_codes.update(sdevice.token_set.values_list("token", flat=True))
            secret = base64.b32encode(bytes.fromhex(totp.key)).decode("ascii")
            totp_authenticator = Authenticator(
                user_id=totp.user_id,
                type=Authenticator.Type.TOTP,
                data={"secret": adapter.encrypt(secret)},
            )
            authenticators.append(totp_authenticator)
            authenticators.append(
                Authenticator(
                    user_id=totp.user_id,
                    type=Authenticator.Type.RECOVERY_CODES,
                    data={
                        "migrated_codes": [adapter.encrypt(c) for c in recovery_codes],
                    },
                )
            )
        Authenticator.objects.bulk_create(authenticators)

User Sessions

Introduction

The purpose of the optional egaauth.usersessions app is to keep track of (authenticated) user sessions, allowing users to view a list of all their active sessions, as well as offering a means to end these sessions.

Installation

On top of the standard installation, add the following:

INSTALLED_APPS = [
    ...
    'django.contrib.humanize',
    'egaauth.usersessions',
    ...
]

MIDDLEWARE = [
    ...
    # Optional -- needed when: USERSESSIONS_TRACK_ACTIVITY = True
    'egaauth.usersessions.middleware.UserSessionsMiddleware',
    ...
]

Configuration

Available settings:

USERSESSIONS_ADAPTER (default: "egaauth.usersessions.adapter.DefaultUserSessionsAdapter")

Specifies the adapter class to use, allowing you to alter certain default behaviour.

USERSESSIONS_TRACK_ACTIVITY (default: False)

Whether or not user sessions are kept updated. User sessions are created at login time, but as the user continues to access the site the IP address might change. Enabling this setting makes sure that the session is kept track of, meaning, the IP address, user agent and last seen timestamp are all kept up to date. Requires egaauth.usersessions.middleware.UserSessionsMiddleware to be installed.

Signals

The following signal is emitted while handling user sessiond.

Adapter

egaauth.usersessions.adapter.DefaultUserSessionsAdapter

Headless

Introduction

Support for single-page and mobile applications is offered by the egaauth.headless app. Note that you still need to have e.g. the egaauth.account app installed for this to work, yet, you can completely disable its views using settings.HEADLESS_ONLY = True.

Installation

In order to use the headless functionality you need to install the headless extra as follows:

pip install "django-egaauth[headless]"

In your settings.py, include:

INSTALLED_APPS = [
    ...

    # Required
    'egaauth',
    'egaauth.account',
    'egaauth.headless',

    # Optional
    'egaauth.socialaccount',
    'egaauth.mfa',
    'egaauth.usersessions',

    ...
]

# These are the URLs to be implemented by your single-page application.
HEADLESS_FRONTEND_URLS = {
    "account_confirm_email": "https://app.project.org/account/verify-email/{key}",
    "account_reset_password_from_key": "https://app.org/account/password/reset/key/{key}",
    "account_signup": "https://app.org/account/signup",
}

Your project urls.py should include:

urlpatterns = [
    # Even when using headless, the third-party provider endpoints are stil
    # needed for handling e.g. the OAuth handshake. The account views
    # can be disabled using `HEADLESS_ONLY = True`.
    path("accounts/", include("egaauth.urls")),

    # Include the API endpoints:
    path("_egaauth/", include("egaauth.headless.urls")),
]

Configuration

Available settings:

HEADLESS_ADAPTER (default: "egaauth.headless.adapter.DefaultHeadlessAdapter")

Specifies the adapter class to use, allowing you to alter certain default behavior.

HEADLESS_CLIENTS (default: ("app", "browser"))

Specifies the supported types of clients for the API. Setting this to e.g. ("app",) will remove all "browser" related endpoints.

HEADLESS_FRONTEND_URLS (default: {})

Email confirmation and password reset mails contain links that by default point to the views from the egaauth.account app. In case you need to point these to your own frontend application, you can do so by configuring this setting, as follows:

HEADLESS_FRONTEND_URLS = {
    "account_confirm_email": "https://app.project.org/account/verify-email/{key}",
    # Key placeholders are automatically populated. You are free to adjust this
    # to your own needs, e.g.
    #
    # "https://app.project.org/account/email/verify-email?token={key}",
    "account_reset_password": "https://app.project.org/account/password/reset",
    "account_reset_password_from_key": "https://app.project.org/account/password/reset/key/{key}",
    "account_signup": "https://app.project.org/account/signup",
    # Fallback in case the state containing the `next` URL is lost and the handshake
    # with the third-party provider fails.
    "socialaccount_login_error": "https://app.project.org/account/provider/callback",
}
HEADLESS_ONLY (default: False)

You can use headless-only mode in case your application fully takes care of the frontend, and you do not want for e.g. the login and signup views to be accessible. In this case, including egaauth.urls skips those views, yet, still includes e.g. the provider callback views.

HEADLESS_SERVE_SPECIFICATION (default: False)

Whether or not to serve the OpenAPI specification files. When enabled, the endpoints /_egaauth/openapi.yaml, /_egaauth/openapi.json and /_egaauth/openapi.html become available. This functionality requires the extra django-egaauth[headless-spec] to be installed.

HEADLESS_SPECIFICATION_TEMPLATE_NAME (default: "headless/spec/redoc_cdn.html")

The template used to serve the OpenAPI specification in HTML format. Out of the box, Redoc ("headless/spec/redoc_cdn.html") and Swagger ( ("headless/spec/swagger_cdn.html") are available.

HEADLESS_TOKEN_STRATEGY (default: "egaauth.headless.tokens.strategies.sessions.SessionTokenStrategy")

If you need to change the way tokens are created and handled, you can plug in your own ./tokens.

API

OpenAPI Specification

You can find the documentation for the API itself in its OpenAPI Specification.

Example

The source code repository contains a React example application that demonstrates how the API can be put to use in practice. The documentation for that application can be found in the README. Instructions on how to run it locally can be found there as well, though you can also view a copy running over at react.demo.egaauth.org.

Cross-Origin Resource Sharing (CORS)

In case your project requires CORS handling, the following configuration can be used as a starting point:

MIDDLEWARE = (
    ...
    "corsheaders.middleware.CorsMiddleware",
    ...
)

INSTALLED_APPS = (
    ...
    "corsheaders",
    ...
)

CORS_ALLOWED_ORIGINS = [
    "https://app.project.org",
]

from corsheaders.defaults import default_headers

CORS_ALLOW_HEADERS = (
    *default_headers,
    "x-session-token",
    "x-email-verification-key",
    "x-password-reset-key",
)
CORS_ALLOW_CREDENTIALS = True

Adapter

egaauth.headless.adapter.DefaultHeadlessAdapter

Token Strategies

Introduction

The authentication process is an inherently stateful process, as unauthenticated users are guided through a number of stages in order to become fully authenticated. For example, state is needed to keep track of a user that did successfully complete the password stage, but did not yet pass the two-factor authentication stage. Similarly, in case of email verificiation using a code, we need to keep track of the code and keep track of the number of failed login attempts.

The state of the authentication process is stored in the session. When using egaauth headless in non-browser contexts, such as mobile apps, there is no session cookie that can be used to link clients to their session. So, instead of relying on cookies, the HTTP header X-Session-Token is used to keep track of the session. The app is required to hand over the session token during the authenticaton process.

Once a user is fully authenticated, you can hand out your own type of token by setting up a specific strategy. Out of the box, two token strategies are offered: session-tokens and jwt-tokens.

Session Tokens

Introduction

When using egaauth headless in non-browser contexts, such as mobile apps, a session token is used to keep track of the authentication state. This session token is handed over by the app by providing the X-Session-Token request header.

If you do not have any requirements that prescribe a specific token strategy, the simplest way forward is to use the X-Session-Token authentication mechanism for your own APIs as well. In order to do so, integration with Django Ninja and Django REST framework is offered out of the box.

Securing Your API Endpoints

Django Ninja

For Django Ninja, the following security class is available:

egaauth.headless.contrib.ninja.security.XSessionTokenAuth

An example on how to use that security class in your own code is listed below:

from egaauth.headless.contrib.ninja.security import x_session_token_auth
from ninja import NinjaAPI

api = NinjaAPI()

@api.get("/your/own/api", auth=[x_session_token_auth])
def your_own_api(request):
    ...
Django REST framework

For Django REST framework, the following authentication class is available:

egaauth.headless.contrib.rest_framework.authentication.XSessionTokenAuthentication

An example on how to use that authentication class in your own code is listed below:

from egaauth.headless.contrib.rest_framework.authentication import (
    XSessionTokenAuthentication,
)
from rest_framework import permissions
from rest_framework.views import APIView

class YourOwnAPIView(APIView):

    authentication_classes = [
        XSessionTokenAuthentication,
    ]
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        ...

JWT Tokens

Introduction

When using the JWT token strategy, an access token and refresh token pair is handed out when the user becomes fully authenticated. The access token is a JWT token, and is typically short-lived. The refresh token can be used to issue a new access token in case the access token is expired.

The use of JWT is mainly beneficial for a multi-service architecture, where authentication can be performed in each service without having to contact a central authority.

Often, the JWT token strategy is incorrectly touted as being stateless. Unless it is acceptable that access tokens can still be used after logging out, state is needed to invalidate outstanding access tokens, diminishing the stateless aspect of the JWT token strategy.

Usage

With the JWT token strategy enabled, the way of working is as follows:

Configuration

Available settings:

HEADLESS_JWT_ALGORITHM (default: "RS256")

The algorithm used to sign the tokens. For asymmetric algorithms (e.g. "RS256"), the HEADLESS_JWT_PRIVATE_KEY is used as the RSA private key. For symmetric algorithms (e.g. "HS256"), HEADLESS_JWT_PRIVATE_KEY is used as the secret. In case a symmetric algorithm is used and the private key is not configured, settings.SECRET_KEY is used as a fallback.

HEADLESS_JWT_PRIVATE_KEY (default: "")

The private key (or secret) used to sign the JWT tokens. For asymmetric algorithms, it can be generated using the following command:

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

Then, include that private_key.pem in your settings.py:

HEADLESS_JWT_PRIVATE_KEY = """
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCiStSwvoSk61uf
cQvkGDmR6gsM2QjVgKxCTPtg3tMhMO7kXq3PPMEiWlF49JicjPWs5vkYcLAsWNVE
...
rfnteLIERvzd4rLi9WjTahfKA2Mq3YNIe3Hw8IDrrczJgd/XkEaENGYXmmNCX22B
gtUcukumVPtrDhGK9i/PG3Q=
-----END PRIVATE KEY-----
"""
HEADLESS_JWT_ACCESS_TOKEN_EXPIRES_IN (default: 300)

The lifetime (in seconds) of the access tokens.

HEADLESS_JWT_REFRESH_TOKEN_EXPIRES_IN (default: 86400)

The lifetime (in seconds) of the refresh tokens.

HEADLESS_JWT_AUTHORIZATION_HEADER_SCHEME (default: "Bearer")

Specifies the HTTP Authorization header scheme for access tokens, e.g.: Authorization: Bearer <access-token>.

HEADLESS_JWT_STATEFUL_VALIDATION_ENABLED (default: False)

When enabled, it is validated that the access token still belongs to an active session. As a result, logging out will immediately invalidate the access token.

HEADLESS_JWT_ROTATE_REFRESH_TOKEN (default: True)

When enabled, refreshing the access token results in a new refresh token as well. The original refresh token is invalidated.

Customization

You can customize the behavior of the JWT token strategy by creating a custom class deriving from JWTTokenStrategy, and pointing settings.HEADLESS_TOKEN_STRATEGY to that class.

egaauth.headless.tokens.strategies.jwt.JWTTokenStrategy

Securing Your API Endpoints

Django Ninja

For Django Ninja, the following security class is available:

egaauth.headless.contrib.ninja.security.JWTTokenAuth

An example on how to use that security class in your own code is listed below:

from egaauth.headless.contrib.ninja.security import jwt_token_auth
from ninja import NinjaAPI

api = NinjaAPI()

@api.get("/your/own/api", auth=[jwt_token_auth])
def your_own_api(request):
    ...
Django REST framework

For Django REST framework, the following authentication class is available:

egaauth.headless.contrib.rest_framework.authentication.JWTTokenAuthentication

An example on how to use that authentication class in your own code is listed below:

from egaauth.headless.contrib.rest_framework.authentication import (
    JWTTokenAuthentication,
)
from rest_framework import permissions
from rest_framework.views import APIView

class YourOwnAPIView(APIView):

    authentication_classes = [
        JWTTokenAuthentication,
    ]
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        ...

Abstract Token Strategy

If the token strategies that are offered out of the box do not suit your needs you can create your own strategy. Your strategy should match the following interface:

egaauth.headless.tokens.strategies.base.AbstractTokenStrategy

Frequently Asked Questions

My signup requires additional inputs, is that supported?

Yes, though an important thing to keep in mind is that there is no one-to-one mapping of headed forms to headless input payloads. For example, while having to enter your password twice makes sense in a headed environment, it is pointless from an API point of view. As a result, the headed forms that can be overridden by means of ACCOUNT_FORMS play no role in the headless environment.

Instead of overriding the complete signup form via ACCOUNT_FORMS, provide a ACCOUNT_SIGNUP_FORM_CLASS as documented here , listing the additional fields you need. These fields will automatically show up in the headed signup form, and will automatically be validated when posting payloads to the signup endpoint.

Identity Provider

You can easily enable identity provider functionality in your own projects by means of egaauth.idp. While this module is structured to allow for multiple protocols, currently, there is only one protocol supported: OpenID Connect.

OpenID Connect

Introduction

The egaauth.idp package offers an out of the box OpenID Connect provider by means of the egaauth.idp.oidc Django application. The provider functionality is dependent on the base egaauth.account handling, and works seamlessly with any of the other egaauth packages.

The following OpenID Connect functionality is supported:

Functionality intentionally not supported:

Installation

In order to use this functionality you need to install the idp-oidc extra as follows:

pip install "django-egaauth[idp-oidc]"

As the provider functionality is dependent on the regular egaauth account handling, you will need to follow the installation instructions related to egaauth.account first. On top of that, you will need to add the following to your project setup.

In your settings.py, include the OpenID Connect provider app:

INSTALLED_APPS = [
    ...
    "egaauth.idp.oidc",
    ...
]

Next, create a private key using the following command:

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

Then, include that private_key.pem in your settings.py:

IDP_OIDC_PRIVATE_KEY = """
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCiStSwvoSk61uf
cQvkGDmR6gsM2QjVgKxCTPtg3tMhMO7kXq3PPMEiWlF49JicjPWs5vkYcLAsWNVE
...
rfnteLIERvzd4rLi9WjTahfKA2Mq3YNIe3Hw8IDrrczJgd/XkEaENGYXmmNCX22B
gtUcukumVPtrDhGK9i/PG3Q=
-----END PRIVATE KEY-----
"""

Your project urls.py should include:

urlpatterns = [
    ...
    path("", include("egaauth.idp.urls")),
    ...
]

Configuration

Available settings:

IDP_OIDC_ACCESS_TOKEN_EXPIRES_IN (default: 3600)

The time (in seconds) after which access tokens expire.

IDP_OIDC_ACCESS_TOKEN_FORMAT (default: "opaque")

The format of issued access tokens. This can be "opaque" for randomized strings, or, "jwt" for JWT based access tokens.

IDP_OIDC_ADAPTER (default: "egaauth.idp.oidc.adapter.DefaultOIDCAdapter")

Specifies the adapter class to use, allowing you to alter certain default behavior.

IDP_OIDC_AUTHORIZATION_CODE_EXPIRES_IN (default: 60)

The time (in seconds) after which authorization codes expire.

IDP_OIDC_DEVICE_CODE_EXPIRES_IN (default: 300)

The time (in seconds) after which device codes expire.

IDP_OIDC_DEVICE_CODE_INTERVAL (default: 5)

The time (in seconds) a client should wait between polling attempts when using the device authorization flow.

IDP_OIDC_ID_TOKEN_EXPIRES_IN (default: 300)

The time (in seconds) after which ID tokens expire.

IDP_OIDC_USER_CODE_FORMAT (default: settings.egaauth_USER_CODE_FORMAT)

Controls the format of the user code.

IDP_OIDC_PRIVATE_KEY (default: "")

The private key used for creating ID tokens (and .well-known/jwks.json).

IDP_OIDC_RATE_LIMITS (default: {"device_user_code": "5/m/ip"})

Rate limit configuration.

IDP_OIDC_ROTATE_REFRESH_TOKEN (default: True)

When access tokens are refreshed the old refresh token can be kept (False) or replaced (True) with a new one (rotated).

IDP_OIDC_RP_INITIATED_LOGOUT_ASKS_FOR_OP_LOGOUT (default: True)

During the RP initiated logout, the OIDC specification recommends that the end user is asked whether or not to logout of the OP as well. When this setting is True, the end user is always asked. When False, the user is only asked if needed according to the specification.

IDP_OIDC_USERINFO_ENDPOINT (default: None)

This setting can be used to point the userinfo_endpoint value as returned in the ".well-known/openid-configuration" to a custom URL. Setting this disables the built-in userinfo endpoint.

URLs & Views

Your project urls.py should include:

urlpatterns = [
    ...
    path("", include("egaauth.idp.urls")),
    ...
]

The above will enable the following views:

/.well-known/openid-configuration

Provides the OpenID Provider Configuration Information. This endpoint returns a JSON document that includes metadata about the OIDC provider, such as supported authentication methods, token endpoints, and available scopes.

/.well-known/jwks.json

Serves the JSON Web Key Set (JWKS) used to verify the signatures of JWTs issued by the OIDC provider. Clients use this to validate ID tokens and access tokens.

/identity/o/authorize

The authorization endpoint used to initiate the OAuth2/OIDC flow. It handles authentication requests and issues authorization codes or tokens based on the request parameters.

/identity/o/api/revoke

Allows clients to revoke access or refresh tokens. This endpoint helps maintain security by invalidating credentials that are no longer needed or have been compromised.

/identity/o/api/userinfo

Returns user profile information in a JSON format. This endpoint is typically used after a successful authentication to fetch claims about the authenticated user.

/identity/o/api/token

Handles the exchange of authorization codes for tokens, or client credentials for access tokens. This is a key component of the token flow in OAuth2/OIDC.

Clients

The Open ID Connect clients (also commonly referred to as consumers, or applications) can be managed via the Django admin. A typical client configuration has the following properties:

Name

The name of the client. This is displayed in the authorization page.

ID

The client ID. This field is automatically populated.

Secret

The client secret. When adding clients using the Django admin, the secret is automatically generated and displayed only once via a message at creation time.

Scopes

The scope(s) the client is allowed to request. Values are provide line by line, e.g.:

openid
profile
email
Default scopes

In case the client does not specify any scope, this are the default scope that will be used. Values are provide line by line.

Grant types

A list of allowed grant types. Provide one value per line, e.g.:

authorization_code
client_credentials
refresh_token
CORS origins

A list of allowed origins for cross-origin requests, one per line.

Redirect URIs

A list of allowed redirect (callback) URLs, one per line.

Allow URI wildcards

When enabled, allows the use of wildcards () in redirect URIs and CORS origins. This is particularly useful for supporting deploy previews from services like Cloudflare Pages or Netlify, where preview URLs may contain dynamic subdomains (e.g., ``https://.project.pages.dev``). When wildcards are enabled:

  • URIs can contain a single asterisk (*)
  • Only the hostname portion of the URI is allowed to contain a wildcard
Response types

A list of allowed response types. Provide one value per line, e.g.:

code
Skip consent

When enabled, the consent page is silently skipped and all requested scopes are granted.

Type

Confidential clients are clients that are able to securely authenticate with the authorization server as they are able to keep their registered client secret safe. Public clients, such as applications running in a browser or mobile device, are unable to keep the client secrets safe.

Adapter

egaauth.idp.oidc.adapter.DefaultOIDCAdapter

Integrations

In order to simplify implementing your own OIDC based API endpoints, egaauth offers out of the box support for authenticating and authorizing requests with Django Ninja as well as Django REST framework.

Django Ninja

For Django Ninja, the following security class is available:

egaauth.idp.oidc.contrib.ninja.security.TokenAuth

An example on how to use that security class in your own code is listed below:

from egaauth.idp.oidc.contrib.ninja.security import TokenAuth
from ninja import NinjaAPI

api = NinjaAPI()

@api.get("/api/resource", auth=[TokenAuth(scope=["view-resource"])])
def resource(request):
    ...

Django REST framework

For Django REST framework, the following authentication class is available:

egaauth.idp.oidc.contrib.rest_framework.authentication.TokenAuthentication

egaauth.idp.oidc.contrib.rest_framework.permissions.TokenPermission

An example on how to use that authentication class in your own code is listed below:

from rest_framework.views import APIView

from egaauth.idp.oidc.contrib.rest_framework.authentication import TokenAuthentication
from egaauth.idp.oidc.contrib.rest_framework.permissions import TokenPermission


class ResourceView(APIView):
    authentication_classes = [TokenAuthentication]
    permission_classes = [TokenPermission.has_scope(["view-resource"])]

    def get(request, *args, **kwargs):
        ...

Common Functionality

Configuration

Available settings:

egaauth_DEFAULT_AUTO_FIELD

Can be set to configure the primary key of all models. For example: "hashid_field.HashidAutoField".

egaauth_USER_CODE_FORMAT (default: {"numeric": False, "dashed": True, length: 8})

Controls the format of user-facing verification codes (e.g. email verification, phone verification, login codes).

Sending Email

Emails sent (e.g. in case of password forgotten or email confirmation) can be altered by providing your own templates. Templates are named as follows:

account/email/email_confirmation_signup_subject.txt
account/email/email_confirmation_signup_message.txt

account/email/email_confirmation_subject.txt
account/email/email_confirmation_message.txt

In case you want to include an HTML representation, add an HTML template as follows:

account/email/email_confirmation_signup_message.html

account/email/email_confirmation_message.html

The project does not contain any HTML email templates out of the box. When you do provide these yourself, note that both the text and HTML versions of the message are sent.

If this does not suit your needs, you can hook up your own custom mechanism by overriding the send_mail method of the account adapter (egaauth.account.adapter.DefaultAccountAdapter).

Templates

Introduction

The templates that are offered out of the box are intentionally plain and without any styling. We do not want to pick a side in the multitudes of frontend styling options out there, and the look and feel typically should be adjusted to match the branding of your project. Therefore it is recommended that you copy all templates over to your own project and adjust them as you see fit.

Having said that, with features such as third party account providers and two-factor authentication, adjusting the templates involves a lot more than just styling a login.html and a signup.html template. Therefore, a mechanism is included that allows you to adjust the look and feel of all templates by only overriding a few core templates. This approach allows you to achieve visual results fast, but is of course more limited compared to styling all templates yourself.

Overriding the Built-In Templates

The egaauth app includes all templates, and can be found in the egaauth/templates directory. When egaauth is part of your INSTALLED_APPS, and "APP_DIRS": True is configured, Django will be able to find its templates. As DIRS is searched before APP_DIRS, overriding the templates involves adding an entry to DIRS that points to your a project specific template folder, as follows:

from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [
            BASE_DIR / "templates"
        ],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

If you copy over all templates to your BASE_DIR / "templates" it should contain these entries (a.o.):

Styling the Existing Templates

Instead of copying all templates, a mechanism is included that allows you to adjust the look and feel of all templates by only overriding a few core templates. This approach allows you to achieve visual results fast, but is of course more limited compared to styling all templates yourself.

Layouts

The existing templates use two base page layouts:

You can alter these layouts by providing these templates in your own project:

Template file Description
egaauth/layouts/base.html The overall base template.
egaauth/layouts/entrance.html The entrance template, extending the base template.
egaauth/layouts/manage.html The account management template, extending the base template.

Elements

When rendering e.g. a Bootstrap button you would typically use:

<button class="btn btn-primary">Okay</button>

Yet, when a different CSS framework is used other class names apply, and possibly even other markup. Therefore, the built-in templates do not include the above content directly. Instead of referring to tags such <button>, <h1> or <form> directly, the templates render those elements using a special element `templatetag`:

{% load egaauth %}
{% element h1 tags="foo,bar" %}Welcome{% endelement %}

Under the hood, this templatetag renders the egaauth/elements/h1.html template, which out of the box contains this:

{% load egaauth %}<h1>{% slot %}{% endslot %}</h1>

If you want to change the styling of all headings across all pages, you can do so by overriding that egaauth/elements/h1.html template, as follows:

{% load egaauth %}
<div class="myproject-h1 aa-{{ origin|slugify }}"
     style="font-size: {% if "foo" in attrs.tags %}3{% else %}5{% endif %}rem">
    {% slot %}{% endslot %}
</div>

Of course, the above is a bit of a contrived example. In each of the element templates the {{ origin }} context variable is available, which is equal to the base template name where the element is used (e.g. account/login for elements used from within the account/login.html template).

Slots may also be named. In that case, the element will be invoked like:

{% load egaauth %}
{% element form method="post" action=action_url %}
    {% slot body %}
        ...
    {% endslot %}
    {% slot actions %}
        ...
    {% endslot %}
{% endelement %}

When overriding an element with named slots, they may be injected in any order. For example, with egaauth/elements/form.html:

{% load egaauth %}
<form method="{{ attrs.method }}" action="{{ attrs.action }}">
    {% slot body %}
    {% endslot %}
    <hr>
    {% slot actions %}
    {% endslot %}
</form>

The following elements are available -- override them as you see fit for your project:

Template file Description
egaauth/elements/alert.html Display alert messages.
egaauth/elements/badge.html Badges for labeling purposes.
egaauth/elements/button.html A button (<button>).
egaauth/elements/button_group.html A group of related buttons.
egaauth/elements/details.html Disclosure (<details>).
egaauth/elements/field.html A single form field.
egaauth/elements/fields.html The form fields, uses {{form.as_p}} by default, hence, not rendering the field.html.
egaauth/elements/form.html The <form> container tag.
egaauth/elements/h1.html Level 1 heading (<h1>).
egaauth/elements/h2.html Level 2 heading (<h2>).
egaauth/elements/hr.html Horizontal rule (<hr>).
egaauth/elements/img.html An image tag (<img>).
egaauth/elements/panel.html A panel (aka card), consisting of a title, body and actions.
egaauth/elements/p.html Paragraphs (<p>).
egaauth/elements/provider.html A link to a third-party provider.
egaauth/elements/provider_list.html The container element for the list of third-party providers.
egaauth/elements/table.html Table (<table>).
egaauth/elements/tbody.html Table body (<tbody>).
egaauth/elements/td.html Table data cell (<td>).
egaauth/elements/th.html Table header cell (<th>).
egaauth/elements/thead.html Table head (<thead>).
egaauth/elements/tr.html Table row (<tr>).

Example

The source repository contains a Bootstrap styled example project, which provides a good example of how all of the above can be put together to provide styling without altering any of the content templates. Please take a look at the templates of the example project. You can see those templates live in the running demo project.

Messages

The Django messages framework (django.contrib.messages) is used if it is listed in settings.INSTALLED_APPS. All messages (as in django.contrib.messages) are configurable by overriding their respective template. If you want to disable a message, simply override the message template with a blank one.

Admin

The Django admin site (django.contrib.admin) does not use Django egaauth by default. Since Django admin provides a custom login view, it does not go through the normal Django egaauth workflow.

Warning

This limitation means that Django egaauth features are not applied to the Django admin site:

  • The admin login is not protected from being brute forced (ACCOUNT_RATE_LIMITS).
  • Two-factor authentication is not enforced.
  • Any other custom workflow that overrides the Django egaauth adapter's login method will not be applied.

An easy workaround for this is to require users to login before going to the Django admin site's login page, by adding this to urls.py (note that the following would need to be applied to every instance of AdminSite):

from django.contrib import admin
from egaauth.account.decorators import secure_admin_login

admin.autodiscover()
admin.site.login = secure_admin_login(admin.site.login)

Rate Limits

In order to be secure out of the box various rate limits are in place. The rate limit mechanism is backed by a Django cache. Hence, rate limiting will not work properly if you are using the DummyCache.

When rate limits are hit the 429.html template is rendered, alternatively, you can configure a custom handler by declaring a handler429 view in your root URLconf.

Rate limits are consumed by triggering actions, the full list of which is documented below. Per action, the rate can be configured. The rate itself is an amount, per time unit, per either IP address, user or action-specific key.

For example, requesting a password reset is an action that is both limited globally by IP address, as well as per email. Here, the email address used is the specific key.

Danger

Rate limits rely on accurate client IP address detection to function correctly. However, it is not possible for django-egaauth to reliably determine the client IP address out of the box because the correct method varies depending on your deployment architecture (direct connections, load balancers, reverse proxies, CDNs, etc.).

The X-Forwarded-For header cannot be used to determine the client IP, as this header can be trivially spoofed by malicious actors, allowing them to completely bypass rate limits.

To ensure rate limits work correctly:

  1. Review and adjust the rate limit configuration settings documented below (egaauth_TRUSTED_PROXY_COUNT, egaauth_TRUSTED_CLIENT_IP_HEADER) to match your security requirements and deployment architecture.
  2. If the settings are not sufficient, override the account adapter's get_client_ip() method to implement custom logic for extracting the real client IP address from the correct header(s) for your specific infrastructure. See the adapter documentation for guidance on implementing a custom adapter.

Configuration

egaauth_TRUSTED_PROXY_COUNT (default: 0)

As the X-Forwarded-For header can be spoofed, you need to configure the number of proxies that are under your control and hence, can be trusted. The default is 0, meaning, no proxies are trusted. As a result, the X-Forwarded-For header will be disregarded by default.

egaauth_TRUSTED_CLIENT_IP_HEADER (default: None)

If your service is running behind a trusted proxy that sets a custom header containing the client IP address, specify that header name here. The client IP will be extracted from this header instead of X-Forwarded-For. Examples: "CF-Connecting-IP" (Cloudflare), "X-Real-IP" (nginx).

Implementation Notes

The builtin rate limitting relies on a cache and uses non-atomic operations, making it vulnerable to race conditions. As a result, users may occasionally bypass the intended rate limit due to concurrent access. However, such race conditions are rare in practice. For example, if the limit is set to 10 requests per minute and a large number of parallel processes attempt to test that limit, you may occasionally observe slight overruns—such as 11 or 12 requests slipping through. Nevertheless, exceeding the limit by a large margin is highly unlikely due to the low probability of many processes entering the critical non-atomic code section simultaneously.

Testing

Unless the rate limit is disabled or the default limits are increased, you might run intro problems if you're running unit tests that are dependant on funcionalities covered by the rate limits. For example, if you're testing the confirm_email functionality in your unit tests and you're testing if the verification email is sent twice after requesting it twice, only one of the emails will be sent.

Project

Commercial Support

Commercial support is available. If you find certain functionality missing, or require assistance on your project(s), please contact us: info@intenct.nl.

Funding

Maintaining a project of this nature to align with the latest standards and expectations demands a significant investment of time and effort. If you are using this project, especially in a commercial context, then please do consider sponsoring.

Contributing

Thank you for considering contributing to django-egaauth! This document outlines the process for contributing to the project and sets up your development environment.

Code of Conduct

In the interest of fostering an open and welcoming community, we as contributors and maintainers pledge to make participation in our project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

Getting Started

Setting Up Your Development Environment

There are two primary ways to set up your development environment:

  1. Install system dependencies

    Before creating a virtual environment, you'll need to install some system dependencies:

    On macOS:

    # Using Homebrew
    brew install libxml2 libxmlsec1 pkg-config openssl

    On Ubuntu/Debian:

    sudo apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl pkg-config

    On RHEL/CentOS/Fedora:

    sudo dnf install libxml2-devel xmlsec1-devel xmlsec1-openssl-devel libtool-ltdl-devel
  2. Create and activate a virtual environment

    # Create a virtual environment
    python -m venv virtualenv
    
    # Activate it
    # On Windows:
    virtualenv\Scripts\activate
    # On macOS/Linux:
    source virtualenv/bin/activate
  3. Install django-egaauth in development mode

    # Install development dependencies
    pip install -r requirements-dev.txt

If you prefer a more isolated and reproducible development environment, you can use Nix-based devenv:

  1. Install devenv (If you don't have it already)

    Follow the official installation instructions.

  2. Activate the developer environment

    # This will create an isolated environment with all required dependencies
    devenv shell

    Note: The first time you run this command, it may take a significant amount of time as it builds all dependencies. Subsequent launches will be much faster.

Running Tests

django-egaauth uses a comprehensive test suite. You can run tests in several ways:

Using pytest directly

# Run all tests for the default setup
pytest egaauth/

# Run tests with a specific Django settings module
pytest --ds=tests.projects.regular.settings tests/

# Run a specific test file
pytest tests/apps/account/test_login.py

Note, if you are using MacOS, using pip and get this error when run tests:

import xmlsec
ImportError: dlopen( ...  symbol not found in flat namespace '_xmlSecOpenSSLTransformHmacRipemd160GetKlass')

You can try:

pip uninstall xmlsec lxml
pip install --no-binary :all: xmlsec
# Ref: https://github.com/xmlsec/python-xmlsec/issues/320

Nox automates testing across different Python and Django versions:

# List all available sessions
nox --list

# Run tests for a specific Python version
nox -x --session "test-3.11"

# Run tests for specific environment
nox -x --session "test-3.11" --python 3.11 -- --ds=tests.projects.regular.settings tests/apps/account/test_login.py

Run Code Quality Checks

# Run all linting checks
nox -t lint

# Run specific check
nox --session black
nox --session isort
nox --session flake8
nox --session bandit
nox --session djlint

Building Documentation

Documentation is built using Sphinx:

# Build the documentation
nox --session docs

The built documentation will be available in the docs/_build/html directory.

Development Workflow

  1. Create a new branch for your feature or bugfix

    git checkout -b feature/your-feature-name
  2. Make your changes and add tests

    All new features should include proper tests.

  3. Run tests locally to ensure everything passes

    nox -x --session "test-3.11"
  4. Run code quality checks

    nox -t lint
  5. Commit your changes with meaningful commit messages

  6. Submit a pull request to the main repository

Pull Request Guidelines

Additional Resources

Thank you for your contributions!

Frequently Asked Questions

Overall

Why don't you implement support for ... ?

This app is just about authentication. Anything that is project specific, such as making choices on what to display in a profile page, or, what information is stored for a user (e.g. home address, or favorite color?), is beyond scope and therefore not offered.

This information is nice and all, but... I need more!

Here are a few third party resources to help you get started:

I think I found a security issue... now what?

Please report security issues only to security@egaauth.org.

You may also want to subscribe to django-egaauth-announce@googlegroups.com to get notified about security releases.

Troubleshooting

The /accounts/ URL is giving me a 404

There is no such URL. Try /accounts/login/ instead.

When I attempt to login I run into a 404 on /accounts/profile/

When you end up here you have successfully logged in. However, you will need to implement a view for this URL yourself, as whatever is to be displayed here is project specific. You can also decide to redirect elsewhere:

https://docs.djangoproject.com/en/dev/ref/settings/#login-redirect-url

When I sign up I run into connectivity errors (connection refused et al)

You probably have not got an email (SMTP) server running on the machine you are developing on. Therefore, egaauth is unable to send verification mails.

You can work around this by adding the following line to settings.py:

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

This will avoid the need for an SMTP server as emails will be printed to the console. For more information, please refer to:

https://docs.djangoproject.com/en/dev/ref/settings/#email-host

Development

Why did you just close my issue?

Time is limited and I have to pick my battles. Please do not file the following types of issues:

Release Notes

Recent

History

65.15.1 (2026-04-02)

Note

💙 Is django-egaauth's authentication the entrance to your business? Please consider supporting its continued development by becoming a sponsor at https://egaauth.org/sponsors/. Your support helps keep this project thriving!

Fixes

65.15.0 (2026-03-09)

Note worthy changes

Backwards incompatible changes

65.14.3 (2026-02-13)

Fixes

65.14.2 (2026-02-13)

Security notice

65.14.1 (2026-02-07)

Fixes

Security notice

65.14.0 (2026-01-17)

Note worthy changes

Fixes

65.13.1 (2025-11-20)

Note worthy changes

Fixes

65.13.0 (2025-10-31)

Note worthy changes

Security notice

Backwards incompatible changes

65.12.1 (2025-10-16)

Security notice

65.12.0 (2025-10-05)

Note worthy changes

65.11.2 (2025-09-09)

Fixes

65.11.1 (2025-08-27)

Security notice

65.11.0 (2025-08-15)

Note worthy changes

Fixes

Backwards incompatible changes

65.10.0 (2025-07-10)

Note worthy changes

Backwards incompatible changes

Fixes

65.9.0 (2025-06-01)

Note worthy changes

65.8.1 (2025-05-21)

Fixes

Security notice

65.8.0 (2025-05-08)

Note worthy changes

65.7.0 (2025-04-03)

Note worthy changes

Fixes

65.6.0 (2025-03-27)

Note worthy changes

Fixes

65.5.0 (2025-03-14)

Note worthy changes

Fixes

65.4.1 (2025-02-07)

Fixes

65.4.0 (2025-02-06)

Note worthy changes

2024

2023

2022

2021

2020

2019

2018

2017

2016

2015

2014

2013

2012

65.3.1 (2024-12-25)

Fixes

65.3.0 (2024-11-30)

Note worthy changes

Security notice

65.2.0 (2024-11-08)

Note worthy changes

Fixes

Security notice

65.1.0 (2024-10-23)

Note worthy changes

Fixes

65.0.2 (2024-09-27)

Fixes

65.0.1 (2024-09-23)

Fixes

65.0.0 (2024-09-22)

Note worthy changes

Backwards incompatible changes

64.2.1 (2024-09-05)

Fixes

Security notice

64.2.0 (2024-08-30)

Note worthy changes

Security notice

64.1.0 (2024-08-15)

Note worthy changes

Security notice

64.0.0 (2024-07-31)

Note worthy changes

Backwards incompatible changes

0.63.6 (2024-07-12)

Security notice

0.63.5 (2024-07-11)

Fixes

0.63.4 (2024-07-10)

Security notice

0.63.3 (2024-05-31)

Note worthy changes

Backwards incompatible changes

Security notice

0.63.2 (2024-05-24)

Note worthy changes

0.63.1 (2024-05-17)

Note worthy changes

0.63.0 (2024-05-14)

Note worthy changes

0.62.1 (2024-04-24)

0.62.0 (2024-04-22)

Note worthy changes

Backwards incompatible changes

0.61.1 (2024-02-09)

Fixes

Security notice

0.61.0 (2024-02-07)

Note worthy changes

Security notice

Backwards incompatible changes

0.60.1 (2024-01-15)

Fixes

Backwards incompatible changes

0.60.0 (2024-01-05)

Note worthy changes

Backwards incompatible changes

0.59.0 (2023-12-13)

Note worthy changes

Backwards incompatible changes

0.58.2 (2023-11-06)

Fixes

0.58.1 (2023-10-29)

Fixes

0.58.0 (2023-10-26)

Note worthy changes

Backwards incompatible changes

Security notice

0.57.0 (2023-09-24)

Note worthy changes

Fixes

0.56.1 (2023-09-08)

Security notice

0.56.0 (2023-09-07)

Note worthy changes

Backwards incompatible changes

0.55.2 (2023-08-30)

Fixes

0.55.1 (2023-08-30)

Fixes

0.55.0 (2023-08-22)

Note worthy changes

Security notice

Backwards incompatible changes

0.54.0 (2023-03-31)

Note worthy changes

Security notice

0.53.1 (2023-03-20)

Note worthy changes

0.53.0 (2023-03-16)

Note worthy changes

0.52.0 (2022-12-29)

Note worthy changes

0.51.0 (2022-06-07)

Note worthy changes

Security notice

The reset password form is protected by rate limits. There is a limit per IP, and per email. In previous versions, the latter rate limit could be bypassed by changing the casing of the email address. Note that in that case, the former rate limit would still kick in.

0.50.0 (2022-03-25)

Note worthy changes

0.49.0 (2022-02-22)

Note worthy changes

Backwards incompatible changes

0.48.0 (2022-02-03)

Note worthy changes

Backwards incompatible changes

0.47.0 (2021-12-09)

Note worthy changes

Backwards incompatible changes

Security notice

Automatically signing in users into their account and connecting additional third party accounts via a simple redirect ("/accounts/facebook/login/") can lead to unexpected results and become a security issue especially when the redirect is triggered from a malicious web site. For example, if an attacker prepares a malicious website that (ab)uses the Facebook password recovery mechanism to first sign into his/her own Facebook account, followed by a redirect to connect a new social account, you may end up with the attacker's Facebook account added to the account of the victim. To mitigate this, SOCIALACCOUNT_LOGIN_ON_GET is introduced.

0.46.0 (2021-11-15)

Note worthy changes

0.45.0 (2021-07-11)

Note worthy changes

0.44.0 (2020-11-25)

Security notice

In previous versions, the mechanism to prevent too many failed login attempts (ACCOUNT_LOGIN_ATTEMPTS_LIMIT) could be bypassed by changing the casing of the login.

Backwards incompatible changes

0.43.0 (2020-10-15)

Note worthy changes

Backwards incompatible changes

0.42.0 (2020-05-24)

Note worthy changes

0.41.0 (2019-12-18)

Security notice

Note worthy changes

Backwards incompatible changes

0.40.0 (2019-08-29)

Note worthy changes

Backwards incompatible changes

0.39.1 (2019-02-28)

Note worthy changes

Backwards incompatible changes

0.39.0 (2019-02-26)

Note worthy changes

Backwards incompatible changes

0.38.0 (2018-10-03)

Security notice

The {% user_display user %} tag did not escape properly. Depending on the username validation rules, this could lead to XSS issues.

Note worthy changes

0.37.1 (2018-08-27)

Backwards incompatible changes

0.37.0 (2018-08-27)

Note worthy changes

0.36.0 (2018-05-08)

Note worthy changes

Security notice

0.35.0 (2018-02-02)

Note worthy changes

Security notice

Backwards incompatible changes

Note worthy changes

0.34.0 (2017-10-29)

Security notice

Note worthy changes

0.33.0 (2017-08-20)

Note worthy changes

Backwards incompatible changes

0.32.0 (2017-04-27)

Note worthy changes

Backwards incompatible changes

0.31.0 (2017-02-28)

Note worthy changes

Backwards incompatible changes

0.30.0 (2017-01-01)

Note worthy changes

Backwards incompatible changes

0.29.0 (2016-11-21)

Note worthy changes

0.28.0 (2016-10-13)

Security notice

Note worthy changes

0.27.0 (2016-08-18)

Note worthy changes

Backwards incompatible changes

0.26.1 (2016-07-25)

Note worthy changes

0.26.0 (2016-07-24)

Note worthy changes

Backwards incompatible changes

0.25.2 (2016-03-13)

Note worthy changes

0.25.1 (2016-03-13)

Note worthy changes

0.25.0 (2016-03-12)

Note worthy changes

Backwards incompatible changes

None

0.24.1 (2015-11-09)

Note worthy changes

Backwards incompatible changes

0.24.0 (2015-11-08)

Note worthy changes

Backwards incompatible changes

0.23.0 (2015-08-02)

Note worthy changes

Backwards incompatible changes

None

0.22.0 (2015-07-23)

Note worthy changes

Backwards incompatible changes

0.21.0 (2015-07-02)

Note worthy changes

Backwards incompatible changes

0.20.0 (2015-05-25)

Note worthy changes

Backwards incompatible changes

0.19.1 (2015-02-05)

Note worthy changes

0.19.0 (2015-01-04)

Note worthy changes

Backwards incompatible changes

0.18.0 (2014-08-12)

Note worthy changes

Backwards incompatible changes

0.17.0 (2014-06-16)

Note worthy changes

Backwards incompatible changes

None

0.16.1 (2014-03-12)

Note worthy changes

Backwards incompatible changes

None

0.16.0 (2014-03-10)

Note worthy changes

Backwards incompatible changes

0.15.0 (2013-12-01)

Note worthy changes

Backwards incompatible changes

0.14.2 (2013-11-16)

Note worthy changes

Backwards incompatible changes

0.14.1 (2013-10-28)

Note worthy changes

Backwards incompatible changes

None

0.14.0 (2013-10-28)

Note worthy changes

Backwards incompatible changes

0.13.0 (2013-08-31)

Note worthy changes

Backwards incompatible changes

0.12.0 (2013-07-01)

Note worthy changes

Backwards incompatible changes

0.11.1 (2013-06-04)

Note worthy changes

Backwards incompatible changes

None

0.11.0 (2013-06-02)

Note worthy changes

Backwards incompatible changes

None

0.10.1 (2013-04-16)

Note worthy changes

Backwards incompatible changes

None

0.10.0 (2013-04-12)

Note worthy changes

Backwards incompatible changes

0.9.0 (2013-01-30)

Note worthy changes

Backwards incompatible changes

0.8.3 (2012-12-06)

Note worthy changes

Backwards incompatible changes

0.8.2 (2012-10-10)

Note worthy changes

Backwards incompatible changes

0.8.1 (2012-09-03)

Note worthy changes

Backwards incompatible changes

None

0.8.0 (2012-09-01)

Note worthy changes

Backwards incompatible changes

0.7.0 (2012-07-18)

Note worthy changes

Backwards incompatible changes

None

0.6.0 (2012-06-20)

Note worthy changes

Backwards incompatible changes

0.5.0 (2012-06-08)

Note worthy changes

Backwards incompatible changes

0.4.0 (2012-03-25)

Note worthy changes

Backwards incompatible changes

None

0.3.0 (2012-01-19)

Note worthy changes

Backwards incompatible changes

Publishing to egaauth.egaforce.live

Recommended canonical URL: https://egaauth.egaforce.live/latest/

Recommended PDF download: https://egaauth.egaforce.live/downloads/django-egaauth-documentation.pdf

Suggested public information architecture

RoutePurpose
/Landing page with latest-version links, quickstart entry points, release highlights, and PDF download.
/latest/Canonical current documentation build.
/releases/<version>/Versioned archives for stable historical browsing.
/providers/Filtered entry point into provider-specific setup pages.
/downloads/PDF manuals and any offline bundles.

Publishing checklist

  1. Update the codebase version, changelog, and docs content together.
  2. Generate a static HTML build and a PDF build from the same source snapshot.
  3. Verify canonical URLs, sitemap, robots, favicons, and social-preview metadata.
  4. Redirect any legacy docs hostnames to the new canonical domain.
  5. Cache static assets aggressively, but keep HTML either unversioned with low TTL or hashed and immutable.
  6. Publish the PDF at a predictable download path and link it from the homepage and footer.
  7. Smoke-test search, provider pages, login-protected examples, and all external links after deployment.

Production recommendations