You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Experimental. This feature may change or be removed without further notice. Must be flagged as experimental in both code (doc comments) and documentation, and lives under auth.experimental.
Problem
As an OpenID4VCI wallet in the auth-code flow, the node authenticates as a public client using its did:web base URL as client_id + client_id_scheme=entity_id:
Token request: auth/client/iam/openid4vp.go:227 (client_id only, no secret)
entity_id is Nuts-specific. Third-party issuers expect a registered client_id + secret. AET (OpenIddict) requires client auth -- a public client_id is refused (invalid_client) and client_secret_post is accepted. No way to configure this today, so the auth-code flow can't complete against any non-entity_id AS.
Solution
Per-server OAuth client config under auth.experimental. On a match, present the configured client_id + client_secret (client_secret_post) instead of the did:web + entity_id defaults.
Keys are single tokens (serverurl, not server_url): env vars map _ to the . delimiter (core/config.go:60), so ..._SERVER_URL would nest as server.url. No match -> current behavior unchanged (additive).
Implementation Decisions
Config
Add to the existing ExperimentalConfig (auth/config.go), which already documents "subject to change without notice and may be removed":
Validate at Configure: serverurl required + valid URL (HTTPS in strict mode); clientid required; reject duplicate serverurl.
Match key
Exact match of serverurl against the AS identifier authzServerMetadata.Issuer (not credential_issuer) -- credentials are an AS concept and the token request targets the AS. AET splits them: credential_issuer = .../aet, AS = .../aet-oauth. Operators configure the AS URL from authorization_servers. One lookup helper, shared by both request sites.
Auth method
client_secret_post only for now.
TODO: derive method from AS metadata token_endpoint_auth_methods_supported (fallback post when absent) and support client_secret_basic. AET omits the field, so deriving buys nothing yet.
Request changes
Site
On match
Authorization request (api/iam/openid4vci.go:131)
configured client_id, omit client_id_scheme
Token request (client/iam/openid4vp.go:227)
configured client_id; secret as client_secret form field, or public client if empty
Modules
auth/config.go: type + ExperimentalConfig.Clients field + validation + experimental doc comment.
auth/auth.go:135: thread into iam.ClientConfig + reach the api/iam Wrapper.
Assert wire output via httptest.Server: on match, redirect carries configured client_id and no client_id_scheme, token request carries client_id + client_secret; no match = today's entity_id shape.
Related: #4310, #3197
Problem
As an OpenID4VCI wallet in the auth-code flow, the node authenticates as a public client using its
did:webbase URL asclient_id+client_id_scheme=entity_id:auth/api/iam/openid4vci.go:131-140auth/client/iam/openid4vp.go:227(client_idonly, no secret)entity_idis Nuts-specific. Third-party issuers expect a registeredclient_id+ secret. AET (OpenIddict) requires client auth -- a publicclient_idis refused (invalid_client) andclient_secret_postis accepted. No way to configure this today, so the auth-code flow can't complete against any non-entity_idAS.Solution
Per-server OAuth client config under
auth.experimental. On a match, present the configuredclient_id+client_secret(client_secret_post) instead of thedid:web+entity_iddefaults.Keys are single tokens (
serverurl, notserver_url): env vars map_to the.delimiter (core/config.go:60), so..._SERVER_URLwould nest asserver.url. No match -> current behavior unchanged (additive).Implementation Decisions
Config
Add to the existing
ExperimentalConfig(auth/config.go), which already documents "subject to change without notice and may be removed":Validate at
Configure:serverurlrequired + valid URL (HTTPS in strict mode);clientidrequired; reject duplicateserverurl.Match key
Exact match of
serverurlagainst the AS identifierauthzServerMetadata.Issuer(notcredential_issuer) -- credentials are an AS concept and the token request targets the AS. AET splits them:credential_issuer=.../aet, AS =.../aet-oauth. Operators configure the AS URL fromauthorization_servers. One lookup helper, shared by both request sites.Auth method
client_secret_postonly for now.Request changes
api/iam/openid4vci.go:131)client_id, omitclient_id_schemeclient/iam/openid4vp.go:227)client_id; secret asclient_secretform field, or public client if emptyModules
auth/config.go: type +ExperimentalConfig.Clientsfield + validation + experimental doc comment.auth/auth.go:135: thread intoiam.ClientConfig+ reach theapi/iamWrapper.auth/api/iam/openid4vci.go: authorization request.auth/client/iam/openid4vp.go: token request.Testing
Assert wire output via
httptest.Server: on match, redirect carries configuredclient_idand noclient_id_scheme, token request carriesclient_id+client_secret; no match = today'sentity_idshape.auth/config.go: validation +NUTS_AUTH_EXPERIMENTAL_CLIENTS_0_*array binding.auth/api/iam: authorization-requestclient_id/scheme, match vs no-match.auth/client/iam: token-request public client +client_secret_post.Impact
clientsecretredacted in/configoutput and logs, never logged in the token request body. Strict mode = HTTPS-only token endpoint.Out of Scope
client_secret_basic(TODO above).client_secret_jwt,private_key_jwt); dynamic client registration.did:web+entity_idfor Nuts-to-Nuts flows (Auth: add Authorization Server URL in Service Access Token flow (v2) #3197).Further Notes
Only target now is the AET OpenID4VCI issuer; shape kept protocol-neutral for later reuse.
AET probe (via
nuts-services.nlbridge): publicclient_id->invalid_client;client_secret_postaccepted (localhost/passwordreachedinvalid_granton a dummy code).token_endpoint_auth_methods_supportedabsent;grant_types_supported:["authorization_code"]."Send no
client_id" is not covered: an entry requiresclientid.Implementation Plan
client_id, omitentity_idschemeclient_id+client_secret_postclientsecretredaction