feat(auth): validate access tokens with the ZITADEL Go SDK

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.
This commit is contained in:
Admin Bot
2026-10-26 10:23:00 -07:00
parent 4e8319289b
commit 8a3a1c447c
11 changed files with 789 additions and 64 deletions
+12 -1
View File
@@ -66,12 +66,23 @@ type AuthConfig struct {
JWKSURL string
// RequiredCapability is the permission required for LLM inference (e.g., "llm:inference").
RequiredCapability string
// TokenURL is the Authentik token endpoint for password/refresh grants.
// TokenURL is the OIDC token endpoint used for token exchange
// (client_credentials / password grants).
TokenURL string
// ClientID is the OAuth2 client ID for token exchange.
ClientID string
// ClientSecret is the OAuth2 client secret (loaded from env, never from config file).
ClientSecret string
// ZitadelDomain is the ZITADEL instance host (for example
// "zitadel.riotpiao.com"). When set, token validation uses the official
// ZITADEL Go SDK instead of the raw JWKS path.
ZitadelDomain string
// ZitadelInsecurePort, when non-empty, makes the SDK speak plain HTTP to
// that port. Only for a local port-forward.
ZitadelInsecurePort string
// ZitadelClientIDs is the audience allow-list for the SDK verifier. Empty
// accepts any audience issued by the same instance.
ZitadelClientIDs []string
}
// Route represents a single route and its upstream configuration.