key-cape/workplans/KEY-WP-0005-iam-profile-core-claims.md
tegwick 260ff9cc99 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 <noreply@anthropic.com>
2026-07-23 22:47:00 +02:00

5 KiB
Raw Blame History

id type title domain repo status owner topic_slug created updated
KEY-WP-0005 workplan IAM Profile core claims: tenant, principal_type, groups, roles, assurance, tenant_roles infotech key-cape ready codex netkingdom 2026-07-23 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

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)

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

id: KEY-WP-0005-T03
status: todo
priority: low

Confirm T01T02 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.