What’s included
The shape of every auth flow
Every sign-in method mints aSession and returns a token for the client to store. Authenticated requests then carry Authorization: Bearer <token>.
AuthContext is what flows into your policies, your TypeScript functions (as ctx.auth), and /api/auth/me.
Endpoints
All under/api/auth/:
Sessions vs cookies vs bearer tokens
Pylon supports both:- Bearer tokens — pass via
Authorization: Bearer <token>header. Used by every SDK, native clients, and curl. Tokens are 256-bit opaque strings prefixedpylon_. - Cookies — when configured (see Sessions), the same token can ride in an
HttpOnlycookie for browser apps. Set automatically on successful sign-in.
Session server-side. There is no JWT, no signing key to rotate, and no refresh-token exchange. The tokens are opaque and you can revoke them.
Auth context
What every authenticated request gets:Security guarantees
- Tokens are CSPRNG-generated with 256 bits of entropy, prefixed
pylon_. No JWT signing key to rotate. - Magic codes are numeric (6 digits by default, 4 to 8 with
PYLON_AUTH_CODE_LENGTH), expire after 10 minutes, burn after 5 wrong attempts, and use constant-time comparison. Each email and phone number also has a daily wrong-guess limit (429 CODE_LOCKED). - Magic-code create is throttled to 1 per email per minute.
- Passwords are hashed with Argon2id (the OWASP-recommended default).
- OAuth state is CSRF-protected via single-use tokens (10-minute expiry).
- Failed password logins run a dummy hash so timing doesn’t leak whether the email exists.
- Sessions can be persisted to SQLite so they survive restart (see Sessions).
/api/auth/sessionPOST is admin-gated in production — clients can’t forge sessions. In dev it is open only to requests from the same machine.
Configuration
Minimal env (most apps):PYLON_EMAIL_* provider, so one config covers both auth and your
app’s own ctx.email. To give auth its own sending identity (separate
key, separate from-address, separate sending domain), set the
PYLON_AUTH_EMAIL_* family. It takes precedence for auth email only:
PYLON_AUTH_EMAIL_* is set, app code’s ctx.email does not see
it. It reads only PYLON_EMAIL_*. That separation lets a host back auth
email with a shared, locked-down key (for example, on a dedicated sending
subdomain) without exposing it to app sends. Stack0 Cloud configures auth
email this way.
One-time codes (magic codes, email verification, phone):
PYLON_AUTH_CODE_LENGTH stops the server at boot. Shorter codes
get a lower daily wrong-guess limit per email or phone (10 for 4 digits,
1,000 for 6). See Code length and
SMS text.
Cookie auth (browser apps):
Where to go next
Sign-in methods
- Magic codes — the simplest sign-in flow; what most apps should use first
- Password — email + password with Argon2 hashing
- OAuth — Google + GitHub + 23 more built-in providers
- Passkeys — phishing-resistant FIDO2 / WebAuthn
- Phone / SMS — E.164 sign-in via Twilio (or any custom SmsSender)
- SIWE — Sign-In With Ethereum, EIP-4361
- SSO — per-org OIDC + SAML, members sign in through their IdP
Sessions + tokens
- Sessions — token lifetime, refresh, revoke, persistent storage, cookies
- API keys — long-lived keys for server-to-server calls
- JWT sessions — stateless
Bearer <jwt>mode for microservices
Account features
- RBAC — roles, the active tenant, multi-org apps
- Organizations — multi-tenant orgs, invites, member management
- TOTP / 2FA — RFC 6238 + backup codes
- Trusted devices — remember-this-browser cookie
- Email verification — confirm a user owns their email
- Account deletion — GDPR-style hard delete
Hardening + integrations
- CAPTCHA — gate sign-in endpoints with hCaptcha / Turnstile / reCAPTCHA
- Stripe billing — hosted Checkout + signed webhooks
- SCIM 2.0 — Okta / Azure AD user provisioning
- OIDC provider — Pylon as IdP for other systems