> 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:
```text
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
| `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:
```text
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 **onboarding-risk / entity-shape** classification
only. It does not gate 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