Files
flex-auth/docs/iam-profile-consumption.md
tegwick aa8e3a4e34
Some checks failed
CI / Build and Test (push) Has been cancelled
CI / Lint (push) Has been cancelled
Align IAM Profile consumption with v0.2
2026-05-22 14:35:30 +02:00

8.0 KiB

NetKingdom IAM Profile — flex-auth Consumption Surface

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 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:

  • 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
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>, ...]             # 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>, ...]     # 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
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 v0.2 "Core 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.
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.
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.
Claim flex-auth field Use
email claims.email Contact identity; never used for authorization decisions.
name claims.name Display only.
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. 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. 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

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 rolesservice.
  2. If azp starts with svc- or service is in rolesservice.
  3. If agent metadata is present → agent.
  4. Otherwise → human.

This matches Markitect's NetKingdomIdentityClaimsAdapter._principal_type as a compatibility path. New claim envelopes should not force flex-auth to infer principal type.

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

Compatibility Notes

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