Authentication
Rule
Scope: Projects using Clerk (@clerk/nextjs), WorkOS (@workos-inc/authkit-nextjs), or any auth provider. The security-engineer agent loads these rules when auth packages are detected.
General (All Providers)
- MUSTPerform auth checks server-side for all data access. Client-side auth is for UX gating only.
- MUSTInvalidate sessions server-side on logout — don't rely on cookie deletion alone.
- MUSTVerify webhook signatures before processing payloads. Use the provider's SDK verification, not manual HMAC.
- MUSTReturn 401 for unauthenticated requests and 403 for insufficient permissions. Never return 404 to hide resources from unauthorized users without explicit reason.
- MUSTEnforce RBAC at the data layer (queries/mutations), not just at route or middleware level. Middleware can be bypassed; data-layer checks cannot.
- SHOULDRate-limit auth endpoints (login, signup, password reset, token refresh).
- SHOULDRotate refresh tokens on each use (one-time use tokens).
- SHOULDInclude
userIdin cache keys for any user-specific cached data. - NEVERTrust auth state from client components for data decisions. Always re-verify server-side.
Clerk
- MUST
await auth()— it's async in Next.js 15+ (App Router). Forgettingawaitreturns a pending Promise that's always truthy. - MUSTUse
auth.protect()in Server Actions, not justauth().protect()throws on failure;auth()returns null. - MUSTKeep webhook routes public in Clerk middleware matcher. Webhook endpoints must not require auth.
- MUSTHandle all user lifecycle webhook events (
user.created,user.updated,user.deleted) when syncing to a database. - MUSTUse
pk_test_*/sk_test_*keys in development. Production keys in dev environments create real user records. - SHOULDConfigure proxy matcher to protect routes explicitly. Default
publicRoutesapproach is error-prone — prefermatcherconfig. (Note: Next.js renamedmiddleware.tstoproxy.ts.) - SHOULDUse
setupClerkTestingToken()in Playwright tests for fast auth without UI login. - NEVERExpose
CLERK_SECRET_KEYin client bundles. It starts withsk_— any env withoutNEXT_PUBLIC_prefix is safe, but verify bundler config. - NEVERUse
useAuth()for page-load access control. It runs client-side after render — useauth()in Server Components or middleware instead.
WorkOS
- MUSTWrap root layout with
AuthKitProvider. Missing provider causes silent auth failures — no error, just no auth. - MUSTAdd
authkitMiddleware()toproxy.ts(formerlymiddleware.ts). Without it, auth state is never populated. It fails silently. - MUSTSet
WORKOS_COOKIE_PASSWORDto 32+ characters. Shorter values cause cryptic runtime errors. - MUSTEnsure
WORKOS_REDIRECT_URImatches the redirect URI configured in the WorkOS dashboard exactly, including trailing slashes. - MUSTUse the correct env prefix for your build tool:
NEXT_PUBLIC_for Next.js,VITE_for Vite,REACT_APP_for CRA. - SHOULDUse
await createClient()— it's async. Missingawaitreturns a Promise, not a client. - SHOULDUse
withAuth()for page-level protection instead of manual redirect logic. - NEVERUse
authLoaderfor fetching user data. It's only for handling OAuth callbacks.
Further Reading
For complete patterns, code examples, and anti-patterns, see references/authentication.md.