Guide: Magic links¶
Passwordless sign-in via email. See examples/magic_link/.
Install¶
.env¶
SECRET=... # `fastauth generate-secret`
DATABASE_URL=sqlite+aiosqlite:///./magic_links.db
SMTP_HOST=...
SMTP_PORT=587
SMTP_USER=...
SMTP_PASS=...
SMTP_FROM=noreply@example.com
BASE_URL=http://localhost:8000
main.py¶
import os
from contextlib import asynccontextmanager
from dotenv import load_dotenv
from fastapi import FastAPI
from fastauth import FastAuth, FastAuthConfig
from fastauth.adapters.sqlalchemy import SQLAlchemyAdapter
from fastauth.email_transports.smtp import SMTPTransport
from fastauth.providers.magic_links import MagicLinksProvider
load_dotenv(".env")
adapter = SQLAlchemyAdapter(engine_url=os.environ.get("DATABASE_URL", "sqlite+aiosqlite:///./magic_links.db"))
email_transport = SMTPTransport(
host=os.environ["SMTP_HOST"],
port=int(os.environ.get("SMTP_PORT", "587")),
username=os.environ["SMTP_USER"],
password=os.environ["SMTP_PASS"],
from_email=os.environ["SMTP_FROM"],
use_tls=False, # disable for Mailpit / dev SMTP
)
config = FastAuthConfig(
secret=os.environ["SECRET"],
providers=[MagicLinksProvider()],
adapter=adapter.user,
token_adapter=adapter.token,
email_transport=email_transport,
base_url=os.environ.get("BASE_URL", "http://localhost:8000"),
)
auth = FastAuth(config)
@asynccontextmanager
async def lifespan(app: FastAPI):
await adapter.create_tables()
yield
app = FastAPI(lifespan=lifespan)
auth.mount(app)
Try it¶
uvicorn main:app --reload
curl -X POST http://localhost:8000/auth/magic-links/login \
-H "Content-Type: application/json" \
-d '{"email":"a@b.com"}'
# -> {"message":"Magic link sent — check your email"}
# If unknown, a user was auto-created. Click the link in the email (or via Mailpit):
# http://localhost:8000/auth/magic-links/callback?token=...
# (cookie mode sets the cookies; JSON mode returns a token pair)
Local dev with Mailpit¶
mailpit is a great local SMTP catcher:
mailpit # listens on :1025 (SMTP) and :8025 (web UI)
# In .env use:
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_USER= # empty
SMTP_PASS= # empty
SMTP_FROM=noreply@localhost
View the email at http://localhost:8025. Set use_tls=False.
Notes¶
- Magic links auto-register unknown emails with
hashed_password=None. Gate viaallow_signin(user, "magic_link")if account creation should be restricted. token_adapteris required —MagicLinksProvider.send_login_requestraisesConfigErrorwithout it.on_signinis JSON-mode only. See Hooks.