Metadata-Version: 2.4
Name: stapel-auth
Version: 0.23.0
Summary: Full-featured authentication Django app for the Stapel framework
License: MIT
Project-URL: Homepage, https://github.com/usestapel/stapel-auth
Project-URL: Repository, https://github.com/usestapel/stapel-auth
Project-URL: Documentation, https://github.com/usestapel/stapel-auth#readme
Project-URL: Changelog, https://github.com/usestapel/stapel-auth/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/usestapel/stapel-auth/issues
Keywords: django,stapel,authentication,jwt,passkey,totp,sso
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Django
Classifier: Framework :: Django :: 5.2
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: stapel-core<1.0,>=0.28.0
Requires-Dist: pyotp>=2.10
Requires-Dist: webauthn>=2.8
Requires-Dist: phonenumbers>=8.13
Provides-Extra: phone
Requires-Dist: twilio>=9.0; extra == "phone"
Provides-Extra: oauth
Requires-Dist: social-auth-app-django>=5.4; extra == "oauth"
Requires-Dist: python3-openid>=3.2; extra == "oauth"
Provides-Extra: saml
Requires-Dist: lxml>=5.0; extra == "saml"
Requires-Dist: signxml>=3.2; extra == "saml"
Provides-Extra: all
Requires-Dist: stapel-auth[oauth,phone,saml]; extra == "all"
Dynamic: license-file

