Token management
-
Validate JWTs locally. Fetch JWKS from
/.well-known/jwks.json, cache it, and verify tokens in your backend to avoid a round trip on every request. - Refresh before expiry. Access tokens default to 15 minutes. Refresh about 30–60 seconds before expiry to avoid transient 401s.
-
Treat refresh tokens as single-use. Each
/refreshcall invalidates the previous refresh token. Always persist the latest pair. - Handle revocation. A 401 on a formerly valid token can mean password reset or admin revocation. Send the user back through login.
Security
-
Never ship API keys in client code. Proxy requests through your backend or serverless layer so only your servers add
X-API-Key. -
Protect refresh tokens. Prefer
httpOnlycookies on the web; use platform secure storage on mobile. Do not store refresh tokens inlocalStoragein the browser. -
Model the TOTP two-step flow. When
/loginreturnsrequires_totp: true, collect a TOTP code and call/login/totpbefore you have tokens. Login tickets expire in 5 minutes.
Performance
-
Cache JWKS with rotation in mind. Cache for 1–24 hours. On verification failure with an unknown
kid, refresh JWKS. -
Do not call
/meon every request. It hits the database. Prefer readingsub, roles, permissions, andemail_verifiedfrom the JWT. -
Use
last_login_methodfrom login responses. You can persist it on the client for UX hints without calling/me.
Error handling
- Treat 401 and 403 differently. 401 means bad or missing credentials; 403 means good credentials but policy or state blocks access.
-
Backoff on 429. Use the
RateLimitheader’sresetvalue to wait until the window resets. -
Avoid email enumeration.
/forgot-passwordalways returns 204. Login uses the same error for wrong email and wrong password.