Skip to main content

Authentication failure

Symptoms​

401 or 403 on a route that worked in another tenant. auth.error.staleSession. Refresh fails. AI routes return ai.error.workspaceDisabled.

Likely causes​

SymptomCause in code
401 on a normal routeMissing access JWT, or not @PublicRoute
auth.error.staleSessionJWT tenantId no longer exists
403 on a permissioned route@RequirePermissions and the membership does not have every listed permission
Call succeeds without the permission you expectedThe handler has no @RequirePermissions. The guard allows it
Refresh 401Refresh route is public but JwtRefreshGuard still requires the refresh token
Warehouse 403x-warehouse-id is outside the membership’s warehouses
AI 403 ai.error.workspaceDisabledAI_WORKSPACE_ENABLED is not the string true
Login 400 with TurnstileSite and secret keys are set and the token is missing. Blank keys disable the check

How to investigate​

  • Decode the access token and compare tenantId with GET /v1/auth/my-tenants.
  • Read the controller decorators. Do not infer a deny from the module name.
  • Permission cache is 60 seconds. A role change can lag.

Passwords​

Unknown: which flows hash with argon2 and which use bcryptjs. Both libraries are dependencies.

Impersonation​

POST /v1/auth/impersonate issues a 30-minute token. The controller summary says Admin only. The exact @Roles or permission decorator was not re-read for this page. Check the handler before you assume a platform role.