Kanidm RFC 8693 conformance: issued_token_type serialization breaks agentgateway token exchange #39
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lab/iac#39
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Kanidm RFC 8693 conformance:
issued_token_typeserialization breaks agentgateway token exchangeStatus
Open / blocking the Golden Path. This is the single hard blocker for the agentgateway per-request token-exchange architecture (#38). All three cluster-tooling consumers (#36 Sveltos, #37 Argo CD) and every future Kanidm-trusting service (homebox, sonarr, …) depend on it.
Why it matters
The Golden Path (#38) replaces all short-lived-token refresh machinery with an agentgateway that performs the RFC 8693 token exchange per request:
The gateway then forwards the exchanged token upstream. This works end-to-end against a conformant authorization server, but fails against Kanidm because Kanidm's exchange response advertises a non-standard
issued_token_type.Reproduction (verified live 2026-08-12)
Environment: kanidm/server 1.10.4 (deployed on portable), agentgateway standalone v1.4.1.
argocdOAuth2 client, generate an API token.scope=openid: Response (sensitive fields elided):policies.backendTLS: {}— see config notes below) and send a request through it. It fails:The exchange also returns
id_tokenalongsideaccess_token, but agentgateway forwards theaccess_tokenfield only.Root cause
RFC 8693 (§3.1) requires
issued_token_typeto be a URI, and the standard for an access token is the URNurn:ietf:params:oauth:token-type:access_token.kanidm/proto/src/oauth2.rs: Confirmed present in tag v1.10.4 and unfixed on kanidm main (checked 2026-08-12).requested_token_typedoes not influence it: the response is"AccessToken"whether the field is omitted or explicitlyurn:ietf:params:oauth:token-type:access_token; requesting an id_token/jwt URN returnsinvalid_request.crates/agentgateway/src/http/auth/oauth/mod.rsOAuthTokenType::from_urnhas noAccessTokenbranch, so Kanidm's value parses toNoneand the exchange is rejected withunusable issued_token_type: AccessToken. Confirmed in both v1.4.1 and agentgateway main. Even withrequested_token_typeunset, agentgateway expectsaccess_token(empirically verified: a mock returning a different-but-valid URN was rejected withexpected urn:ietf:params:oauth:token-type:access_token).Agentgateway config notes (from the same spike, for anyone re-running it)
hostmust setpolicies: { backendTLS: {} }, or the gateway connects plaintext to :443 and the ingress returns 404.clientAuth: { clientId: argocd, method: clientSecretPost }with noclientSecret— matches howkanidm-login.pyauthenticates.scope: openid groupsis rejected withaccess_deniedfor a principal whose groups have nogroupsscope-map entry on the client (ADR-0005 gap, separate from this bug).available_scopesin the Kanidm server log showed{argocd_role, email, openid}.Impact
audto be thekubernetesclient and flux-clusters #11/#1522 live — those are tracked separately.execProviderConfig) or Sveltos exec-provider still work today, because a custom wrapper can read theid_token/access_tokenfrom the raw Kanidm response. The gateway path needs the fix.Proposed fixes (evaluate when implementing)
Serialize/DeserializeforIssuedTokenTypethat maps the variants to/from the RFC 8693 URNs (urn:ietf:params:oauth:token-type:{access_token,refresh_token,id_token,saml1,saml2}). ~1-line change. Would fix every OIDC client, not just agentgateway.issued_token_typevalidation tolerant — treat"AccessToken"as the access-token URN, or skip the check when norequestedTokenTypeis configured. Smaller surface; fixes only our gateways.Acceptance criteria (when the blocker is lifted)
curlexchange against Kanidm returnsissued_token_type: urn:ietf:params:oauth:token-type:access_token(or agentgateway accepts the response).backendAuth.oauthTokenExchange(token endpoint = Kanidm,backendTLS: {}, public client,scope: openid groups) forwards a fresh token upstream (200).aud=<service-client-id>and thegroupsclaim when the SA's groups have the scope map.Upstream filing packet
If we ever decide to report upstream, this is the material. Expected format: kanidm/kanidm issue under
Server, labelledoauth2, with the reproduction in the "Reproduction" section above and a pointer toproto/src/oauth2.rsIssuedTokenType. Include the note thatrequested_token_typeis ignored by the server when serializing the response.Linked
kubernetesclient + machine SAs — the credential source)RESOLUTION — CLOSED (2026-08-12)
Fixed server-side and verified. Kanidm
1.11.0-itt-fix(harbor.animeteamspeak.moe/library/kanidm-server:1.11.0-itt-fix, deployed to portable) implements theIssuedTokenTypeURN serialization fix. Re-ran the full agentgateway E2E against it:issued_token_type: "urn:ietf:params:oauth:token-type:access_token"(was the bare"AccessToken"). Kanidm logs showrequested_token_type: Some("urn:ietf:params:oauth:token-type:access_token")withstatus_code: 200.backendTLS: {}, public client,audiences: [argocd],scope: openid): the SA API token was exchanged and a freshat+jwtforwarded upstream, HTTP 200 (previouslybackend authentication failed: unusable issued_token_type: AccessToken).Remaining (not blockers for this issue):
groupsscope-map gap (ADR-0005): SAs whose groups lack agroupsscope-map entry on the client still getaccess_deniedforscope=openid groups.argocd_userscurrently exposes{argocd_role, email, openid}. Tracked as a provisioning requirement in #35.Decision (unchanged): not filed upstream. The lab is self-hosting the patched build; the "Upstream filing packet" above remains available if that changes.
RESOLVED: Kanidm 1.11.0-itt-fix returns the RFC 8693 issued_token_type URN; agentgateway E2E passes (HTTP 200, fresh at+jwt forwarded). Remaining: groups scope-map gap (#35) and Gate 2 apiserver OIDC (#38/#28). Not filed upstream.