Minor version. Per ADR-0011's versioning rule, this adds an optional claim and clarifies non-normative guidance — no required claim, validation rule, or previously-issued token is invalidated. Existing v0.2 implementations remain conformant; `tenant_roles` and the revised Tenant Claim guidance are additive.

## Purpose

The NetKingdom IAM Profile is the provider-neutral OIDC contract that identity implementations issue and applications consume.

It defines:

- OIDC discovery and endpoint requirements;
- Authorization Code + PKCE for human login;
- service-account and workload identity token requirements;
- human, service, and agent principal representation;
- tenant, tenant-grouping, and platform-boundary claims;
- tenant capability roles and their carrying mechanism;
- explicit assurance evidence;
- the identity-to-authorization claim contract consumed by flex-auth;
- local-development and emergency-access behavior;
- executable conformance expectations.

Applications target this profile, not a concrete identity provider. key-cape is the lightweight implementation. Keycloak is the expanded-mode implementation. Both are interchangeable at the application and authorization boundary when they conform to this document.

## Ownership

NetKingdom owns the core/platform profile. See ADR-0011.

Downstream systems may define extension scopes, roles, resource names, and tenant policy vocabularies. Those extensions are not part of the core profile unless a future version explicitly adopts them. Extension vocabularies must map back to the core claims in this document before flex-auth or applications consume them.

## Design Principles

- Consumers trust signed OIDC tokens, not provider-specific sessions.
- Identity providers assert identity and authentication evidence; they do not make final resource authorization decisions.
- The same profile works in lightweight key-cape mode and expanded Keycloak mode.
- Tenancy is explicit. `tenant:platform` is distinct from tenant planes such as `tenant:coulomb` and from later tenants grouped per ADR-0013.
- A tenant's onboarding grouping (ADR-0013) and its capability roles (ADR-0014) are independent axes. Neither is encoded in the other; a tenant's roles may change without renaming its identifier.
- Human, service, and agent principals are distinguishable.
- Assurance evidence is explicit enough for flex-auth policy.
- Local-development issuers are useful but never accepted by production.
- Emergency access is auditable, time-bounded, and reviewable.

## Discovery Contract

Every IAM Profile implementation MUST expose OIDC discovery at:

    GET <issuer>/.well-known/openid-configuration

The discovery response MUST include:

Field | Requirement
--- | ---
`issuer` | Exact issuer identifier used in tokens
`authorization_endpoint` | Required for human Authorization Code + PKCE
`token_endpoint` | Required for token exchange and service accounts
`jwks_uri` | Required for signature validation
`userinfo_endpoint` | Required when userinfo is supported by the flow
`scopes_supported` | MUST include `openid`; SHOULD include `profile` and `email`
`response_types_supported` | MUST include `code`
`grant_types_supported` | MUST include `authorization_code`; MUST include `client_credentials` or a documented workload-token exchange for service identities
`id_token_signing_alg_values_supported` | MUST include the implementation signing algorithm; RS256 is required for v0.2+ conformance
`code_challenge_methods_supported` | MUST include `S256`

The response SHOULD include `end_session_endpoint` where logout is supported and `claims_supported` listing the core claims below.

Consumers MUST discover endpoints and key material from the issuer metadata instead of hardcoding provider-specific paths.

## Required Flows

### Human Interactive Flow

Human users authenticate with Authorization Code + PKCE.

Required properties:

- PKCE with `S256` is mandatory for browser and CLI clients.
- Implicit flow is not part of the profile.
- MFA or equivalent strong assurance is mandatory for privileged, destructive, platform-root, and emergency access in production.
- Access tokens are short-lived.
- Refresh tokens are allowed only for trusted clients with explicit rotation and revocation.

### Service Account Flow

Service-to-service traffic uses client credentials or a deployment's documented workload identity token-exchange equivalent.

Required properties:

- Service subjects are stable and named for service plus environment.
- Secrets or workload credentials are delivered through the credential-management standard, not plaintext configuration.
- Tokens include an audience that identifies the target service.
- Tokens carry `principal_type: service`.
- Service accounts receive only required scopes and roles.
- Credentials are rotated and never shared between environments.

### Agent Principal Flow

Agents are automation principals that may act autonomously or under delegated authority.

