Anti-patterns
Things people try with IdentSphere that they shouldn't. Read this before shipping.
1. Don't reuse IDENTSPHERE_JWT_SECRET across environments
Each deployment (dev / staging / prod / per-customer) gets its own secret. Reusing the secret means a leaked staging credential is also a prod credential.
- IDENTSPHERE_JWT_SECRET=hardcoded-shared-secret
+ IDENTSPHERE_JWT_SECRET=$(openssl rand -hex 32) # per-env
2. Don't ship cookies_secure = false in production
cookies_secure = false means session cookies travel over plaintext HTTP.
This defeats the entire transport-security model. The only acceptable
non-production place for this is localhost.
3. Don't use HS256 + a short jwt_secret
The default is HS256 + a 32-byte secret. If you reduce the secret length, the JWT signatures are guessable. Stick to ≥32 bytes from a CSPRNG.
For multi-instance deployments that want public-key verification, switch to
RS256 (configurable in AuthServiceConfig) and publish the JWKS.
4. Don't paste user PII into the audit metadata field
The metadata JSONB column outlives the user record (no foreign keys).
Storing plaintext email or display names there is GDPR-unfriendly. Use
opaque identifiers (user_id) and join at query time.
5. Don't expose /v1/auth/invitations/accept behind the auth middleware
The accept endpoint must be reachable BEFORE the caller has a session
(otherwise a new user can't accept their first invitation). Mount it via
routes::invitations::router_public() OUTSIDE your auth middleware.
The SDK enforces this with a runtime check, but the cleaner fix is correct wiring at startup.
6. Don't roll your own CSRF check on top of the SDK's
The SDK handles CSRF for cookie auth via the identsphere_csrf double-submit
pattern. Adding a separate X-CSRF flow on top creates confusion and
double-counts the check. Trust the SDK's middleware.
7. Don't query users.role directly for authorization
users.role is the user's home-org default role. Authoritative
permissions live in organization_memberships. The SDK reads from there;
your code should too.
8. Don't issue API keys with * scopes by default
API keys can carry arbitrary scopes. Issuing keys with ["*"] defeats the
scoping model. Define the minimum set of scopes per use case and grant
only those.
9. Don't store recovery codes anywhere on the server after issuance
The SDK stores SHA-256 hashes only. The response containing the plaintext codes is the user's single chance to save them. Don't log it, don't email it, don't cache it.
10. Don't disable MFA programmatically without password re-confirmation
The disable endpoint requires the user's current password — for a reason. Allowing an already-authenticated session to flip MFA off would mean a hijacked session could permanently downgrade the account's security posture.
11. Don't use cookie auth + Bearer auth on the same request
The middleware prefers Authorization: Bearer when both are present. The
behavior is well-defined but the intent isn't. Pick one transport per
client.
12. Don't share access tokens between server-side workers
Access tokens are bearer credentials. If your background worker needs to act on a user's behalf, mint an API key for the worker (scoped to the operations it actually performs), don't reuse the user's browser session.
13. Don't rely on useSession() for authorization checks
useSession() returns the user's capabilities, but treating it as the
authoritative permission gate is wrong:
- The capabilities are a UX hint.
- The server still enforces every permission on the actual request.
- A client that bypasses your
if (caps.includes(...))check still hits the server-side 403.
Use the React-side check for UX (hide buttons the user can't use) and trust the server.
14. Don't add columns to IdentSphere tables
The schema is the SDK's contract. Adding your own columns to users or
organizations may break across migrations. Put your fields in your own
tables and foreign-key into the SDK schema:
CREATE TABLE my_app.user_profile_extras (
user_id UUID PRIMARY KEY REFERENCES IdentSphere.users(id) ON DELETE CASCADE,
favorite_color TEXT
);
15. Don't disable the body-limit on avatar uploads
The 5 MiB request-body limit is there for a reason (memory pressure, DoS defense). Lifting it allows a malicious user to OOM your server with a multi-GB upload.