<!-- Generated by stapel-readme from docs/readme.md + docs/*.json. Do not edit this file; edit docs/readme.md and re-run `make readme`. -->

# stapel-auth

[![CI](https://img.shields.io/github/actions/workflow/status/usestapel/stapel-auth/ci.yml?branch=main&logo=github&label=CI)](https://github.com/usestapel/stapel-auth/actions/workflows/ci.yml?query=branch%3Amain)
[![coverage](https://img.shields.io/codecov/c/github/usestapel/stapel-auth?branch=main&logo=codecov&label=coverage)](https://app.codecov.io/gh/usestapel/stapel-auth)
[![pypi](https://img.shields.io/pypi/v/stapel-auth?logo=pypi&logoColor=white&label=pypi)](https://pypi.org/project/stapel-auth/)
[![downloads](https://static.pepy.tech/badge/stapel-auth/month)](https://pepy.tech/project/stapel-auth)
[![python](https://img.shields.io/pypi/pyversions/stapel-auth?logo=python&logoColor=white)](https://pypi.org/project/stapel-auth/)
[![license](https://img.shields.io/github/license/usestapel/stapel-auth)](https://github.com/usestapel/stapel-auth/blob/main/LICENSE)
[![llms.txt](https://img.shields.io/badge/llms.txt-blue)](https://github.com/usestapel/stapel-auth/blob/main/docs/llms.txt)

> User authentication and account access: sign-up and sign-in with email or phone one-time codes, passwords, social (OAuth) accounts, corporate SSO (SAML/OIDC), magic links, QR hand-off and passkeys; guest (anonymous) access; two-factor authentication and step-up verification; session and device management with a security audit trail.

Part of the [Stapel framework](https://github.com/usestapel) — composable Django apps that deploy as a monolith or as microservices without changing module code.

## Install

```bash
pip install stapel-auth
```

## At a glance

| Fact | Value |
|---|---|
| Version | `0.23.0` |
| Python | `>=3.11` (3.11, 3.12, 3.13, 3.14) |
| HTTP operations | 111 |
| Config axes | 29 |
| Usage surface | 18 |
| Extension points | 6 |
| Error codes | 132 |
| Documented flows | 4 |
| Fleet dependencies | [`stapel-core`](https://github.com/usestapel/stapel-core) · [`stapel-gdpr`](https://github.com/usestapel/stapel-gdpr) (optional) · [`stapel-notifications`](https://github.com/usestapel/stapel-notifications) (optional) |

## Documentation

**Flows:** [English](https://github.com/usestapel/stapel-auth/blob/main/docs/flows/en/README.md) · [Русский](https://github.com/usestapel/stapel-auth/blob/main/docs/flows/ru/README.md) · **Errors:** [English](https://github.com/usestapel/stapel-auth/blob/main/docs/errors.en.md) · [Español](https://github.com/usestapel/stapel-auth/blob/main/docs/errors.es.md) · [Русский](https://github.com/usestapel/stapel-auth/blob/main/docs/errors.ru.md) · [OpenAPI](https://github.com/usestapel/stapel-auth/blob/main/docs/schema.json) · [capabilities.json](https://github.com/usestapel/stapel-auth/blob/main/docs/capabilities.json) · [llms.txt (for agents)](https://github.com/usestapel/stapel-auth/blob/main/docs/llms.txt)

## What this is

Authentication is the part of a product that everyone builds and nobody wants
to own: a dozen sign-in methods, each with its own rate limits, lockouts,
notification rules and recovery paths, plus the security surface underneath
them — sessions, devices, audit, step-up. `stapel-auth` is that whole area as
one installable Django app, with every method behind a settings flag so a
product ships only the ones it wants.

The shape to keep in mind: **sign-in methods are axes, not forks.** Email OTP,
phone OTP, password (+ TOTP), OAuth, enterprise SSO, magic link, QR hand-off,
passkeys and guest access are each one `AUTH_*` flag. Turning a flag off
unmounts its endpoints *and* removes it from the capabilities response the
frontend reads — so the login screen changes with the setting, not with a
frontend release. `GET /capabilities/` is the contract for that: availability,
placement, interaction and icon per method, plus OTP code length, TTL and
resend cooldown, so no client hardcodes a number this module owns.

## Quick start

```python
# settings.py
INSTALLED_APPS = [
    # ...
    "stapel_auth",
]

STAPEL_AUTH = {
    "AUTH_EMAIL": True,          # email OTP sign-in
    "AUTH_PASSWORD_LOGIN": True,  # password (+ TOTP step-up when enrolled)
    "AUTH_ANONYMOUS": False,      # no guest accounts
}
```

```python
# urls.py
path("auth/", include("stapel_auth.urls")),
```

```bash
python manage.py migrate
```

Every configuration axis, its default and the operations it gates are listed
in [`docs/capabilities.json`](https://github.com/usestapel/stapel-auth/blob/main/docs/capabilities.json) — the same document the
table above is generated from, and the one an agent reads before writing code
against this module.

## Step-up verification

Any endpoint in any module can demand a fresh proof of identity by decorating
itself with `@requires_verification` (from `stapel_core.verification`). This
module registers the factors that satisfy it — `otp_email`, `otp_phone`,
`totp`, `passkey` — and hosts the challenge endpoints.

The factors are interchangeable by design: a challenge names a scope and the
factors currently available to that user, and *any* of them closes it. A
client implements the cycle once (403 with a challenge envelope → pick a
factor → initiate → complete → repeat the original request) and reuses it for
every protected endpoint in the product, forever. The reference walkthrough is
the `auth.step_up_verification` flow.

## Sessions, devices and recovery

Sessions are JWT (cookie plus a token pair) with a tracked `UserSession` per
device, so "sign out everywhere" and "revoke this device" are real operations
rather than a token TTL. Suspicious sessions (new device, unexpected IP) are
detected, notified and revocable from the notification itself.

Authenticator changes — email, phone or TOTP — run through one model and one
set of tasks, in two speeds: instant, when the user can prove control of the
current authenticator, and delayed, when they cannot. The delayed path is the
one that matters after a lost phone: it notifies the verified contact on day
1, 7 and 13 and completes on day 14, which gives an attacker who has the inbox
but not the device two weeks of loud warnings and the real owner two weeks to
cancel.

## Enterprise SSO

SAML SP and OIDC RP, configured per organization in the database rather than
in settings — a tenant onboards without a deploy. Users provisioned by an org
admin land in the `auth.first_login` flow: the first password login returns a
short-lived challenge instead of a session, routing to a forced password change
and/or MFA enrolment before anything else is reachable.

## Bus events

Emitted through `stapel_core.comm` (transactional outbox — the event leaves if
and only if your transaction commits):

| Event | Payload | When |
|---|---|---|
| `user.session_created` | [schema](https://github.com/usestapel/stapel-auth/blob/main/schemas/emits/user.session_created.json) | A user authenticated and a session was created |
| `user.session_revoked` | [schema](https://github.com/usestapel/stapel-auth/blob/main/schemas/emits/user.session_revoked.json) | A session was revoked (logout or admin action) |

## Extension points

Providers, models and policies are replaced by dotted path, never by fork —
additional OAuth providers, a custom re-registration model, serializer and
permission seams. [`MODULE.md`](https://github.com/usestapel/stapel-auth/blob/main/MODULE.md) is the full agent-facing map;
`docs/capabilities.json` carries the machine-readable list.

## Development

```bash
pip install -e . && pip install pytest pytest-django pytest-cov ruff
./setup-hooks.sh
pytest tests/
```

## License

MIT — see [LICENSE](https://github.com/usestapel/stapel-auth/blob/main/LICENSE).

---

<sub>This page is assembled by `stapel-readme` from `docs/readme.md` plus the contract artifacts in `docs/`. Edit the prose in `docs/readme.md`; the badges, facts and links above and below it are generated — do not hand-edit `README.md`.</sub>
