Authentication Flows
ezAuth supports multiple authentication methods. All flows use the publishable key and produce JWT access tokens + refresh tokens.
How Sessions Work
Every successful authentication creates a session consisting of:
- Access token — an RS256 JWT valid for 15 minutes (configurable). Contains
sub(user ID),sid(session ID), andapp_id. - Refresh token — a long-lived opaque token valid for 30 days (configurable). Used to get a new access token after expiry.
Access tokens are delivered as an __session HttpOnly cookie (browser flows) or in the JSON response body (API flows). Refresh tokens are always in the response body.
JWT Verification
Access tokens can be verified offline using the public JWKS endpoint at /.well-known/jwks.json. See the Python Server SDK for an easy integration.
Email + Password
The most common flow. Users sign up with email and password, verify their email, then sign in directly.
Sign Up
- Client sends
POST /v1/signupswithemail,password, and optionalredirect_url - Server creates the user and sends a verification email (6-digit code or magic link, based on app config)
- User provides the code via
POST /v1/verify-codeor clicks the magic link - Server returns
access_token+refresh_token
Sign In
// Password sign-in (returns tokens immediately)
const session = await ez.auth.signIn({
email: '[email protected]',
password: 's3cret',
})
// session.access_token, session.refresh_token
Set strategy: "password" explicitly or provide a password field (the SDK auto-detects).
Magic Link
Passwordless authentication via email. No password needed at sign up or sign in.
Flow
- Client sends
POST /v1/signinswithemailandredirect_url(strategy defaults tomagic_link) - Server sends an email with a one-time link
- User clicks the link, which hits
GET /v1/email/verify?token=... - Server verifies the token, creates a session, sets the
__sessioncookie, and redirects toredirect_url
// Request magic link
await ez.auth.signIn({
email: '[email protected]',
redirectUrl: 'https://myapp.com/dashboard',
})
6-Digit Verification Code
An alternative to magic links for non-browser flows (mobile apps, CLIs). After signup or magic link sign-in, the server sends a 6-digit code instead of (or in addition to) a link.
// After signup or magic link request, verify the code
const session = await ez.auth.verifyCode({
email: '[email protected]',
code: '482917',
})
The app's verification_method setting controls whether code or link is sent.
Token Refresh
When an access token expires (401 response), exchange the refresh token for a new pair:
const newSession = await ez.auth.refreshToken(oldRefreshToken)
// newSession.access_token (fresh JWT)
// newSession.refresh_token (rotated — store this one)
Token Rotation
Each refresh issues a new refresh token and invalidates the old one. Always store the latest refresh token.
Sign Out
Revokes the current session server-side and clears the session cookie:
await ez.auth.signOut()
Get Current User
Returns the authenticated user's information:
const me = await ez.auth.getSession()
// { user_id, email, email_verified, is_bot }
Works with the __session cookie (browser) or an Authorization: Bearer <jwt> header.
Hashcash (Proof-of-Work)
When HASHCASH_ENABLED=true (default), sign-ups require a proof-of-work solution to deter bots and spam.
Flow
- Client requests a challenge:
POST /v1/challenges - Server returns a challenge string, difficulty, and Argon2 parameters
- Client finds a nonce such that
argon2id(challenge + nonce)has the required number of leading zero bits - Client includes the proof in the signup request
// The SDK handles this automatically in some clients (e.g. CLI)
// For manual use:
const challenge = await ez.auth.requestChallenge()
// Solve the challenge client-side, then:
await ez.auth.signUp({
email: '[email protected]',
hashcash: { challenge: challenge.challenge, nonce: solvedNonce },
})
Typical client-side solving takes 1-3 seconds. The server verifies with a single hash (instant).
Server-Side User Creation
With the secret key, you can create users directly without email verification:
// Backend only (secret key)
const user = await ez.users.create({
email: '[email protected]',
password: 'optional-password',
})
Sign-In Tokens (Server-to-Server)
Create short-lived tokens on behalf of a user for server-to-server auth flows:
const result = await ez.sessions.createSignInToken({
userId: 'user-uuid',
expiresInSeconds: 300, // 5 minutes (default)
})
// result.token (JWT), result.refresh_token, result.expires_at
Rate Limiting
All auth endpoints are rate limited per IP and per email:
| Endpoint | Per IP | Per Email |
|---|---|---|
| Sign Up | 10 per minute | 1 per 5 minutes |
| Sign In | 10 per minute | — |
Rate limits are configurable via environment variables. When exceeded, the API returns 429 Too Many Requests.