Embeddable user registration, login, and account management for FastAPI apps. SQLite / Postgres / MongoDB.
Project description
regstack
Production-grade user accounts for your FastAPI app — without the vendor lock-in, the second service to run, or the homegrown auth bugs.
pip install regstack, point it at SQLite (default), PostgreSQL,
or MongoDB, and you have register / login /
verify-email / reset-password / change-email / delete-account / Sign in
with Google / optional SMS two-factor / admin endpoints / themable
HTML pages — all behind a small Python API and one config file.
📚 Docs: https://regstack.readthedocs.io
·
🧪 Try it: examples/sqlite
·
🛡️ Security model: security guide
The problem regstack solves
Every web application that has users eventually needs the same dozen endpoints: register, log in, log out, verify email, reset a forgotten password, change password, change email, delete account, list users for the admin panel, lock out brute-force attackers, and ideally a second factor. Every one of those endpoints has a well-known way to get subtly wrong:
- Password hashing. Use Argon2id, not MD5, SHA-1, bcrypt-without-pepper, or — somehow still common — plain text.
- Token revocation. A JWT is signed and self-contained: the server can't "log it out" unless you build a revocation list. Forget this and a stolen token works until it expires.
- Account enumeration. A login or password-reset endpoint that responds differently for "user exists" vs "user doesn't" lets an attacker harvest your customer list.
- Bulk session invalidation. When a user changes their password because they think they were compromised, every existing token they hold should stop working immediately. Most homegrown JWT layers don't do this.
- One-time tokens. Verification and password-reset tokens should be random, hashed at rest, single-use, and expire fast. Storing the raw token in the database is a "now your DB backup is also a credential dump" mistake.
- Phone numbers. SMS codes need E.164-validated numbers, attempt limits, and an upstream provider. Wiring all of that yourself for a single feature is rarely worth it.
Doing all of these correctly, with tests, is two to four weeks of engineering for a competent team. Doing them once and embedding the result everywhere is what regstack is for.
What you get
✔ Email + password registration with email verification
✔ JWT login (RFC 7519) with per-token revoke AND bulk revoke
✔ Forgot / reset password — anti-enumeration: identical responses
✔ Change password (revokes old tokens) / change email (re-verify)
✔ Delete account
✔ Sign in with Google (PKCE + ID-token verification, opt-in)
✔ Optional SMS two-factor (TOTP-style 6-digit codes over SMS)
✔ Server-side login lockout (HTTP 429 + Retry-After)
✔ Admin endpoints (list / disable / delete users, stats)
✔ Server-rendered HTML pages, theme with one CSS file
✔ Pluggable email (console / SMTP / Amazon SES) and SMS (Amazon SNS / Twilio)
✔ Argon2 password hashing, CSP-friendly templates
✔ Setup wizards (live in their own pywebview windows): `regstack init` (project bootstrap), `regstack oauth setup` (guided Google OAuth client config), `regstack theme design` (live theme designer with preview), and `regstack doctor` (config validator)
✔ Three storage backends: SQLite, PostgreSQL, MongoDB — chosen by URL
Every feature is opt-in. Mount only the JSON router for a headless
backend; flip enable_ui_router to also get the bundled SSR pages.
Skip the SMS extras and you don't pull twilio or aioboto3.
Why not just use…?
There are real alternatives. Here's why regstack might still be the right call.
| Alternative | Why you might pick it | Why you might pick regstack instead |
|---|---|---|
| Auth0 / Clerk / WorkOS / Stytch (hosted SaaS) | Zero ops. Polished UI. Enterprise SSO out of the box. | Cost scales per-user. Your auth lives on someone else's servers. Your customer list is in their database. Vendor lock-in is real and migrations are painful. |
| Keycloak / Authentik / Authelia / Ory Kratos (self-hosted IAM) | Full identity platform. SAML, OIDC, federation. | A separate Java/Go service to run, monitor, back up, upgrade, and reason about. Heavyweight for "let users sign up". Schema lives outside your app. |
| fastapi-users | Same language, same framework. Good registration / login primitives. | Doesn't ship verification flows, anti-enumeration, bulk revoke, SMS MFA, admin endpoints, or themable pages — you build those. regstack is the longer tail. |
| Roll your own | Total control. No dependency to learn. | You re-solve every bullet from "The problem" above, including the ones you didn't know existed yet. Two to four engineering weeks, then forever to maintain. |
regstack's bet is that for most FastAPI apps the right answer is embed a small Python library that owns the boring 80% correctly, and keep the user table in your own database — not "stand up a separate auth product" and not "write the boring 80% from scratch each time".
30-second start
git clone https://github.com/jdrumgoole/regstack && cd regstack
uv sync --extra dev
# Generate a JWT signing secret. SQLite is the default backend, no DB to install.
export REGSTACK_JWT_SECRET=$(python -c 'import secrets; print(secrets.token_urlsafe(64))')
uv run uvicorn examples.sqlite.main:app --reload
Then visit http://localhost:8000/account/login in your browser, or register from the command line:
curl -X POST http://localhost:8000/api/auth/register \
-H 'content-type: application/json' \
-d '{"email":"alice@app.example.com","password":"<password>","full_name":"Alice"}'
The bundled example serves themed SSR pages at /account/*, prints
verification / reset links and SMS codes to stdout (the console
email/SMS backends), and shows how a host overrides regstack's default
look by serving its own theme.css.
Want PostgreSQL or MongoDB instead? Set REGSTACK_DATABASE_URL to a
postgresql+asyncpg://... or mongodb://... URL and install the matching
extra (uv sync --extra postgres or uv sync --extra mongo). The
schema is created on first boot.
Embed in your own app
from contextlib import asynccontextmanager
from fastapi import FastAPI
from regstack import RegStack, RegStackConfig
config = RegStackConfig.load() # env vars + regstack.toml
regstack = RegStack(config=config)
@asynccontextmanager
async def lifespan(app: FastAPI):
await regstack.install_schema() # idempotent: runs migrations / creates indexes
yield
await regstack.aclose()
app = FastAPI(lifespan=lifespan)
app.include_router(regstack.router, prefix=config.api_prefix)
app.include_router(regstack.ui_router, prefix=config.ui_prefix) # optional
app.mount(config.static_prefix, regstack.static_files) # optional
That is the whole integration. The rest of the surface area —
extending the user model, registering hooks (user_registered,
password_reset, …), supplying your own email service — is in the
embedding guide.
Documentation
| Page | What's there |
|---|---|
| Quickstart | Install, wizard, minimal embed |
| Configuration | Every RegStackConfig field, env vars, TOML layout |
| Architecture | Façade, backends, repos, hooks, lifecycle |
| Security model | Threat model, JWT scheme, anti-enumeration, MFA |
| Embedding | Custom backends, hooks, multi-tenant |
| Theming | CSS variables, template overrides |
| CLI | init, create-admin, doctor |
| API reference | Public types, generated from source |
The same docs are also browsable as Markdown in docs/.
Status
Alpha. Single-file SQLite is the default and runs with no infrastructure;
PostgreSQL and MongoDB backends pass the same parametrized integration
suite. OAuth (Google) shipped in v0.3.0; the regstack oauth setup
guided wizard in v0.4.0; the live regstack theme design tool in
v0.5.0. Latest tagged release: v0.5.0. See the
changelog
for the per-release breakdown.
Contributing
Issues and pull requests welcome at https://github.com/jdrumgoole/regstack. Before opening a PR, please run the test suite and the linter — both should be green:
uv sync --extra dev
uv run python -m invoke test-all # SQLite + Mongo + Postgres in parallel
uv run python -m invoke lint # ruff + format check + mypy
invoke test-sqlite is the fast inner-loop variant that needs no
database services. invoke test-all is what CI runs and what gates a
release. Each pytest-xdist worker isolates its own database, so the
full suite is safe to re-run while you iterate.
Security disclosures: see SECURITY.md.
License
Apache License 2.0 © 2026 Joe Drumgoole.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file regstack-0.5.0.tar.gz.
File metadata
- Download URL: regstack-0.5.0.tar.gz
- Upload date:
- Size: 445.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
35a4b2329cf4669638bdd63f18d9fb724ca730b00b918df8fe165e8ec2dac741
|
|
| MD5 |
50ac61e42cce842d89ae0726fd09c510
|
|
| BLAKE2b-256 |
6fd7495f37c758368699c3cda6671d843569cd7f2158b6b4235171eb391bfd10
|
Provenance
The following attestation bundles were made for regstack-0.5.0.tar.gz:
Publisher:
publish.yml on jdrumgoole/regstack
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
regstack-0.5.0.tar.gz -
Subject digest:
35a4b2329cf4669638bdd63f18d9fb724ca730b00b918df8fe165e8ec2dac741 - Sigstore transparency entry: 1429020977
- Sigstore integration time:
-
Permalink:
jdrumgoole/regstack@0dbb907d87a25c24527acaf5751a5401ae02adfc -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/jdrumgoole
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0dbb907d87a25c24527acaf5751a5401ae02adfc -
Trigger Event:
push
-
Statement type:
File details
Details for the file regstack-0.5.0-py3-none-any.whl.
File metadata
- Download URL: regstack-0.5.0-py3-none-any.whl
- Upload date:
- Size: 191.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab334a4f9a3742058f4237f8b338a0a90c2361624c0cc571e6e05f5838faa797
|
|
| MD5 |
915070a93ed5267af7ace2870f3a722c
|
|
| BLAKE2b-256 |
dd086d23e4bd49f2a85a4149d8fc121cf17db517c844b46bfd5cf4bb4efaf51f
|
Provenance
The following attestation bundles were made for regstack-0.5.0-py3-none-any.whl:
Publisher:
publish.yml on jdrumgoole/regstack
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
regstack-0.5.0-py3-none-any.whl -
Subject digest:
ab334a4f9a3742058f4237f8b338a0a90c2361624c0cc571e6e05f5838faa797 - Sigstore transparency entry: 1429020979
- Sigstore integration time:
-
Permalink:
jdrumgoole/regstack@0dbb907d87a25c24527acaf5751a5401ae02adfc -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/jdrumgoole
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0dbb907d87a25c24527acaf5751a5401ae02adfc -
Trigger Event:
push
-
Statement type: