Skip to main content

Django Rapyd ModernAuth

PyPI version Python versions License CI

A small Django pluggable app that provides a custom User model where the email address is the username. No username field, no extra settings, no migrations beyond the initial one.

Inspiration

Users today expect to use their email address as the username during authentication. This works well because:

  • Users already know their email addresses by heart.
  • Since email addresses are unique, users don't need to remember yet another item when either signing up or logging into web applications. IMHO this is a significant factor that plays a vital role in user adoption.
  • This is a time tested model and many web applications today follow this.

However, Django's default approach for authentication requires a user provide both a username and an email address during sign up and then just use the username during login.

Requirements

  • Python 3.10, 3.11, 3.12, or 3.13
  • Django 4.2 LTS or Django 5.2 LTS

Installation

Important: swap in this user model before running migrate for the first time. Switching AUTH_USER_MODEL mid-project requires hand-written data migrations across every foreign key to the user model. See Django's docs on substituting a custom user model.

Install the package:

pip install django-rapyd-modernauth

Add modernauth to INSTALLED_APPS:

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

Point Django at the custom user model in settings.py:

AUTH_USER_MODEL = "modernauth.User"

Optionally enable case-insensitive login by adding the EmailBackend to your AUTHENTICATION_BACKENDS:

AUTHENTICATION_BACKENDS = [
    "modernauth.backends.EmailBackend",
]

This is optional but recommended. Emails are stored lowercased on save, so the backend lowercases the credential at login time and matches against stored values — meaning Alice@Example.com and alice@example.com resolve to the same account. Without it, Django's default ModelBackend will only match if the consumer's login form lowercases the input first.

Create the database tables:

python manage.py migrate

Usage

The custom User model behaves like Django's default AbstractUser with one difference: email is the unique identifier and there is no username field. Use get_user_model() rather than importing the model directly so your code keeps working if AUTH_USER_MODEL is ever swapped again.

from django.contrib.auth import get_user_model

User = get_user_model()

User.objects.create_user(email="alice@example.com", password="…")
User.objects.create_superuser(email="root@example.com", password="…")

The admin's createsuperuser command will prompt for an email instead of a username:

python manage.py createsuperuser

Login forms, the admin, and authenticate() all use email as the credential.

Emails are normalized to lowercase whenever a user is created or saved, so the address is the canonical identity. With the EmailBackend enabled (see Installation), authenticate() lowercases the supplied credential before lookup, making login case-insensitive.

Migration from 0.0.x

Starting in 0.1.0 the package's behavior changed in two ways:

  • User.save() (and therefore create_user / create_superuser) lowercases email before writing.
  • The new modernauth.backends.EmailBackend lowercases the supplied credential at authentication time, making login case-insensitive when wired into AUTHENTICATION_BACKENDS.

The public API surface is unchanged — same model, same manager methods — but consumers upgrading from 0.0.x should be aware of the points below.

Existing data is not retroactively normalized

This package does not rewrite existing rows. New writes go through the lowercasing path, but rows that already exist with mixed-case local parts (e.g. Alice@example.com) stay as-is until the consumer migrates them. If you want the storage invariant to hold across the whole table, you need to run a one-shot data migration in your own project.

Risk: unique-constraint conflicts

Under the old behavior the unique index on email was byte-comparison, so it was possible to have both Alice@example.com and alice@example.com as separate users. A naive lowercasing migration will fail on the duplicate. De-dup first, then migrate.

Suggested data migration

The sketch below is a starting point, not a turnkey solution. Put it in a data migration in your own project (we don't ship one):

# Sketch: in your project, create a data migration that lowercases existing emails.
def lowercase_emails(apps, schema_editor):
    User = apps.get_model("modernauth", "User")
    for user in User.objects.all():
        lowered = user.email.lower()
        if user.email != lowered:
            # Caller is responsible for de-duping conflicts before running this.
            user.email = lowered
            user.save(update_fields=["email"])

Rolling back

If you don't want this behavior, pin to the last release before the change:

pip install "django-rapyd-modernauth<0.1.0"

Contributing

Pull requests are welcome — the project is intentionally small, so bug fixes, test improvements, and Django LTS support updates are the most likely to land. See CONTRIBUTING.md for the full development setup, test workflow, and release process.

For security vulnerabilities, see SECURITY.md for the private disclosure process. Do not file a public issue for security bugs.

License

Apache License 2.0 — see LICENSE.

Metadata

Release files for django-rapyd-modernauth 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-rapyd-modernauth 0.1.0
File Size Uploaded
django_rapyd_modernauth-0.1.0.tar.gz 16.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-rapyd-modernauth 0.1.0
File Interpreter ABI Platform
django_rapyd_modernauth-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 35.2 kB

Release files / django_rapyd_modernauth-0.1.0.tar.gz

Download URL django_rapyd_modernauth-0.1.0.tar.gz
Size 16.8 kB
Tags Source
SHA-256 checksum
How to use checksums
6202344f9d7a8c7c57b9d99247f7e9270f8d337122d110d976fe979c64e4c1f5
BLAKE2b-256 checksum
How to use checksums
4b8603642826432d2515fbea0b337eb1247675e5aa663b95c0e5bbc1641d8b49
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 4, 2026.

Transparency log

Release files / django_rapyd_modernauth-0.1.0-py3-none-any.whl

Download URL django_rapyd_modernauth-0.1.0-py3-none-any.whl
Size 18.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
11cfcf12d1096df6ab67de1b685d3df9eadf2311e5809e72d44620c87fe5f967
BLAKE2b-256 checksum
How to use checksums
dafe3c7947416a3e73ba15f06e02be1eb59388acf1282513804b6262d1fefc9b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 4, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

0.0.7

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page