Required properties:

- Tokens carry `principal_type: agent`.
- Tokens include an `agent` object with `id` and `mode`.
- `agent.mode` is `autonomous` or `delegated`.
- Delegated agents MUST identify the delegating actor using `actor_sub` or an equivalent `act.sub` claim.
- Agent tokens MUST carry the tenant they operate within.
- Agent tokens MUST include assurance evidence for both the agent credential and any delegated human authority when policy needs it.

## Core Claims

Access tokens accepted by production consumers MUST provide the following claims after provider mapping or normalization:

Claim | Type | Meaning
--- | --- | ---
`iss` | string | OIDC issuer URL or issuer identifier
`sub` | string | Stable subject identifier unique within `iss`
`aud` | string or array | Intended audience; MUST include the receiving service
`exp` | number | Expiry timestamp
`iat` | number | Issued-at timestamp
`nbf` | number | Not-before timestamp, recommended for production tokens
`jti` | string | Token identifier, recommended for audit and replay controls
`tenant` | string | Tenant identifier such as `tenant:platform` or `tenant:friendly:binky`
`principal_type` | string | `human`, `service`, or `agent`
`groups` | array | Group memberships, possibly empty
`roles` | array | Coarse identity roles for the subject, possibly empty
`scope` or `scp` | string or array | Granted OAuth scopes
`assurance` | object | Authentication and credential assurance evidence

Recommended human claims:

Claim | Meaning
--- | ---
`preferred_username` | Human-readable username
`email` | Contact identity
`name` | Display name

Recommended service claims:

Claim | Meaning
--- | ---
`azp` or `client_id` | Authorized client/service identifier
`service` | Object naming the service and environment

Recommended agent claims:

Claim | Meaning
--- | ---
`agent.id` | Stable agent identifier
`agent.mode` | `autonomous` or `delegated`
`actor_sub` or `act.sub` | Delegating subject for delegated agents

Optional claims (new in v0.3):

Claim | Type | Meaning
--- | --- | ---
`tenant_roles` | array | Cached tenant capability roles (`PLTF`/`IAM`/`VEN`/`CUS`), possibly empty. See "Tenant Roles" below — this is a point-in-time cache, not the authoritative source.

### Role Claim

The canonical subject-level role claim is `roles`, an array of strings. This is distinct from `tenant_roles` (below) — a subject's own coarse identity roles are not the same fact as which capability roles the subject's tenant holds.

Expanded-mode Keycloak deployments may also expose provider-native roles such as `realm_access.roles`, but conforming tokens consumed by flex-auth or applications MUST either emit `roles` directly or pass through a normalizing adapter that produces `roles`.

### Scope Vocabulary

The core profile defines only OAuth/OIDC base scopes:

Scope | Meaning
--- | ---
`openid` | Required for OIDC login
`profile` | Basic profile claims
`email` | Email claim where appropriate
`offline_access` | Refresh-token capable access where explicitly allowed

Hub-, application-, and resource-specific scopes such as `hub:*`, `ops:*`, `fin:*`, or storage actions are downstream extensions. They are valid only when the consuming system defines them and maps them to flex-auth resource/action semantics.

## Tenant Claim

`tenant` is required for every token accepted by profile consumers.

Tenant identifiers follow `tenant:<grouping>:<name>`, where `<grouping>` is one of the taxonomy ratified by ADR-0013 at identifier creation:

    trial        - test/trial/showcase tenants only
    friendly     - known, easily reached, tolerant of experimentation/instability
    single       - one-person business entities (freelance consultants)
    small        - up to 10 employees at time of onboarding (attoo)
    medium       - up to 100 employees (attoo)
    large        - up to 1000 employees (attoo)
    enterprise   - 1001+ employees (attoo)
    consumer     - private individuals
    family       - a legal family
    community    - a non-legal group of people
    association  - a legal association of people
    agentic      - financially enabled AI entities

`tenant:platform` and `tenant:coulomb` remain reserved, ungrouped identifiers outside this taxonomy: `tenant:platform` is the platform control-plane tenant, not a business entity being onboarded; `tenant:coulomb` is the first internal/reference tenant established by ADR-0006, predating this taxonomy. Tenant administration for `tenant:coulomb` or any grouped tenant must never imply platform-root authority.

