Alpha. The user-mode auth gate: requires a valid user JWT and contributes non-null ctx.jwtClaims. Verification runs against the project JWKS, the same core withSupabase uses for its user auth mode.
This is the required-caller counterpart to withClaims, which contributes claims when a token is present and lets token-less requests proceed as anonymous. A pipeline picks one or the other, "claims required" or "claims if present"; composing both is a compile-time conflict on the jwtClaims key.
Behavior — every short-circuit uses the standard error payload, with the same code withSupabase({ auth: 'user' }) returns for an identical request:
Authorization: Bearer token → 401 MISSING_CREDENTIALS. The handler never runs.sb_* API key in that position → 401 UNUSABLE_CREDENTIAL: a credential arrived, just not a user JWT.INVALID_JWT, naming the specific reason.JWKS_NOT_CONFIGURED; verification is not optional and there is no decode-only mode.Because the contribution is non-null, gated handlers read ctx.jwtClaims directly, with no ?.sub ?? 'anon' fallbacks. Downstream entries declaring a jwtClaims prerequisite, such as withPostgresClient, compose with no further verification.
The 401 and 500 short-circuits carry no CORS headers, and a bare pipeline answers no OPTIONS preflight. For browser callers, compose withCors (@supabase/middleware/cors) ahead of the gate: it answers preflight before the gate runs and stamps Access-Control-* headers on the short-circuit responses.
Inside withSupabase the context already carries verified jwtClaims, so this gate is unnecessary there and composing it through the middleware option is a compile-time conflict. Use withSupabase({ auth: 'user' }) to gate that path.
import { pipeline } from '@supabase/middleware'
import { withRequiredClaims } from '@supabase/server/middleware/required-claims'
import { withPostgresClient } from '@supabase/server/middleware/postgres'
export default {
fetch: pipeline([withRequiredClaims(), withPostgresClient()], async (req, ctx) => {
const rows = await ctx.postgres.query`select id, title from posts`
return Response.json({ rows, caller: ctx.jwtClaims.sub })
}),
}
The composable middleware surface tracks @supabase/middleware 0.x — entry shapes, context keys, and config options may change between 0.x releases.