ADR-00006: Token Claim Composition for Participant Context
Approval Status
Section titled “Approval Status”Status flow: Proposed → Accepted → Reviewed → Approved (or Rejected), or Not Required. A superseded ADR keeps its file; only its status changes to Superseded by NNNNN.
| Governance body | Status | Last update |
|---|---|---|
| CTN Project Team | Proposed | 2026-05-29 |
| CTN Technical Advisory Board | Pending | — |
| CTN Steering Committee | Pending | — |
| BDI Conformance Team | Proposed | 2026-07-08 |
| BDI Framework Team | Pending | — |
Context and Problem Statement
Section titled “Context and Problem Statement”ADR-00003 split the data between two systems:
- Keycloak (the login system) stores everything about a user’s login (their identity as an account that can sign in).
- The ASR (the Association Register, CTN’s own back-end service) is the authoritative record for participant data — the registered organisations, their business identifiers (such as KVK or LEI numbers) and their verification status (whether those identifiers have been checked).
ADR-00003 also decided that Keycloak keeps track of which users belong to which participant and what role each user has there — using its built-in Organizations feature (version 26.6), one Keycloak Organization per participant.
Service providers and connectors that receive an access token need to know:
- Which participant the user is acting for (and which alternative participants the user could switch to).
- What role the user holds within that participant (admin / member / signatory).
- Which business identifiers identify the participant authoritatively (EUID, KVK, LEI, EORI, VAT — only those that are verified).
Of these, (1) and (2) are pure IAM concerns and can come from Keycloak directly. (3) is authoritative in ASR and must not be mirrored into Keycloak attributes (per ADR-00003).
Two prior issues from the an earlier CTN-ASR prototype review (2026-03-27) directly shape this decision:
- M17 — EUID used as attribute key: Keycloak’s built-in
organizationclaim emits{"organization": {"acme-corp": {...}}}with the alias as key. Consumers cannot rely on a predictable JSON path. - M16 — Trust level missing from tokens: verification status existed in the prototype’s database but was never reflected in tokens. Service providers could not distinguish a fully verified organization from a minimally registered one.
This ADR decides how participant context is composed into the token.
Considered Options
Section titled “Considered Options”Option 1: Keycloak built-in organization scope only
Section titled “Option 1: Keycloak built-in organization scope only”- Description: Use the built-in
organizationscope and the Organization Group Membership mapper. No ASR-side enrichment. Business identifiers are not in the token; consumers fetch them from an ASR endpoint if needed. - Pros: Zero custom code in Keycloak. Token stays small. ASR remains single source of truth — token never goes stale relative to ASR.
- Cons: Token carries no business identifiers — every consumer needs a second call. Alias-as-key shape (M17) remains. Requires a published ASR introspection-style endpoint.
Option 2: Custom Keycloak protocol mapper that calls ASR at token issuance
Section titled “Option 2: Custom Keycloak protocol mapper that calls ASR at token issuance”- Description: A custom protocol mapper (Java SPI or JavaScript mapper) in Keycloak that, during token issuance, calls an internal ASR endpoint to fetch participant claims and merges them into the token under a predictable schema (e.g.
participant.euid,participant.identifiers.kvk). - Pros: Predictable JSON paths for consumers. Single token round-trip. Claims always reflect current ASR state at issuance.
- Cons: Couples Keycloak to ASR availability at token-issuance time. Custom mapper is operational surface (deploy, upgrade, test). Latency added to every token issuance.
Option 3: Token exchange — minimal Keycloak token, ASR-issued participant token
Section titled “Option 3: Token exchange — minimal Keycloak token, ASR-issued participant token”- Description: Keycloak issues a baseline identity token (sub, email, org membership, role). Client exchanges it at an ASR token endpoint for a second token that carries participant claims, signed by ASR. Consumers validate both, or only the ASR token for participant-scoped APIs.
- Pros: Clean separation — ASR is the issuer for participant claims, matching the storage boundary. ASR can apply business rules (e.g. only emit claims for participants where verifications are current). No Keycloak customization.
- Cons: Two tokens to manage. Consumers need to understand which token they are validating. More moving parts; documentation burden.
Option 4: Userinfo-style enrichment — keep token minimal, fetch on demand
Section titled “Option 4: Userinfo-style enrichment — keep token minimal, fetch on demand”- Description: Token from Keycloak carries only identity and org membership. ASR exposes a
/participant/contextendpoint that returns the participant payload for the authenticated user. Consumers fetch when they need it; can cache for the token lifetime. - Pros: No Keycloak customization. Token always small. ASR claims always fresh. Easy to evolve the payload without breaking token consumers.
- Cons: Every consumer must implement the second call. Cache invalidation on the consumer side. Performance impact for high-volume APIs.
Decision Outcome
Section titled “Decision Outcome”Chosen option: TBD.
Rationale
Section titled “Rationale”TBD.
Consequences
Section titled “Consequences”- Positive: TBD.
- Negative: TBD.
- Neutral: TBD.
Open Questions
Section titled “Open Questions”- Should business identifiers in the token be limited to those with
Approvedverifications, or include all known identifiers with a status field? - What is the desired token TTL relative to verification-status change frequency?
- Do connectors / external dataspaces have a preferred claim schema we should align with (BDI, iSHARE, eIDAS-related profiles)?
- For Option 2 or 3: how do we handle the case where a user is a member of multiple participants — one token per participant, or one token with an array?
Implementation Plan
Section titled “Implementation Plan”- Phase 1: Inventory token consumers (connectors, service providers, internal services) and document their claim needs.
- Phase 2: Evaluate the four options against those needs; pick one.
- Phase 3: Specify the concrete claim schema and update this ADR with the decision.
Compliance and Validation
Section titled “Compliance and Validation”- Security review completed
- Performance impact assessed
- Integration impact evaluated
- Documentation updated
- Stakeholder approval obtained
Related Documents
Section titled “Related Documents”- 00002-define-a-preferred-stack.md
- 00003-separate-users-and-participants.md
- an earlier CTN-ASR Final Delivery Review, 2026-03-27 (findings M16, M17, C9, m24)
- Keycloak blog, “Organization Groups” (2026-04-29): https://www.keycloak.org/2026/04/org-groups
- RFC 9068 — JWT Profile for OAuth 2.0 Access Tokens
Status History
Section titled “Status History”| Date | Status | Notes |
|---|---|---|
| 2026-05-29 | Proposed | Skeleton — options outlined, decision pending |
In samenwerking met