Subjects may have access to multiple tenants, but a token used for a request MUST identify the tenant context for that request. If a client needs to switch tenant context, it obtains a new token or uses an approved token-exchange flow that records the target tenant.

The grouping segment is an immutable record of the tenant's onboarding-time onboarding-risk / entity-shape classification. It remains vocabulary-valid but becomes historical if the tenant's classification later changes. The authoritative current grouping is the `grouping` field held by `tenant-engine`; consumers MUST NOT split `tenant` and treat its middle segment as current policy input. Changing current grouping never renames the tenant. Neither the historical segment nor current grouping gates which capability roles (below) a tenant may hold — see Tenant Roles.

## Tenant Roles

Tenant capability roles are a separate fact from the grouping above and from the subject-level `roles` claim: `PLTF`, `IAM`, `VEN`, `CUS` (ratified by ADR-0014), non-exclusive — a tenant may hold several simultaneously, and holding one does not require or restrict any grouping.

Source of truth: `tenant-engine` (`canon/standards/tenant-engine-boundary-contract_v0.1.md`), not this profile and not any token. `tenant-engine` records role grants/revocations, their link (if any) to a plan/subscription, and emits domain events on change.

Carrying mechanism — hybrid, not claim-only:

- `key-cape` (or Keycloak) MAY stamp a cached `tenant_roles` claim onto an issued token at issuance time, sourced from `tenant-engine`.
- Consumers MAY trust the cached claim for ordinary, non-privileged decisions.
- Consumers MUST NOT trust the cached claim for privileged, destructive, platform-root, secret, credential-vending, or otherwise `assurance.level >= aal2`-class decisions. Those decisions MUST query `tenant-engine` live for current role state before authorizing the action.
- This bounds staleness for ordinary actions to the issuing token's lifetime (5-30 minutes for service/agent tokens, see Token Lifecycle) while guaranteeing freshness exactly where a stale grant (e.g. `VEN` surviving a plan cancellation) would matter most.

`trial`-grouped tenants may hold any capability role without restriction — the grouping exists to showcase, test, and explore every role. Safety for `trial` tenants is enforced through `tenant-engine`-owned resource guardrails (spend limits, entity/action counts — reserved, not yet specified), not through role gating.

## Assurance Evidence

The canonical assurance claim is `assurance`.

It is an object with these fields:

Field | Type | Meaning
--- | --- | ---
`level` | string | `aal0`, `aal1`, `aal2`, `aal3`, or `break_glass`
`methods` | array | Authentication methods, e.g. `pwd`, `otp`, `webauthn`, `client_secret`, `workload_identity`, `upstream_mfa`
`mfa` | boolean | Whether the authentication included multiple factors or equivalent upstream evidence
`source` | string | Provider of the evidence, e.g. `key-cape`, `keycloak`, `privacyidea`, `entra`, `local-identity`
`at` | number | Authentication time, recommended

Level meanings:

Level | Meaning
--- | ---
`aal0` | Local/dev or unauthenticated bootstrap evidence; never production privileged
`aal1` | Single-factor or service credential evidence
`aal2` | MFA or equivalent strong upstream assurance
`aal3` | Phishing-resistant or hardware-backed assurance
`break_glass` | Time-bounded emergency access with post-event review

Privileged, destructive, platform-root, secret, credential-vending, and emergency flows require `aal2` or stronger unless a policy explicitly permits a narrower service or workload identity path. Emergency access MUST use `break_glass` and short token lifetimes. This is the same threshold class that gates when `tenant_roles` MUST be re-validated live against `tenant-engine` rather than trusted from the token — see Tenant Roles.

Provider-native claims such as `acr` and `amr` may be present, but consumers use `assurance` as the normalized profile claim.

## Identity To Authorization Contract

flex-auth consumes IAM Profile tokens as normative identity input. flex-auth MUST NOT re-derive identity, tenant, group, role, or assurance facts from provider-specific session state.

The profile guarantees these inputs for authorization decision envelopes:

