Email Code
Email Code sends a short-lived verification code to the user’s email address. The current browser flow is implemented through Direct Auth and the Web SDK.
Overview
Section titled “Overview”sequenceDiagram
participant User
participant App
participant Authrim
participant Email as Email Notifier
User->>App: Enter email address
App->>Authrim: Send email code request with PKCE challenge
Authrim->>Email: Send verification code
Email->>User: Deliver code
User->>App: Enter code
App->>Authrim: Verify code with PKCE verifier
Authrim->>App: Return Direct Auth artifact
App->>Authrim: Exchange artifact for tokens/session
Features
Section titled “Features”- Passwordless: No password to remember or store
- Direct Auth support: Browser clients can authenticate without a hosted redirect
- PKCE-bound verification: The send and verify steps are bound by a
S256challenge - Single-use codes: Successful verification consumes the server-side challenge
- User creation support: A new end user can be created from the verified email address
- Invitation support:
invite_tokencan route signup to the invitation tenant and assignments
Configuration
Section titled “Configuration”Email Code delivery uses a notifier plugin with the notifier.email capability. Configure a built-in email notifier such as Cloudflare Email or Resend, or provide a custom notifier plugin.
# Sender metadata used by email notifier pluginsEMAIL_FROM=noreply@example.comEMAIL_FROM_NAME=Authrim
# Optional HMAC secret used when hashing email codesOTP_HMAC_SECRET=replace-with-random-secretThe Direct Auth implementation currently uses a 6-digit code with a 5-minute TTL. Send requests are rate limited per tenant and email address, and verify attempts are limited per code attempt.
API Usage
Section titled “API Usage”Step 1: Send Email Code
Section titled “Step 1: Send Email Code”POST /api/v1/auth/direct/email-code/sendContent-Type: application/json
{ "client_id": "your-client-id", "email": "user@example.com", "code_challenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", "code_challenge_method": "S256", "channel": "browser", "scope": "openid profile email", "locale": "en"}Optional request fields include invite_token and custom_fields for invitation-based signup and registration fields.
Response:
{ "attempt_id": "7d6ad739-0d19-4e90-b352-8e8b6d7d81ac", "expires_in": 300, "masked_email": "u***r@example.com"}Step 2: Verify Email Code
Section titled “Step 2: Verify Email Code”POST /api/v1/auth/direct/email-code/verifyContent-Type: application/json
{ "attempt_id": "7d6ad739-0d19-4e90-b352-8e8b6d7d81ac", "code": "123456", "code_verifier": "original-pkce-code-verifier", "channel": "browser"}Response:
{ "direct_auth_artifact": "daa_...", "expires_in": 300, "is_new_user": false}The Direct Auth artifact is an intermediate credential. The Web SDK exchanges it through the Direct Auth token exchange and returns the canonical session, user, and token payload.
Legacy Email Code Endpoints
Section titled “Legacy Email Code Endpoints”Authrim also exposes /api/auth/email-codes/send and /api/auth/email-codes/verify for hosted UI and legacy integrations. New browser applications should prefer the Direct Auth endpoints or the Web SDK.
Web SDK Usage
Section titled “Web SDK Usage”import { createAuthrim } from '@authrim/web';
const authrim = createAuthrim({ issuer: 'https://auth.example.com', clientId: 'your-client-id',});
const sendResult = await authrim.emailCode.send('user@example.com', { locale: 'en',});
if (sendResult.ok) { console.log(sendResult.data.maskedEmail);}
const verifyResult = await authrim.emailCode.verify('user@example.com', '123456');
if (verifyResult.ok) { console.log(verifyResult.data.session);}The SDK stores the pending attemptId and PKCE verifier in memory, exposes hasPendingVerification() and getRemainingTime(), and clears the pending state on success or expiry.
Security Considerations
Section titled “Security Considerations”Code Handling
Section titled “Code Handling”- Codes are generated server-side and are not returned in API responses.
- Code validation uses an HMAC-backed hash with tenant/email/attempt context.
- Challenge state is stored server-side and consumed during verification.
- Email addresses are normalized for lookup and routing before verification.
Rate Limiting
Section titled “Rate Limiting”| Limit | Current behavior |
|---|---|
| Send requests | 3 requests per 15 minutes per tenant/email |
| Verify attempts | 5 attempts per code attempt within the code TTL |
| Code TTL | 5 minutes |
| Code format | 6 numeric digits |
Abuse Prevention
Section titled “Abuse Prevention”- Require
code_challenge_method: "S256"for Direct Auth sends. - Verify requests must include the matching
code_verifier. - The
channelsubmitted during verification must match the original send request. - Configure a real email notifier before enabling Email Code in production.
- Set
OTP_HMAC_SECRETto a high-entropy secret instead of relying on fallback values.
Email Templates
Section titled “Email Templates”The built-in Direct Auth email uses the configured email notifier and sender metadata. For production branding, provide a custom email notifier plugin or configure the active notifier to render your preferred template.
Comparison with Other Methods
Section titled “Comparison with Other Methods”| Method | Security | Usability | Requirements |
|---|---|---|---|
| Email Code | High | High | Email access |
| Password | Medium | Medium | Password storage and reset UX |
| TOTP | High | Medium | Authenticator app |
| Passkey | Very High | High | Compatible device |
| SMS OTP | Medium | High | Phone number |
Troubleshooting
Section titled “Troubleshooting”Code not received:
- Confirm an email notifier plugin is installed and active
- Check
EMAIL_FROMand provider-specific settings - Check provider delivery logs and spam folders
- Verify rate limits have not been exceeded
Code expired or invalid:
- Request a new code after expiry
- Ensure the same browser session verifies the code so the SDK still has the PKCE verifier
- Confirm the user entered the latest 6-digit code
Too many attempts:
- Ask the user to request a new code
- Review audit and diagnostic logs for repeated failures