Files
flex-auth/docs/iam-profile-consumption.md
tegwick f930e96568
Some checks failed
CI / Build and Test (push) Has been cancelled
CI / Lint (push) Has been cancelled
IAM Profile consumption doc + claim fixtures; close FLEX-WP-0005
Completes FLEX-WP-0005 T05 and closes the Foundations and Topaz
Alignment workstream.

docs/iam-profile-consumption.md captures flex-auth's input surface
against NetKingdom IAM Profile v0.1:
- boundary (flex-auth consumes verified claims; upstream layer
  validates signatures and audiences)
- normalized input envelope (matches Markitect's EnterpriseIdentity)
- required, recommended, and tolerated claim variations
- role-claim location union (top-level / realm_access / resource_access)
- scope encoding (string vs array)
- principal-type detection (human / service / emergency)
- group-overage and freshness expectations
- production vs local-development handling

examples/claims/ ships five contract fixtures:
- key-cape-lightweight.yaml (profile minimum)
- keycloak-heavy.yaml (full variation set + MFA)
- service-account.yaml (svc-* hub-to-hub)
- emergency.yaml (break-glass with incident metadata)
- keycloak-group-overage.yaml (Entra-style hasgroups: true)

All fixtures parse as valid YAML. They become contract tests for the
standalone evaluator (FLEX-WP-0002 P2.4) and the Topaz adapter
(FLEX-WP-0004 T01); both code paths must produce identical normalized
envelopes for the same fixture.

FLEX-WP-0005 workstream marked status=done in this file and completed
in the State Hub. FLEX-WP-0002 is now fully unblocked.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 09:09:36 +02:00

7.2 KiB

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.

Boundary

The NetKingdom IAM Profile defines the OIDC contract shared across hubs and services. flex-auth consumes verified claims; it does not verify token signatures, fetch JWKS, or terminate OIDC sessions. Those responsibilities belong upstream:

  • key-cape (lightweight mode) validates tokens against its local OIDC provider and emits claims that conform to the profile.
  • Keycloak (heavy mode) signs tokens; integration code (e.g. Markitect's NetKingdomIdentityClaimsAdapter) validates issuer, audience, signature, expiry, and clock skew before handing claims to flex-auth.

A flex-auth deployment that exposes a network endpoint MUST be fronted by an identity layer that does the verification. The flex-auth core accepts a normalized claim envelope and is responsible for everything after "this caller is authenticated".

Input Envelope

flex-auth's standalone evaluator and adapters consume a normalized envelope identical to Markitect's EnterpriseIdentity shape:

issuer: <oidc issuer URL>          # required
subject: <stable subject id>       # required
principal_type: human | service | emergency
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
assurance:
  acr: <oidc acr value, optional>
  amr: [<oidc amr value>, ...]     # e.g. pwd, otp, mfa, hwk
  mfa: <bool — derived from amr>
directory:
  groups_claim_present: <bool>
  group_overage: <bool>            # Microsoft Entra-style group overage
claims: { ... }                    # full original claim map (minus 'groups')
provenance:
  source: claims | jwt | jwt-fixture
  verified_signature: <bool>

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")

flex-auth treats the following as hard requirements. Missing any produces a validation_error before the request reaches a policy package.

Claim flex-auth field Notes
iss issuer Must match the deployment's expected issuer; production rejects local-dev issuers (localhost, 127.0.0.1, .local, dev.local).
sub subject Stable identifier; not a username.
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.
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.
Claim flex-auth field Use
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.

Tolerated Variations

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.
  • 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.

Principal-Type Detection

flex-auth classifies the principal by:

  1. If client_id is set and service is in rolesservice.
  2. If azp starts with svc- or service is in rolesservice.
  3. If emergency is in rolesemergency.
  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).

Group Overage and Freshness

Microsoft Entra and Keycloak both clip the groups claim once a threshold is reached; the token then carries hasgroups: true (Entra) or _claim_names.groups (also Entra). flex-auth's directory layer is responsible for resolving the full set via Graph/SCIM/Keycloak admin API; the claim envelope carries directory.group_overage = true so policy packages can decide whether to fail-closed or accept the partial set with an audit_only outcome.

Group freshness is tracked at the directory-resolver layer (out of scope for this document; see FLEX-WP-0004 T05).

Production vs Local Development

Per IAM Profile §"Local Development Profile":

  • Local-development issuers (localhost, 127.0.0.1, hostnames ending in .local, dev.local) are rejected when environment=production is set in the request context.
  • A development token marked clearly through issuer/audience is accepted in non-production environments.
  • The local-development path exists to keep flex-auth useful before Keycloak is wired in; it never weakens production rules.

Emergency Principals

Per IAM Profile §"Human Override and Emergency Access":

  • emergency is a first-class principal_type.
  • 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 layer ensures the action is durable regardless.

Reference Implementation

Markitect's NetKingdomIdentityClaimsAdapter (at markitect-tool/src/markitect_tool/policy/enterprise.py) implements 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

  • 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.