2026-05-16 09:09:36 +02:00
# NetKingdom IAM Profile — flex-auth Consumption Surface
2026-05-22 14:35:30 +02:00
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` .
2026-05-16 09:09:36 +02:00
## Boundary
2026-05-22 14:35:30 +02:00
The NetKingdom IAM Profile defines the OIDC contract shared across
platform, tenant, service, and agent principals. flex-auth **consumes
verified claims**; it does not
2026-05-16 09:09:36 +02:00
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:
```yaml
issuer: < oidc issuer URL > # required
subject: < stable subject id > # required
2026-05-22 14:35:30 +02:00
tenant: tenant:platform | tenant:< id > # required
principal_type: human | service | agent
2026-05-16 09:09:36 +02:00
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
2026-05-22 14:35:30 +02:00
groups: [< group > , ...] # required, may be empty
2026-05-16 09:09:36 +02:00
assurance:
2026-05-22 14:35:30 +02:00
level: aal0 | aal1 | aal2 | aal3 | break_glass
methods: [< method > , ...]
mfa: < bool >
source: < identity or MFA evidence source >
at: < unix timestamp , optional >
2026-05-16 09:09:36 +02:00
acr: < oidc acr value , optional >
2026-05-22 14:35:30 +02:00
amr: [< oidc amr value > , ...] # tolerated provider-native input
agent:
id: < agent id , optional >
mode: autonomous | delegated
2026-05-16 09:09:36 +02:00
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.
2026-05-22 14:35:30 +02:00
## Required Claims (per IAM Profile v0.2 "Core Claims")
2026-05-16 09:09:36 +02:00
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. |
2026-05-22 14:35:30 +02:00
| `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. |
2026-05-16 09:09:36 +02:00
| `scope` or `scp` | `scopes` | At least one scope required. Empty scope is a hard fail. |
2026-05-22 14:35:30 +02:00
| `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. |
2026-05-16 09:09:36 +02:00
## 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.
2026-05-22 14:35:30 +02:00
- **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.
2026-05-16 09:09:36 +02:00
- **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.
2026-05-22 14:35:30 +02:00
- **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.
2026-05-16 09:09:36 +02:00
## Principal-Type Detection
2026-05-22 14:35:30 +02:00
IAM Profile v0.2 supplies `principal_type` directly. flex-auth uses that
claim as normative input. Legacy fixtures may be classified by:
2026-05-16 09:09:36 +02:00
1. If `client_id` is set and `service` is in `roles` → `service` .
2. If `azp` starts with `svc-` or `service` is in `roles` → `service` .
2026-05-22 14:35:30 +02:00
3. If agent metadata is present → `agent` .
2026-05-16 09:09:36 +02:00
4. Otherwise → `human` .
This matches Markitect's `NetKingdomIdentityClaimsAdapter._principal_type`
2026-05-22 14:35:30 +02:00
as a compatibility path. New claim envelopes should not force flex-auth
to infer principal type.
2026-05-16 09:09:36 +02:00
## 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":
2026-05-22 14:35:30 +02:00
- Emergency access is represented as a human, service, or agent
principal with an `emergency` /`break-glass` role and
`assurance.level: break_glass` .
2026-05-16 09:09:36 +02:00
- 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/` .
2026-05-22 14:35:30 +02:00
## Compatibility Notes
2026-05-16 09:09:36 +02:00
2026-05-22 14:35:30 +02:00
- `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` .