A modular, framework-agnostic engine for delivering passwordless authentication via secure magic links. The project follows a “minimal core + opt-in extras” philosophy so you can embed the library inside any Python stack without surrendering control of storage, email, or infrastructure.
Docs live in
/docs. This README highlights the essentials; detailed guides, recipes, and release instructions are all linked below.
- Features
- Requirements
- Installation
- Quick Start
- Extras
- CLI Utilities
- Documentation
- Testing
- Release Process
- Contributing
- License
- 🔐 Secure token engine – deterministic HMAC signing, hashing, TTL enforcement, property-based tests.
- 🗃️ Pluggable storage – in-memory for dev, SQLAlchemy for RDBMS, Redis for low-latency workloads.
- ✉️ Mailer registry – SMTP implementation plus extensible template hooks.
- 🎛️ Service facade –
MagicLinkServicecoordinates issuance, verification, rate limiting, and single-use guarantees. - 🛠️ CLI helpers –
magic-link generate-configandmagic-link test-emailstreamline setup and diagnostics. - 📚 Extensive documentation – quickstarts, framework recipes (FastAPI, Flask), and in-depth guides for email, migrations, and security.
- ✅ 100% test coverage – unit, integration, and property-based suites validated in CI against live PostgreSQL, Redis, and audo SMTP servers.
- Python 3.8+
- Optional services depending on extras:
- PostgreSQL (or any SQLAlchemy-supported database) for the SQL backend
- Redis 5+ for the Redis backend
- SMTP relay for outbound email
Core library:
pip install magic-linkWith common extras:
pip install "magic-link[sqlalchemy,redis,smtp]"Add cli if you want the command-line utilities bundled:
pip install "magic-link[sqlalchemy,redis,smtp,cli]"-
Generate a configuration template:
magic-link generate-config -o .env
-
Fill in the environment variables (secret key, SMTP credentials, database connection, etc.).
-
Wire the library into your framework. For FastAPI:
from fastapi import FastAPI, HTTPException from fastapi.responses import JSONResponse from magic_link import MagicLinkConfig, MagicLinkService from magic_link.interfaces import MagicLinkMessage from magic_link.mailer import create_mailer from magic_link.storage.redis import RedisStorage app = FastAPI() config = MagicLinkConfig.from_env() storage = RedisStorage.from_url("redis://localhost:6379/0") # see docs for helper service = MagicLinkService(config=config, storage=storage) mailer = create_mailer(config) @app.post("/auth/magic-link") async def issue(payload: dict[str, str]) -> JSONResponse: email = payload.get("email") if not email: raise HTTPException(status_code=400, detail="Email is required") service.enforce_rate_limit(email) issued = service.issue_token(email) link = f"{config.base_url}{config.login_path}?token={issued.token}" mailer.send_magic_link(MagicLinkMessage(recipient=email, link=link, subject="Sign in", expires_at=issued.expires_at)) return JSONResponse({"status": "sent"})
-
Verify tokens using
service.verify_token(...)in your callback endpoint.
For a complete, copy-pasteable example (including Flask), consult the Quickstart guide and recipes.
| Extra | Installs | Purpose |
|---|---|---|
sqlalchemy |
SQLAlchemy, psycopg[binary] |
Persistent token storage in relational DBs |
redis |
redis, hiredis |
High-throughput storage + rate limiting |
smtp |
email-validator |
SMTP mailer backend and email utilities |
cli |
click |
Command-line helpers (magic-link CLI) |
# Print an annotated configuration template
magic-link generate-config
# Send a test email using your configured mailer
magic-link test-email user@example.com- Overview & FastAPI quickstart
- Step-by-step Quickstart
- Framework recipes – Flask and more
- Guides – email templates, database migrations, security considerations
- Architecture
- Release process
pip install -e ".[dev,sqlalchemy,redis,smtp,cli]"
pytest --cov=magic_linkCI runs the full suite (unit, property-based, integration) against PostgreSQL, Redis, and a local SMTP server. Coverage must stay ≥95% (currently 100%).
- Follow the checklist in docs/release.md (trusted publisher + semver workflow).
- Alpha / beta versions: bump
pyproject.toml, updateCHANGELOG.md, tag, and publish. - Official releases are cut via GitHub Releases, which triggers the trusted publisher workflow to upload builds to PyPI.
- Open an issue or draft PR for discussion.
- Ensure tests and linters pass (
pytest,ruff check,black --check,mypy). - Update documentation for user-facing changes.
Distributed under the MIT License.