Browse Apps

AA Skip Email

Allows you to skip entering your email after registering via EVE SSO


AA Skip Email (aa_skip_email)

A small Alliance Auth plugin that lets you skip collecting real email addresses during EVE SSO registration by automatically generating a placeholder User.email. This is useful for integrations (e.g., OIDC clients) that expect email to be present.

<hr />

Features

  • Creates a placeholder email for newly created users during SSO registration.
  • Includes a one-off Django management command to backfill missing emails.
  • Includes a Celery task suitable for periodic execution to keep data consistent.
  • Emits a placeholder_email_replaced signal when a user's placeholder is replaced with a real address — downstream plugins (OIDC providers, audit logs, welcome flows) can react without polling.
  • Exposes is_placeholder_email() and get_placeholder_users() helpers so callers can classify addresses without re-implementing the domain check.
  • Ships a PlaceholderEmailFilter for the Django admin user list.
  • Translated user-visible strings (admin filter, command output, AppConfig name) — currently en source + ru catalogue.
<hr />

Requirements

  • Python >= 3.12 (tested on 3.12 and 3.13)
  • Alliance Auth >= 4.0, < 6 (tested against the latest AA 4.x and AA 5.x)
<hr />

Installation

Install the plugin into the same Python environment as Alliance Auth (pip/uv/poetry-whatever you use).

pip install aa-skip-email

Add the app to INSTALLED_APPS (typically in local.py):

INSTALLED_APPS += [
    ...
    "aa_skip_email",
    ...
]

Add the authentication backend to AUTHENTICATION_BACKENDS.

[!IMPORTANT] This plugin replaces Alliance Auth’s default allianceauth.authentication.backends.StateBackend behavior. Update AUTHENTICATION_BACKENDS so that SkipEmailBackend is used instead of the original AA StateBackend.

Example:

AUTHENTICATION_BACKENDS = [
    # Replace the original AA StateBackend with this plugin backend
    "aa_skip_email.authentication.backends.SkipEmailBackend",
    # Keep the rest of your original backends
    "django.contrib.auth.backends.ModelBackend",
]
<hr />

Configuration

Configure these in local.py (or your equivalent settings file).

AA_SKIP_EMAIL_DOMAIN

Domain used for placeholder addresses.

  • Type: str
  • Default: no-email.invalid
AA_SKIP_EMAIL_DOMAIN = "no-email.invalid"

Using a domain under .invalid helps avoid accidentally generating addresses at a real domain.

The plugin validates this setting at process start and raises ImproperlyConfigured if the value is empty, contains @ or whitespace, or has no . (single-label "domain"). This catches misconfigurations before the first SSO registration attempt.

AA_SKIP_EMAIL_LIMIT

Default limit for how many users are processed per run by the management command and the Celery task.

  • Type: int
  • Default: 5000
AA_SKIP_EMAIL_LIMIT = 5000
<hr />

How placeholder emails are generated

A placeholder email is generated from the user’s username (sanitized to a conservative character set) and a unique suffix (typically the user id; otherwise a UUID). The local-part is kept within 64 characters.

Resulting format is similar to:

<sanitized-username>-<id-or-uuid>@<AA_SKIP_EMAIL_DOMAIN>
<hr />

One-off backfill (management command)

The plugin provides a Django management command to fill missing emails.

Fill missing emails (default)

python manage.py fill_missing_emails

Limit number of users

python manage.py fill_missing_emails --limit 5000

Dry-run (no writes)

python manage.py fill_missing_emails --dry-run --limit 50

Overwrite emails for all users

[!CAUTION] This will replace existing emails.

python manage.py fill_missing_emails --overwrite

Overwrite only existing placeholders

Safer if some users have real emails and you only want to regenerate placeholders.

python manage.py fill_missing_emails --overwrite --only-placeholders
<hr />

Scheduler (Celery Beat)

The plugin includes a Celery task named:

  • aa_skip_email.fill_missing_emails

To run it periodically, add a schedule entry in local.py using CELERYBEAT_SCHEDULE.

Example: run every 6 hours

from celery.schedules import crontab

CELERYBEAT_SCHEDULE["aa_skip_email_fill_missing_emails"] = {
    "task": "aa_skip_email.fill_missing_emails",
    "schedule": crontab(minute=0, hour="*/6"),
    # Optional: helps spread load across instances in some AA deployments
    "apply_offset": True,
}

Example: run daily at 04:15

from celery.schedules import crontab

CELERYBEAT_SCHEDULE["aa_skip_email_fill_missing_emails"] = {
    "task": "aa_skip_email.fill_missing_emails",
    "schedule": crontab(minute=15, hour=4),
    "apply_offset": True,
}
<hr />

Public API for downstream plugins

is_placeholder_email(email)

from aa_skip_email import is_placeholder_email

if is_placeholder_email(user.email):
    ...  # treat as unverified / non-routable

Centralised classifier — returns True for any address produced by this plugin (case-insensitive domain match, anchored to the domain part). Falsy values (None, "") return False.

get_placeholder_users()

from aa_skip_email.helpers import get_placeholder_users

count = get_placeholder_users().count()

ORM-side analogue of is_placeholder_email. Returns a queryset of all users whose email is a plugin-generated placeholder. Useful for admin reports, audit dashboards, or batch follow-up.

Admin filter

Add PlaceholderEmailFilter to AA's existing UserAdmin.list_filter to surface a "Placeholder / Real / Blank" sidebar in the admin user list. The plugin does not re-register the User model so AA's custom UserAdmin remains intact:

# in your local.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from aa_skip_email.admin import PlaceholderEmailFilter

User = get_user_model()
UserAdmin = admin.site._registry[User].__class__
UserAdmin.list_filter = (*UserAdmin.list_filter, PlaceholderEmailFilter)

Signal placeholder_email_replaced

Sent when User.email transitions from a plugin-generated placeholder to a real address. Useful for OIDC providers that flip email_verified=true on the ID token, audit logs, welcome emails, etc.

from django.dispatch import receiver
from aa_skip_email.signals import placeholder_email_replaced

@receiver(placeholder_email_replaced, dispatch_uid="my_app.email_verified")
def on_email_replaced(sender, user, old_email, new_email, **kwargs):
    ...

The signal does not fire on:

  • Initial fill of a blank/NULL email (placeholder is the new value).
  • Placeholder rewritten as another placeholder (--overwrite --only-placeholders).
  • Real email replaced with another real email.
<hr />

See also

No reviews yet...

Python Requirements

  • allianceauth<6,>=4.0

Required Python packages to be installed and other Python requirement.

App Dependencies


Required apps:

    -


Used by apps:

    -

Dependencies to other apps registered in this app directory.

Classifiers

  • Development Status :: 4 - Beta
  • Environment :: Web Environment
  • Framework :: Django
  • Framework :: Django :: 4.2
  • Framework :: Django :: 5.2
  • Intended Audience :: Developers
  • License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
  • Operating System :: OS Independent
  • Programming Language :: Python :: 3 :: Only
  • Programming Language :: Python :: 3.12
  • Programming Language :: Python :: 3.13
  • Topic :: Internet :: WWW/HTTP
  • Topic :: Internet :: WWW/HTTP :: Site Management

Version

0.4.0

License

GPLv3+

Homepage

  https://gitlab.com/zima-corp/aa-skip-email

PyPI

  https://pypi.org/project/aa-skip-email/

Last updated

1 day, 19 hours ago

First published

2 years, 1 month ago

Category

Plugin Apps

Rating

-

Authors

Boris Talovikov

Maintainers

Boris Blade Artrald Boris Blade Artrald

Please login to see more options.