key-cape/workplans/KEY-WP-0005-iam-profile-core-claims.md
tegwick 8059f65a1b chore(consistency): write back state_hub ids for KEY-WP-0005 [auto]
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 23:52:13 +02:00

118 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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"
state_hub_workstream_id: "74ac0d31-b86e-449d-89b2-4e15263bf050"
---
# 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
state_hub_task_id: "66107caf-ba26-4291-bb09-1f9e58275505"
```
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
state_hub_task_id: "1b8f44b4-2763-4d5a-a3eb-1c2b9a25dd15"
```
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
state_hub_task_id: "34555bf7-0f18-4dac-ade2-6ff3738f353f"
```
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`.