Align IAM Profile consumption with v0.2
Some checks failed
CI / Build and Test (push) Has been cancelled
CI / Lint (push) Has been cancelled

This commit is contained in:
2026-05-22 14:35:30 +02:00
parent 8354485632
commit aa8e3a4e34
7 changed files with 105 additions and 45 deletions

View File

@@ -1,14 +1,16 @@
# NetKingdom IAM Profile — flex-auth Consumption Surface
Date: 2026-05-16
Status: Draft for FLEX-WP-0005 P5.5; binds the input contract for the
standalone evaluator (FLEX-WP-0002) and every PDP adapter (FLEX-WP-0004).
Upstream: `~/the-custodian/canon/standards/iam-profile_v0.1.md`.
Date: 2026-05-22
Status: Aligned with NetKingdom IAM Profile v0.2; binds the input contract
for the standalone evaluator (FLEX-WP-0002) and every PDP adapter
(FLEX-WP-0004).
Upstream: `~/net-kingdom/canon/standards/iam-profile_v0.2.md`.
## Boundary
The NetKingdom IAM Profile defines the OIDC contract shared across hubs
and services. flex-auth **consumes verified claims**; it does not
The NetKingdom IAM Profile defines the OIDC contract shared across
platform, tenant, service, and agent principals. flex-auth **consumes
verified claims**; it does not
verify token signatures, fetch JWKS, or terminate OIDC sessions. Those
responsibilities belong upstream:
@@ -32,17 +34,25 @@ envelope identical to Markitect's `EnterpriseIdentity` shape:
```yaml
issuer: <oidc issuer URL> # required
subject: <stable subject id> # required
principal_type: human | service | emergency
tenant: tenant:platform | tenant:<id> # required
principal_type: human | service | agent
audience: [<aud>, ...] # required, non-empty
authorized_party: <azp or client_id, optional>
preferred_username: <string> # required for humans
roles: [<role>, ...] # required, non-empty
scopes: [<scope>, ...] # required, non-empty
groups: [<group>, ...] # optional; resolved by directory layer
groups: [<group>, ...] # required, may be empty
assurance:
level: aal0 | aal1 | aal2 | aal3 | break_glass
methods: [<method>, ...]
mfa: <bool>
source: <identity or MFA evidence source>
at: <unix timestamp, optional>
acr: <oidc acr value, optional>
amr: [<oidc amr value>, ...] # e.g. pwd, otp, mfa, hwk
mfa: <bool — derived from amr>
amr: [<oidc amr value>, ...] # tolerated provider-native input
agent:
id: <agent id, optional>
mode: autonomous | delegated
directory:
groups_claim_present: <bool>
group_overage: <bool> # Microsoft Entra-style group overage
@@ -55,7 +65,7 @@ provenance:
This is the envelope every check API call receives, regardless of
which upstream identity provider produced the token.
## Required Claims (per IAM Profile §"Required Claims")
## Required Claims (per IAM Profile v0.2 "Core Claims")
flex-auth treats the following as hard requirements. Missing any
produces a `validation_error` before the request reaches a policy
@@ -68,9 +78,13 @@ package.
| `aud` | `audience` | Must include the flex-auth instance or the protected system. |
| `exp` | (validated upstream) | flex-auth tolerates ≤60s clock skew per profile §"Token Lifecycle". |
| `iat` | (validated upstream) | Same. |
| `tenant` | `tenant` | Required for platform/tenant boundary decisions. |
| `principal_type` | `principal_type` | `human`, `service`, or `agent`; emergency is a role plus `assurance.level=break_glass`. |
| `groups` | `groups` | Required, possibly empty; overage is handled by directory resolvers. |
| `scope` or `scp` | `scopes` | At least one scope required. Empty scope is a hard fail. |
| `preferred_username` | `preferred_username` | Required for `principal_type=human`. Optional for service accounts. |
| `roles` or `realm_access.roles` or `resource_access.<client>.roles` | `roles` | Union of all three sources. At least one role required. |
| `roles` | `roles` | Canonical role source. At least one role required by current flex-auth policy fixtures. |
| `assurance` | `assurance` | Required normalized evidence object with level, methods, mfa, and source. |
| `preferred_username` | `preferred_username` | Required for `principal_type=human`. Optional for service and agent principals. |
## Recommended Claims
@@ -78,7 +92,6 @@ package.
| --- | --- | --- |
| `email` | `claims.email` | Contact identity; **never** used for authorization decisions. |
| `name` | `claims.name` | Display only. |
| `groups` | `groups` (after resolution) | Authorization input; subject to freshness/overage. |
| `azp` | `authorized_party` | Distinguishes service-account client from impersonating client. |
| `acr` | `assurance.acr` | Authentication context class; gates high-trust scopes. |
| `amr` | `assurance.amr` | Authentication methods; `otp`/`mfa`/`hwk` lift `assurance.mfa` to true. |
@@ -87,29 +100,31 @@ package.
flex-auth normalizes — protected systems never see the variation.
- **Role claim location.** Three OIDC providers ship roles in three
places: top-level `roles`, Keycloak's `realm_access.roles`, and
Keycloak's per-client `resource_access.<client>.roles`. flex-auth
unions all three.
- **Role claim location.** IAM Profile v0.2 makes top-level `roles`
canonical. During migration, flex-auth may also accept Keycloak's
`realm_access.roles` and `resource_access.<client>.roles`, but those
are provider-native compatibility inputs.
- **Scope encoding.** `scope` (space-separated string) and `scp`
(array) both accepted; both produce the same `scopes` array.
- **Audience encoding.** `aud` as a single string or as an array;
flex-auth always normalizes to an array.
- **MFA signal.** Either an explicit `mfa: true` claim or any of
`otp`/`mfa`/`hwk` in `amr` produces `assurance.mfa = true`.
- **MFA signal.** IAM Profile v0.2 uses `assurance.mfa` and
`assurance.level`. Legacy/provider-native `amr` and `acr` are
tolerated as inputs to the normalized assurance object.
## Principal-Type Detection
flex-auth classifies the principal by:
IAM Profile v0.2 supplies `principal_type` directly. flex-auth uses that
claim as normative input. Legacy fixtures may be classified by:
1. If `client_id` is set and `service` is in `roles``service`.
2. If `azp` starts with `svc-` or `service` is in `roles``service`.
3. If `emergency` is in `roles``emergency`.
3. If agent metadata is present`agent`.
4. Otherwise → `human`.
This matches Markitect's `NetKingdomIdentityClaimsAdapter._principal_type`
and follows IAM Profile §"Hub-to-Hub Service Account Pattern" (service
accounts named `svc-*` and carrying the `service` role).
as a compatibility path. New claim envelopes should not force flex-auth
to infer principal type.
## Group Overage and Freshness
@@ -140,7 +155,9 @@ Per IAM Profile §"Local Development Profile":
Per IAM Profile §"Human Override and Emergency Access":
- `emergency` is a first-class `principal_type`.
- Emergency access is represented as a human, service, or agent
principal with an `emergency`/`break-glass` role and
`assurance.level: break_glass`.
- Every decision involving an emergency principal MUST record a
`record_emergency` obligation in the decision envelope.
- Policy packages MAY allow emergency principals; flex-auth's audit
@@ -154,11 +171,11 @@ the validation steps above in Python. flex-auth's Go implementation
(FLEX-WP-0002 P2.4) mirrors its behavior and stays in sync via
contract tests against the fixtures in `examples/claims/`.
## Open Items
## Compatibility Notes
- Whether `roles` becomes canonical and `realm_access.roles` becomes
legacy is still listed as an open question in IAM Profile v0.1. As
of 2026-05-16 flex-auth normalizes both with no preference.
- `Workload identity` (Kubernetes service-account tokens, GCP/AWS
workload-identity federation) is not yet in the IAM Profile.
flex-auth's service-account handling is currently OIDC-only.
- `roles` is canonical in IAM Profile v0.2. `realm_access.roles` and
`resource_access.<client>.roles` remain tolerated provider-native inputs
while Keycloak mappings are updated.
- Workload identity may enter through a documented token-exchange path,
but the normalized envelope still carries `principal_type: service` or
`principal_type: agent`, `tenant`, and `assurance`.