nuts-foundation / nuts-foundation/nuts-node
Server-side RFC 7523 JWT Bearer grant with two VPs (PSA 10.10)
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 28
- Forks
- 23
- Avg merge
- 1d 10h
- Merged PRs (30d)
- 76
Description
Related: #4078, #4079, #3965, #4038
Depends on:
- #4078 (client-side JWT bearer, shared
clientwallet owner type in policy config) - #4079 (generic did:x509 credential validation — hard dependency)
- #4038 (scope model and AuthZen callback)
Problem Statement
The Nuts node, acting as an authorization server, currently only accepts the vp_token-bearer grant type with a single VP. The LSPxNuts PSA section 10.10 OAuth profile requires the AS to also accept the RFC 7523 jwt-bearer grant type (PSA 10.10.1) where the token request contains two VPs (PSA 10.10.6): VP1 (assertion) with the healthcare provider's credentials as authorization grant, and VP2 (client_assertion) with the service provider's credentials as client authentication. The AS must validate both VPs according to a defined processing order (PSA 10.10.6) and return detailed error codes on failure (PSA 10.10.10).
Solution
Extend the server-side token request handling to accept the jwt-bearer grant type with two VPs. The AS validates VP2 (client assertion) against the client PD and VP1 (authorization grant) against the organization PD, using PEX-based matching. Generic credential validation (crypto, signatures, expiration, did:x509 cert chain/CRL via #4079) is performed by the node; business rule validation can optionally be delegated to an AuthZen callback (see #4038). The AS advertises jwt-bearer support in its metadata (PSA 10.10.4) when a client PD is configured. New error codes (PSA 10.10.10) provide detailed feedback on validation failures.
User Stories
- As a Nuts node operator acting as AS, I want to accept token requests using the RFC 7523 JWT bearer grant type, so that I can serve clients using the GF OAuth profile.
- As a Nuts node operator, I want the AS to validate both the client assertion (VP2) and the authorization grant (VP1) against their respective PDs, so that only properly authenticated requests receive tokens.
- As a Nuts node operator, I want the AS to continue accepting the existing
vp_token-bearergrant type, so that backward compatibility is maintained. - As a Nuts node operator, I want the AS metadata to advertise
jwt-bearersupport when aclientPD is configured, so that clients know which grant types are available. - As a client developer, I want descriptive error codes in token error responses that distinguish VP1 issues from VP2 issues, so that I can diagnose authentication failures.
- As a Nuts node operator, I want the introspection response to include information from both VPs keyed by wallet owner type, so that the resource server has the full context for authorization decisions (PSA 10.10.9).
- As a use-case designer, I want to define separate credential requirements for the client (SP) and the authorization grant (HCP) via the policy configuration, so that each party's required credentials are independently specified.
- As a Nuts node operator, I want the token response to include the granted scopes (PSA 10.10.7), so that clients can adjust behavior based on what was authorized.
- As a Nuts node operator, I want existing credential validation behavior to be preserved for current use cases, so that nothing breaks.
Implementation Decisions
Grant type handling
The token endpoint handler is extended to accept urn:ietf:params:oauth:grant-type:jwt-bearer alongside the existing vp_token-bearer. The grant type determines the validation flow:
vp_token-bearer: single VP inassertion(existing flow, unchanged)jwt-bearer: VP1 inassertion+ VP2 inclient_assertion,client_assertion_typemust beurn:ietf:params:oauth:client-assertion-type:jwt-bearer
Two-VP validation flow (PSA 10.10.6)
Processing order:
- Parse scope string and identify the credential profile scope (needed to look up the
clientandorganizationPDs) - Parse and validate VP2 from
client_assertion— VP signature, credential signatures, expiration, revocation, audience. Match VP2 against theclientPD - Parse and validate VP1 from
assertion— VP signature, credential signatures, expiration, revocation, audience. Match VP1 against theorganizationPD - Apply scope policy to determine granted scopes (see #4038)
- Optionally call AuthZen callback for business rule validation (if configured, see #4038)
- Store VP/claim data internally and issue an opaque access token
VP validation includes replay prevention via jti tracking and exp / aud checks as specified in PSA 10.10.6.
Credential validation for did:x509-issued credentials (cert chain, CRL, issuer attribute matching) is handled by the generic did:x509 validation layer (#4079), which applies to all credential types automatically through VerifyVP.
Custom error codes (PSA 10.10.10)
New OAuth2 error codes for detailed failure reporting:
| Error code | Description |
|---|---|
invalid_presentation |
VP is invalid (signature, structure, audience) |
invalid_credential |
A credential in the VP is invalid (expired, revoked, untrusted issuer) |
missing_credential |
A credential required by the PD is missing from the VP |
insufficient_scope |
The requested scope doesn't fit within the credential authorities |
invalid_delegation |
The delegation relationship between SP and HCP is invalid or missing |
The error_description field provides a human-readable explanation. Per the spec, error responses must not leak details about internal authorization logic or credential contents of the data custodian (PSA 10.10.10).
These error codes also improve error reporting for the existing vp_token-bearer flow.
AS metadata (PSA 10.10.4)
When a client PD is configured for at least one credential profile, the AS metadata includes:
grant_types_supported: addsurn:ietf:params:oauth:grant-type:jwt-bearertoken_endpoint_auth_methods_supported: addsprivate_key_jwt
Introspection (PSA 10.10.9)
The access token is opaque — VP and claim data is stored internally in the node and retrieved via introspection.
The introspection response already structures data by wallet owner type via PresentationDefinitions (keyed by WalletOwnerType). Adding client as a new wallet owner type integrates naturally:
PresentationDefinitionsgains acliententry with the PD used to validate VP2PresentationSubmissionsgains an entry for the VP2 submissionVPTokenarray includes both VP1 and VP2InputDescriptorConstraintIdMapincludes values extracted from both VPs
Existing introspection behavior for vp_token-bearer requests is unchanged — the client wallet owner type is only present when the jwt-bearer flow was used. This is an additive change that does not affect backward compatibility.
Token response (PSA 10.10.7)
The token response includes:
access_token: opaque tokentoken_type:DPoPexpires_in: token lifetime in secondsscope: the granted scopes (credential profile scope + authorized resource scopes per #4038)
Presentation definition endpoint
The existing presentation definition endpoint supports the client wallet owner type, allowing remote clients to fetch the SP credential requirements for a given scope.
Modules to build/modify
- JWT bearer token handler (new): handles the
jwt-bearergrant type in the token endpoint. Parses and validates both VPs, matches against PDs via PEX, stores VP data and issues opaque access token. Parallel to the existingvp_token-bearerhandler. - Error codes (modify): add new OAuth2 error codes. Apply them to both
jwt-bearerandvp_token-bearerflows. - Introspection (modify): include VP2 data under the
clientwallet owner type in the introspection response. - AS metadata (modify): advertise
jwt-bearerandprivate_key_jwtwhenclientPD is configured. - Presentation definition endpoint (modify): support
clientwallet owner type parameter.
Testing Decisions
A good test verifies that given a token request with specific VPs and scopes, the correct access token is issued (or the correct error is returned) and the introspection response contains the expected data.
Modules to test:
- JWT bearer handler: unit tests for successful two-VP flow, VP1 failure, VP2 failure, missing
client_assertion, wrongclient_assertion_type, PD mismatch for each VP, replay prevention (jti) - Error codes: verify correct error codes for each failure scenario (invalid VP, expired credential, missing credential, bad scope, bad delegation)
- Introspection: verify VP1 data under
organization, VP2 data underclient, backward compatibility (nocliententry forvp_token-bearerrequests) - AS metadata: verify
jwt-beareris advertised whenclientPD exists, and not when it doesn't - Backward compatibility: verify
vp_token-bearerflow is unchanged - Prior art: existing tests in
auth/api/iam/s2s_vptoken_test.go,auth/api/iam/metadata_test.go
Out of Scope
- Client-side JWT bearer support (covered in #4078)
- Delegation verification via AS metadata —
delegation_presentationandprovider_presentation(PSA 10.10.5) - DPoP changes (already implemented)
- URA-based token endpoint addressing (PSA 10.10.3)
- VC-type-specific validation logic in the
jwt-bearerhandler (handled generically via #4079) - DSL or configurable rule engine for credential validation
client_assertionpresentation submission (VP2 is validated against the localclientPD, not via a submission from the client)
Further Notes
- The
invalid_delegationerror code is defined per PSA 10.10.10 for future use when delegation verification is implemented. For now it may not be triggered by generic validation alone, but could be triggered by an AuthZen callback. - The presentation definition endpoint supporting
clientwallet owner type enables a future pattern where the AS advertises its SP credential requirements to remote clients. - Pastype validation (e.g., requiring pastype S for server certificates, Z for zorgverlenerspas) is handled via PD constraints on the
$.issuerfield using regex patterns — no code changes needed. See #4079 for details.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the existing token flow and tests in auth/api/iam/s2s_vptoken_test.go and auth/api/iam/metadata_test.go. Trace the token endpoint, introspection, metadata, and presentation-definition handling before implementing the two-VP jwt-bearer flow. Done means both VPs are validated in order, errors and granted scopes are returned correctly, introspection contains organization and client data, metadata is conditional, and vp_token-bearer remains compatible.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- api, authentication, backend-api-design, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100