From 260ff9cc99930bd69b2cc3ed20b73017064b16ea Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 23 Jul 2026 22:47:00 +0200 Subject: [PATCH] Draft KEY-WP-0005: IAM Profile core claims (tenant, principal_type, roles, assurance, tenant_roles) Discovered while planning tenant-engine's flex-auth WriteAuthorizer (TEN-WP-0003): src/internal/server/oidc/token.go emits none of iam-profile_v0.3.md's Core Claims yet -- not tenant, principal_type, groups, roles, or assurance. tenant_roles isn't a small addition on top of existing tenant support; it's the last, optional claim on top of a baseline that doesn't exist yet. Registered, not implemented -- security-sensitive Go token-issuance code deserves dedicated context before changing, not a rushed edit from a first read of the file. Co-Authored-By: Claude Sonnet 5 --- .../KEY-WP-0005-iam-profile-core-claims.md | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 workplans/KEY-WP-0005-iam-profile-core-claims.md diff --git a/workplans/KEY-WP-0005-iam-profile-core-claims.md b/workplans/KEY-WP-0005-iam-profile-core-claims.md new file mode 100644 index 0000000..5a3c1cd --- /dev/null +++ b/workplans/KEY-WP-0005-iam-profile-core-claims.md @@ -0,0 +1,114 @@ +--- +id: KEY-WP-0005 +type: workplan +title: "IAM Profile core claims: tenant, principal_type, groups, roles, assurance, tenant_roles" +domain: infotech +repo: key-cape +status: ready +owner: codex +topic_slug: netkingdom +created: "2026-07-23" +updated: "2026-07-23" +--- + +# IAM Profile core claims: tenant, principal_type, groups, roles, assurance, tenant_roles + +Discovered while planning `tenant-engine/workplans/TEN-WP-0003-flex-auth-write-authorizer.md`: +`key-cape`'s token issuance (`src/internal/server/oidc/token.go`) does not +currently emit `tenant`, `principal_type`, `groups`, `roles`, or +`assurance` — none of `net-kingdom/canon/standards/iam-profile_v0.3.md`'s +Core Claims are implemented yet, only `preferred_username`/`email`/`groups` +(LDAP-sourced, not the profile's normalized `groups` claim) and OIDC's own +base claims (`nonce`, etc.). + +This is bigger than "add the `tenant_roles` claim on top of existing tenant +support" — `key-cape` isn't yet profile-conformant at all on the tenant/ +principal/assurance axis. `tenant_roles` (the reason this workplan exists) +is the last, optional claim in that same set (v0.3's addition on top of +v0.2's baseline) — implementing it in isolation without the claims it +depends on for meaning (`tenant`, `principal_type`) would be incoherent. + +**This workplan is registered, not implemented, by the same session that +found the gap.** Token issuance is security-sensitive code in a Go codebase +with its own adapter conventions (`internal/adapters/{lldap,privacyidea,authelia}`) +that deserves dedicated context before changing, not a rushed edit from a +first read of `token.go`. Scoped and sequenced here so the work isn't lost. + +**Depends on:** `net-kingdom/canon/standards/iam-profile_v0.3.md` (ratified). +**Related:** `tenant-engine/workplans/TEN-WP-0003-flex-auth-write-authorizer.md` +(the consumer waiting on `tenant_roles`); `key-cape/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md` +(Binky's onboarding, which needs `tenant` claim support to produce a +correct `tenant:friendly:binky` token at all). + +## Task: Emit `tenant`, `principal_type`, `groups`, `roles`, `assurance` + +```task +id: KEY-WP-0005-T01 +status: todo +priority: high +``` + +Bring `token.go`'s claim assembly up to `iam-profile_v0.3.md`'s Core Claims +table baseline (the v0.2-originated set): + +- `tenant`: resolve from whatever user/service/agent-to-tenant mapping + `key-cape`'s LLDAP/Authelia backend can provide today; if no such mapping + exists yet, that's itself a sub-task — this claim cannot be stamped from + nothing. +- `principal_type`: `human` for the existing Authorization Code + PKCE flow; + `service` for client-credentials flows (check whether `key-cape` already + issues client-credentials tokens at all — `grant_types_supported` in the + discovery contract claims it does, but that needs verifying against real + code, the same way this workplan verified `tenant` claim absence). +- `groups`, `roles`: normalize from LDAP group/role data into the profile's + flat string-array shape. +- `assurance`: `{level, methods, mfa, source, at}` per the profile — + `privacyIDEA`-backed MFA evidence maps to `aal2`; single-factor maps to + `aal1`. + +Done when: `tools/iam-profile-conformance/` (already exists) passes against +these claims for both human and service token issuance paths. + +## Task: Emit `tenant_roles` (optional, cached) + +```task +id: KEY-WP-0005-T02 +status: todo +priority: high +``` + +At token-issuance time, call `tenant-engine`'s cache-read endpoint +(`GET /tenants/{tenant_id}/roles`) using the `tenant` claim resolved in T01, +and stamp the result as the optional `tenant_roles` claim +(`iam-profile_v0.3.md`, "Tenant Roles" section). Follows the same adapter +pattern as `internal/adapters/{lldap,privacyidea,authelia}` — a new +`internal/adapters/tenantengine` package, not inline HTTP calls in +`token.go`. + +Fail open on this claim specifically, not closed: `tenant_roles` is +documented as a cache callers must not trust for privileged decisions +anyway (`flex-auth` re-validates live). If `tenant-engine` is unreachable at +issuance time, omit the claim rather than fail the entire token issuance — +losing a performance optimization is acceptable; blocking login because a +cache source is down is not. This is the one place in the whole `tenant_roles` +design where fail-open is correct, precisely because the live-lookup path +exists as the real gate. + +Done when: token issuance still succeeds with `tenant_roles` omitted when +`tenant-engine` is unreachable (test simulates the outage); present and +correct when reachable, for a tenant with known role grants. + +## Task: Closure review + +```task +id: KEY-WP-0005-T03 +status: todo +priority: low +``` + +Confirm T01–T02 done; conformance suite green for both tasks together. +Note in closure: this unblocks `KEY-WP-0004`'s Binky onboarding actually +producing conformant tokens, and unblocks `flex-auth`'s side of the +`tenant_roles` picture receiving a real claim to reason about (though +`flex-auth`'s live-lookup path doesn't depend on this claim existing — only +the cache-read/performance path does). Run `statehub fix-consistency`.