Security
Security controls in convex-auth: 2FA, CAPTCHA, breach checks, rate limits, sessions, and keys.
convex-auth gives you several security controls out of the box. Most are configured in convex/auth.ts; the rest are environment and deployment practices.
Passwords
Breach detection
emailAndPassword.checkBreach checks new passwords against Have I Been Pwned and rejects known-breached ones.
export const auth = convexAuth({
component: components.convexAuth,
emailAndPassword: {
enabled: true,
checkBreach: true,
minPasswordLength: 8,
maxPasswordLength: 128,
},
});
Password reset
By default revokeSessionsOnPasswordReset is true. When a user resets their password, all active sessions are invalidated immediately.
Rate limiting
Configure sign-in attempt rate limits per email:
export const auth = convexAuth({
component: components.convexAuth,
emailAndPassword: {
enabled: true,
rateLimit: {
windowMs: 15 * 60 * 1000,
maxAttempts: 5,
},
},
});
CAPTCHA
Protect the sign-up and password-reset endpoints with a CAPTCHA provider:
export const auth = convexAuth({
component: components.convexAuth,
captcha: {
provider: "cloudflare-turnstile",
secretKey: process.env.TURNSTILE_SECRET_KEY!,
},
});
Supported providers: cloudflare-turnstile, google-recaptcha, hcaptcha, and captchafox. The default endpoints are /api/auth/sign-up/email, /api/auth/sign-up, and /api/auth/request-password-reset.
Two-factor authentication
TOTP is native. When 2FA is enabled, signIn returns a twoFactorChallengeToken. The client must verify the TOTP code or a backup code before a session token is issued.
const { data } = await client.signIn.email({ email, password });
// data.twoFactorChallengeToken is set if 2FA is enabled
await client.twoFactor.verifyTotp({ code, trustDevice: true });
Set trustDevice to skip 2FA on the current device for the configured trust window.
JWT and JWKS
Sessions are RS256 JWTs.
JWT_PRIVATE_KEY— the private JWK used to sign tokens. Never commit it.JWKS— the public JWKS used to verify tokens on every Convex request.- Use
convex env setfor both local and production. - Rotate keys by generating a new pair, updating
JWKS, and then updatingJWT_PRIVATE_KEY.
Trusted origins
All email links, OAuth callbacks, and verification redirects require an allowed origin. Configure the origins in the provider config:
export const auth = convexAuth({
component: components.convexAuth,
emailAndPassword: {
trustedOrigins: ["https://yourdomain.com", "https://app.yourdomain.com"],
},
oauth: {
trustedOrigins: ["https://yourdomain.com", "https://app.yourdomain.com"],
},
});
Session and refresh tokens
- Access tokens are short-lived (default 7 days).
- Refresh tokens are long-lived (default 30 days).
- Revoke sessions from the client with the session-list hooks.
- Password reset revokes all sessions by default.
- Revocation is enforced on every validation path — a revoked session’s token is rejected by session resolution, token verification, the bearer HTTP endpoints, OAuth account linking, and MCP authorization immediately, not just at expiry.
- Session listings never expose raw tokens;
listSessionsreturnsisCurrentinstead. - Revoking a passkey revokes every session and refresh token it created, including sessions minted after a two-factor challenge.
Security checklist
Use strong signing keys
Generate RS256 keys with jose and keep them in Convex env, not in the repo.
Enable breach checks
checkBreach: true for email/password.Add CAPTCHA
Rate-limit sign-in
Configure rateLimit for email/password and any custom actions.
Pin trusted origins
trustedOrigins for email, OAuth, and magic links.Use 2FA for admin accounts
Require TOTP for accounts with elevated privileges.
Rotate keys on suspicion
Regenerate JWT_PRIVATE_KEY and JWKS if a key leaks.