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. |
Recommended Claims
| 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
rolescanonical. During migration, flex-auth may also accept Keycloak'srealm_access.rolesandresource_access.<client>.roles, but those are provider-native compatibility inputs. - Scope encoding.
scope(space-separated string) andscp(array) both accepted; both produce the samescopesarray. - Audience encoding.
audas a single string or as an array; flex-auth always normalizes to an array. - MFA signal. IAM Profile v0.2 uses
assurance.mfaandassurance.level. Legacy/provider-nativeamrandacrare 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:
- If
client_idis set andserviceis inroles→service. - If
azpstarts withsvc-orserviceis inroles→service. - If agent metadata is present →
agent. - 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 whenenvironment=productionis 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-glassrole andassurance.level: break_glass. - Every decision involving an emergency principal MUST record a
record_emergencyobligation 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
rolesis canonical in IAM Profile v0.2.realm_access.rolesandresource_access.<client>.rolesremain 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: serviceorprincipal_type: agent,tenant, andassurance.