Decision input | Source claim
--- | ---
Subject | `sub`
Issuer | `iss`
Audience | `aud`
Tenant | `tenant`
Principal type | `principal_type`
Groups | `groups`
Subject roles | `roles`
Tenant capability roles | `tenant_roles` (cached); `tenant-engine` live lookup required for high-stakes decisions
Scopes | `scope` or `scp`
Assurance | `assurance`
Authorized client | `azp` or `client_id`, where present
Agent/delegation context | `agent`, `actor_sub`, or `act`, where present
Token lifetime/audit ids | `iat`, `nbf`, `exp`, `jti`, where present

Authorization decisions are made by flex-auth and its delegated PDP adapters. Identity providers may assert roles or scopes, but those claims are inputs to policy, not final permission to act on a resource.

## Token Lifecycle

Recommended production defaults:

Token | Lifetime | Notes
--- | --- | ---
Human access token | 5-15 minutes | Short-lived bearer token
Refresh token | 8-12 hours | Rotated and revoked on logout or suspicion
Service token | 5-30 minutes | Reissued by client credentials or workload identity
Agent token | 5-30 minutes | Shorter when delegated or platform-scoped
Emergency token | 5-15 minutes | Requires incident/review record

Consumers MUST reject expired tokens and tokens with invalid issuer, audience, signature, `nbf`, or algorithm. Clock skew tolerance SHOULD be small, normally no more than 60 seconds.

JWKS material may be cached, but consumers MUST tolerate key rotation by refreshing JWKS when a token uses an unknown `kid`.

## Local Development Profile

A local file-backed provider may be used for development, tests, and bootstrap contexts where the full platform is unavailable.

It MUST:

- expose OIDC discovery;
- issue signed JWTs;
- support deterministic test users and service accounts;
- use local-only issuer URLs or a clearly local issuer identifier;
- mark tokens as local/development through issuer, audience, or assurance evidence;
- be rejected by production consumers.

Production consumers MUST reject:

- issuer `local-identity`;
- `http://` issuers;
- loopback issuers such as `localhost` or `127.0.0.1`;
- tokens with `assurance.level: aal0`;
- tokens where the environment marks the issuer as local/dev.

## Emergency And Break-Glass Access

Emergency access is allowed only as a break-glass path.

Requirements:

- Emergency identities are disabled by default.
- Activation requires an incident, decision, or human-recorded review reference.
- Tokens are short-lived and carry the `emergency` role.
- Tokens carry `assurance.level: break_glass`.
- Every emergency action emits an audit/progress/incident event.
- Emergency access is reviewed after use and then disabled again.

Emergency access MUST NOT bypass audit logging or flex-auth policy.

## Conformance

An implementation conforms to IAM Profile v0.3 when it passes the executable conformance suite in:

    tools/iam-profile-conformance/

The suite validates:

- discovery document completeness;
- PKCE `S256` advertisement and rejection of authorization requests that omit a code challenge;
- JWKS structure and key ids;
- token issuer, audience, expiry, `nbf`, `iat`, and RS256 signature;
- tenant, principal type, groups, roles, scopes, and assurance claim shape;
- `tenant_roles` claim shape when present (array of ratified role strings);
- agent and delegated-agent claim shape;
- local-development issuer rejection in production mode.

Conformance must be runnable against both key-cape lightweight issuers and Keycloak expanded-mode issuers. Implementations may add provider adapters, but the token consumed by applications and flex-auth must match the core claim contract above. `tenant_roles` conformance does not require an implementation to emit the claim (it is optional); when emitted, it must match the ratified vocabulary.

## Validation Checklist

A service or implementation is profile-ready when:

- it reads OIDC discovery rather than hardcoding endpoints;
- it validates issuer, audience, expiry, `nbf`, algorithm, and signature;
- it refreshes JWKS on unknown `kid`;
- it supports Authorization Code + PKCE for human login;
- it supports service-account or workload identity tokens;
- it emits `tenant`, `principal_type`, `groups`, `roles`, `scope`/`scp`, and `assurance`;
- it uses the ADR-0013 grouping vocabulary for new tenant identifiers;
- if it consumes `tenant_roles`, it treats the claim as a cache and re-validates live against `tenant-engine` before any `aal2`-class decision;
- it maps provider-native claims into the canonical core claims;
- it rejects local-development issuers in production;
- it logs emergency access with a durable audit trail;
- flex-auth receives identity facts from the profile, not from provider-specific sessions.