feat(api): real Zitadel token introspection (fail-closed) + expected-audience enforcement
CI (SIAX Cloud) / security (push) Successful in 12s
CI (SIAX Cloud) / contracts (push) Successful in 30s
CI (SIAX Cloud) / quality (push) Successful in 1m33s

- preHandler: Bearer tokens are introspected against Zitadel (RFC 7662)
- any introspection failure (unreachable, non-200, bad JSON, inactive) = 401
- asserted aud must include ZITADEL_EXPECTED_AUDIENCE (project id); absent aud accepted per RFC 7662
- config: ZITADEL_INTROSPECTION_CLIENT_ID/SECRET + ZITADEL_EXPECTED_AUDIENCE (optional; Bearer-presence fallback logs warn in prod)
- 13 new tests (introspection fail-closed matrix + preHandler flow)
This commit is contained in:
2026-09-16 22:06:46 +02:00
parent dac79054de
commit 495050752a
5 changed files with 301 additions and 0 deletions
+3
View File
@@ -7,6 +7,9 @@ export const envSchema = z.object({
ZITADEL_ISSUER: z.string().url(),
ZITADEL_AUDIENCE: z.string(),
DATABASE_URL: z.string(),
ZITADEL_INTROSPECTION_CLIENT_ID: z.string().optional(),
ZITADEL_INTROSPECTION_CLIENT_SECRET: z.string().optional(),
ZITADEL_EXPECTED_AUDIENCE: z.string().optional(),
AUD0_BASE_URL: z.string().url().optional(),
AUD0_API_KEY: z.string().optional(),
ST0RE_BASE_URL: z.string().url().optional(),