Skip to main content

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.

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.