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
Integrating an EHR with an external OpenID4VCI issuer often requires a set of issuer-specific authorization request parameters. The AET ZORG-ID SDK, for example, needs both auth_method=SmartCard and scope=openid profile api on the authorization request, and more parameters are likely as integrations grow.
Today the only way to add these is the raw authorization_request_params field (#4328) on each requestCredential call. That pushes issuer-specific knowledge into every EHR and is a footgun: each integrator must know the exact parameter set, spell it correctly, and keep it in sync. Getting it wrong fails at the issuer, often with unclear errors.
We want a curated, named bundle of parameters -- a profile -- that the node ships and operators can extend, so an EHR only has to say "use the aet profile" instead of repeating the parameter set.
Solution
Add named profiles in config under auth.experimental.profile.<name>, each with an authrequest field (map[string][]string) that sets authorization request parameters. The node ships a built-in aet profile; operators can add profiles or override the built-in by name (merge per parameter key, operator wins).
A new optional profile field on the requestCredential body selects a profile by name. The node applies the profile's authrequest parameters to the OpenID4VCI authorization request.
auth:
experimental:
profile:
aet: # ships built-in; this block (if present) merges over itauthrequest:
auth_method: [SmartCard]scope: ["openid profile api"]
The subfield is named authrequest so the same profile can later carry parameter sets for other requests (OpenID4VP, OpenID4VCI Credential Request) without a config redesign.
User Stories
User Stories
As an EHR integrator, I want to name a profile ("profile": "aet") instead of hand-listing issuer parameters, so I can't get the parameter set wrong.
As a node operator, I want to define or tweak profiles in config, so I can support an issuer without a code change.
As a maintainer, I want common issuers shipped as built-in profiles, so integrators get them out of the box.
Implementation Decisions
Configuration
// auth/config.go -- under ExperimentalConfig, alongside Clients.typeExperimentalConfigstruct {
// ...Profilesmap[string]ProfileConfig`koanf:"profile"`
}
typeProfileConfigstruct {
// AuthorizationRequest sets parameters on the OpenID4VCI authorization request. Multi-valued per key.AuthorizationRequestmap[string][]string`koanf:"authrequest"`
}
EXPERIMENTAL: this configuration may change or be removed without further notice (under auth.experimental).
Built-in profiles live in code and are merged under operator config per parameter key:
// Returns the merged authrequest params for a profile (built-in overlaid by config), false if the name is unknown.func (auth*Auth) AuthorizationRequestProfile(namestring) (map[string][]string, bool)
Consulted from the api/iam Wrapper via the AuthenticationServices interface, mirroring OAuthClientCredentials.
Applying to the authorization request
Order, building the authorization redirect query:
Node parameters (response_type, state, client_id, client_id_scheme, authorization_details, redirect_uri, code_challenge, code_challenge_method); on project-gf-pilot the configured-client block may replace client_id / drop the scheme.
The query is built as url.Values (was map[string]string) to support multi-valued parameters; scope: ["openid profile api"] renders as a single space-joined value, ["a","b"] renders as repeated parameters.
docs/_static/auth/v2.yaml + make gen-api: profile request field.
auth/api/iam/openid4vci.go: resolve + apply the profile in RequestOpenid4VCICredentialIssuance.
Testing Decisions
Assert external behavior. For the accessor: built-in aet returns its parameters; operator config merges/overrides per key; unknown name returns false. For the handler: a request with profile: "aet" produces a redirect whose query carries auth_method=SmartCard and scope=openid profile api; an unknown profile returns 400; profile + authorization_request_params layer in the documented order.
auth/api/iam: redirect query for profile: aet, unknown-profile error, layering with authorization_request_params.
Prior art: the authorization_request_params tests in auth/api/iam/openid4vci_test.go.
Impact Assessment
Backwards compatibility: additive. No profile -> unchanged behavior. authorization_request_params keeps working. Versioning: minor. Experimental: may change or be removed without notice. Configuration/deployment: new auth.experimental.profile.<name>.authrequest; not a CLI flag (map type), so it loads from YAML/env and does not appear in server_options.rst. Built-in aet needs no config. Security: profiles are operator/built-in config (trusted), so they may override node parameters -- a misconfigured profile could break the flow (e.g. overwrite state/code_challenge); this is the operator's responsibility. The raw per-request field stays locked down (cannot override node parameters).
Out of Scope
Profile parameter sets for other requests (OpenID4VP, OpenID4VCI Credential Request) -- the authrequest subfield name reserves room; not implemented now.
Removing authorization_request_params -- kept as the low-level escape hatch.
Per-issuer automatic profile binding -- selection is explicit via the profile request field.
Further Notes
Built-in aet uses scope and auth_method; neither is a node-set authorization parameter today, so aet only adds.
Implementation Plan
#
Description
PR
Status
Depends on
1
auth.experimental.profile config + ProfileConfig + built-in aet + accessor
--
--
--
2
profile request field + apply in authorization request (multi-value query)
Related: #4316, #4328
Problem Statement
Integrating an EHR with an external OpenID4VCI issuer often requires a set of issuer-specific authorization request parameters. The AET ZORG-ID SDK, for example, needs both
auth_method=SmartCardandscope=openid profile apion the authorization request, and more parameters are likely as integrations grow.Today the only way to add these is the raw
authorization_request_paramsfield (#4328) on eachrequestCredentialcall. That pushes issuer-specific knowledge into every EHR and is a footgun: each integrator must know the exact parameter set, spell it correctly, and keep it in sync. Getting it wrong fails at the issuer, often with unclear errors.We want a curated, named bundle of parameters -- a profile -- that the node ships and operators can extend, so an EHR only has to say "use the
aetprofile" instead of repeating the parameter set.Solution
Add named profiles in config under
auth.experimental.profile.<name>, each with anauthrequestfield (map[string][]string) that sets authorization request parameters. The node ships a built-inaetprofile; operators can add profiles or override the built-in by name (merge per parameter key, operator wins).A new optional
profilefield on therequestCredentialbody selects a profile by name. The node applies the profile'sauthrequestparameters to the OpenID4VCI authorization request.{ "issuer": "https://issuer.example.com/oauth", "wallet_did": "did:web:example.com", "authorization_details": [ { "type": "openid_credential", "credential_configuration_id": "..." } ], "redirect_uri": "https://example.com/oauth2/org1/callback", "profile": "aet" }-> redirect:
.../authorize?...&auth_method=SmartCard&scope=openid+profile+api.The subfield is named
authrequestso the same profile can later carry parameter sets for other requests (OpenID4VP, OpenID4VCI Credential Request) without a config redesign.User Stories
User Stories
"profile": "aet") instead of hand-listing issuer parameters, so I can't get the parameter set wrong.Implementation Decisions
Configuration
EXPERIMENTAL: this configuration may change or be removed without further notice (under
auth.experimental).Built-in profiles live in code and are merged under operator config per parameter key:
Lookup
Consulted from the
api/iamWrapper via theAuthenticationServicesinterface, mirroringOAuthClientCredentials.Applying to the authorization request
Order, building the authorization redirect query:
response_type, state, client_id, client_id_scheme, authorization_details, redirect_uri, code_challenge, code_challenge_method); onproject-gf-pilotthe configured-client block may replaceclient_id/ drop the scheme.authrequest-- set/override, multi-valued. May override node parameters (trusted operator/built-in config).authorization_request_params(raw, OpenID4VCI: add authorization_request_params to requestCredential for issuer-specific authorization parameters #4328) -- applied last; may not override the node parameters from step 1 (rejected with 400), but may add or override profile-set parameters.Failure modes:
profilenames an unknown profile (not built-in, not configured) -> 400, fail loud.authorization_request_paramstries to override a node parameter -> 400 (unchanged from OpenID4VCI: add authorization_request_params to requestCredential for issuer-specific authorization parameters #4328).The query is built as
url.Values(wasmap[string]string) to support multi-valued parameters;scope: ["openid profile api"]renders as a single space-joined value,["a","b"]renders as repeated parameters.Modules to build/modify
auth/config.go:ExperimentalConfig.Profiles+ProfileConfig+ built-inaet.auth/auth.go:AuthorizationRequestProfileaccessor (merge built-in + config).auth/interface.go+ regenerated mocks: interface method.docs/_static/auth/v2.yaml+make gen-api:profilerequest field.auth/api/iam/openid4vci.go: resolve + apply the profile inRequestOpenid4VCICredentialIssuance.Testing Decisions
Assert external behavior. For the accessor: built-in
aetreturns its parameters; operator config merges/overrides per key; unknown name returns false. For the handler: a request withprofile: "aet"produces a redirect whose query carriesauth_method=SmartCardandscope=openid profile api; an unknown profile returns 400; profile +authorization_request_paramslayer in the documented order.Modules to test:
auth:AuthorizationRequestProfile(built-in, merge, unknown).auth/api/iam: redirect query forprofile: aet, unknown-profile error, layering withauthorization_request_params.authorization_request_paramstests inauth/api/iam/openid4vci_test.go.Impact Assessment
Backwards compatibility: additive. No
profile-> unchanged behavior.authorization_request_paramskeeps working.Versioning: minor. Experimental: may change or be removed without notice.
Configuration/deployment: new
auth.experimental.profile.<name>.authrequest; not a CLI flag (map type), so it loads from YAML/env and does not appear inserver_options.rst. Built-inaetneeds no config.Security: profiles are operator/built-in config (trusted), so they may override node parameters -- a misconfigured profile could break the flow (e.g. overwrite
state/code_challenge); this is the operator's responsibility. The raw per-request field stays locked down (cannot override node parameters).Out of Scope
authrequestsubfield name reserves room; not implemented now.authorization_request_params-- kept as the low-level escape hatch.profilerequest field.Further Notes
Built-in
aetusesscopeandauth_method; neither is a node-set authorization parameter today, soaetonly adds.Implementation Plan
auth.experimental.profileconfig +ProfileConfig+ built-inaet+ accessorprofilerequest field + apply in authorization request (multi-value query)