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:platformis distinct from tenant planes such astenant:coulomband 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
S256is 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
agentobject withidandmode. agent.modeisautonomousordelegated.- Delegated agents MUST identify the delegating actor using
actor_subor an equivalentact.subclaim. - 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 cachedtenant_rolesclaim onto an issued token at issuance time, sourced fromtenant-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 querytenant-enginelive 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.
VENsurviving 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.
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
localhostor127.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
emergencyrole. - 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
S256advertisement 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_rolesclaim 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, andassurance; - 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 againsttenant-enginebefore anyaal2-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.