# Express route-protection discipline
## getAuth() returns, it does not reject
`getAuth(req)` gives you `{ isAuthenticated, userId }`. On a signed-out request `isAuthenticated` is false and `userId` is null. It never throws, never redirects, never sends a 401. Your handler must do all three decisions:
```ts
const { isAuthenticated, userId } = getAuth(req)
if (!isAuthenticated || !userId) {
res.status(401).json({ error: 'Unauthorized' })
return
}
```
If a route "works for signed-out users", the check is missing, not broken. Grep every route for the `isAuthenticated` guard; `clerkMiddleware()` alone protects nothing, it only attaches auth state.
## getUser() hits the network
`clerkClient.users.getUser(userId)` calls Clerk's Backend API on every invocation. In a hot route that means a network round trip per request plus rate-limit exposure.
Checklist:
- Only call it when you need fresh profile data. The session token already carries `userId` and basic claims; use those for identity checks.
- Cache the User object with a short TTL (tens of seconds) keyed by userId if a route needs profile fields repeatedly.
- For writes or security decisions, prefer a fresh fetch or the webhook-synced copy in your own DB over a long-cached profile.
## Cross-origin APIs
If your Express API serves a different origin than the frontend, the session arrives as a Bearer header, not a cookie. Verify with `authorizedParties` set to your frontend origins; Clerk's docs flag unset authorized parties as a CSRF risk.