workflow, memory, s3 and iam served requests with no Authorization header. Only sqs and the LLM models checked a token.
Set auth.required true on all five adapters. The dispatcher derives one capability per method from the verb: GET/HEAD/OPTIONS -> :read, anything else -> :write.
Those strings now exist as ZITADEL role keys with the colon, and the riotpiao grant carries them.
workflow, memory, s3 and iam served requests with no Authorization header. Only sqs and the LLM models checked a token.
Set auth.required true on all five adapters. The dispatcher derives one capability per method from the verb: GET/HEAD/OPTIONS -> <service>:read, anything else -> <service>:write.
Those strings now exist as ZITADEL role keys with the colon, and the riotpiao grant carries them.
Replace the handwritten Authentik JWT checks with github.com/zitadel/zitadel-go/v3.
What the SDK now owns:
- OIDC discovery (client.Discover) to find issuer and JWKS URI
- remote, auto-refreshing JWKS (rp.NewRemoteKeySet)
- signature, expiry and issuer checks (op.NewAccessTokenVerifier,
op.VerifyAccessToken)
- the verified claim set and role lookup (oauth.IntrospectionContext,
IsGrantedRole)
What stays local:
- a multi-audience allow-list, because this gateway is a shared resource
server and the SDK's oauth.WithJWT accepts exactly one client ID
- an adapter from IntrospectionContext to jwt.MapClaims so the rest of the
gateway keeps one claim representation
Other changes:
- issuer is configuration-driven; the hardcoded authentik.riotpiao.com
check is gone
- aud may be a string or an array (ZITADEL sends an array)
- CheckPermissions also reads urn:zitadel:iam:org:project:roles, the claim
ZITADEL uses for project roles
- requiredCapability accepts a comma-separated list, so llm:inference and
llm-inference both satisfy the LLM gate
- a five-segment token is reported as an encrypted JWE with the fix in the
message, instead of failing as a bad signature
Config: auth.zitadelDomain turns the SDK path on; jwksUrl stays supported for
the legacy path and for tests.
workflow, memory, s3 and iam served requests without any Authorization header.
Only sqs and the LLM models checked a token, so four upstreams were reachable
from inside the cluster with no authentication at all.
Set auth.required true on all five adapters. The dispatcher then enforces one
capability per method, derived from the verb
(internal/serviceadapter/router.go:142):
GET/HEAD/OPTIONS -> "<service>:read"
anything else -> "<service>:write"
Those strings must exist as ZITADEL role keys with the colon. The project now
holds memory:read, memory:write, workflow:read, workflow:execute, s3:read,
s3:write, iam:read, iam:write, sqs:read and sqs:write, and the riotpiao user
grant carries them, so a browser session keeps working.
Correct the old comment: ZITADEL does store colon role keys. Verified by listing
the project roles, which returned all eleven colon keys.
rock
merged commit 29f5c64fab into main2026-10-27 08:12:52 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
workflow, memory, s3 and iam served requests with no Authorization header. Only sqs and the LLM models checked a token.
Set auth.required true on all five adapters. The dispatcher derives one capability per method from the verb: GET/HEAD/OPTIONS -> :read, anything else -> :write.
Those strings now exist as ZITADEL role keys with the colon, and the riotpiao grant carries them.