Skip to main content

magic_link

PyPI License: MIT CI Coverage

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.


Table of Contents

Features

  • 🔐 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 – MagicLinkService coordinates issuance, verification, rate limiting, and single-use guarantees.
  • 🛠️ CLI helpers – magic-link generate-config and magic-link test-email streamline 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.

Requirements

  • 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

Installation

Core library:

pip install magic-link

With 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]"

Quick Start

  1. Generate a configuration template:

    magic-link generate-config -o .env
    
  2. Fill in the environment variables (secret key, SMTP credentials, database connection, etc.).

  3. 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"})
    
  4. Verify tokens using service.verify_token(...) in your callback endpoint.

For a complete, copy-pasteable example (including Flask), consult the Quickstart guide and recipes.

Extras

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)

CLI Utilities

# Print an annotated configuration template
magic-link generate-config

# Send a test email using your configured mailer
magic-link test-email user@example.com

Documentation

Testing

pip install -e ".[dev,sqlalchemy,redis,smtp,cli]"
pytest --cov=magic_link

CI runs the full suite (unit, property-based, integration) against PostgreSQL, Redis, and a local SMTP server. Coverage must stay ≥95% (currently 100%).

Release Process

  1. Follow the checklist in docs/release.md (trusted publisher + semver workflow).
  2. Alpha / beta versions: bump pyproject.toml, update CHANGELOG.md, tag, and publish.
  3. Official releases are cut via GitHub Releases, which triggers the trusted publisher workflow to upload builds to PyPI.

Contributing

  • 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.

License

Distributed under the MIT License.

Metadata

Release files for magic-link 1.0.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 magic-link 1.0.0
File Size Uploaded
magic_link-1.0.0.tar.gz 24.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for magic-link 1.0.0
File Interpreter ABI Platform
magic_link-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 47.0 kB

Release history Release notifications | RSS feed

This release

1.0.0 This release

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