Skip to content

Commit fcdd7a8

Browse files
committed
fix: make standard OIDC scopes optional in credential matching
Previously, standard OIDC scopes like 'openid' were explicitly excluded from credential matching. This was problematic because: 1. The 'openid' scope is REQUIRED by the OIDC specification - clients cannot remove it 2. It should be possible to optionally configure a credential to match the 'openid' scope if desired This change makes standard OIDC scopes (openid, profile, email, address, phone, offline_access) optional for credential matching: - If a scope has a corresponding credential configuration, it will match - If a scope has no configuration, it is silently skipped - The existing 'no match found' logic handles unconfigured scopes gracefully Changes: - client.go: Removed explicit 'openid' exclusion in buildLegacyDCQLQuery - presentation_builder.go: Updated BuildDCQLQuery and scopesMatch to not filter standard scopes - Exported StandardOIDCScopes and FilterStandardScopes as utility functions for when filtering is specifically needed
1 parent 54d4f2a commit fcdd7a8

File tree

2 files changed

+36
-41
lines changed

2 files changed

+36
-41
lines changed

internal/verifier/apiv1/client.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,15 +205,14 @@ func (c *Client) createDCQLQuery(ctx context.Context, scopes []string) (*openid4
205205
return c.buildLegacyDCQLQuery(scopes)
206206
}
207207

208-
// buildLegacyDCQLQuery builds a DCQL query using credential constructor config
208+
// buildLegacyDCQLQuery builds a DCQL query using credential constructor config.
209+
// All scopes are considered for matching, including standard OIDC scopes like "openid".
210+
// Scopes that don't have a corresponding credential configuration are silently skipped,
211+
// making standard OIDC scopes optional - they can match if configured, but are not required.
209212
func (c *Client) buildLegacyDCQLQuery(scopes []string) (*openid4vp.DCQL, error) {
210213
var credentials []openid4vp.CredentialQuery
211214

212215
for _, scope := range scopes {
213-
if scope == "openid" {
214-
continue
215-
}
216-
217216
credInfo, ok := c.cfg.CredentialConstructor[scope]
218217
if !ok {
219218
continue

pkg/openid4vp/presentation_builder.go

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -85,23 +85,18 @@ func (pb *PresentationBuilder) BuildFromTemplate(ctx context.Context, templateID
8585
return dcql, template, nil
8686
}
8787

88-
// BuildDCQLQuery creates a DCQL query from OIDC scopes
89-
// This attempts to find matching templates, and falls back to a generic DCQL query if none are found
88+
// BuildDCQLQuery creates a DCQL query from OIDC scopes.
89+
// This attempts to find matching templates, and falls back to a generic DCQL query if none are found.
90+
// All scopes are considered for matching, including standard OIDC scopes like "openid".
91+
// This allows standard OIDC scopes to optionally map to credentials if configured.
9092
func (pb *PresentationBuilder) BuildDCQLQuery(ctx context.Context, scopes []string) (*DCQL, error) {
9193
if len(scopes) == 0 {
9294
// Return a generic DCQL query when no scopes provided
9395
return pb.createGenericDCQL(), nil
9496
}
9597

96-
// Filter out standard OIDC scopes
97-
credentialScopes := filterStandardScopes(scopes)
98-
if len(credentialScopes) == 0 {
99-
// No credential-specific scopes, return generic DCQL
100-
return pb.createGenericDCQL(), nil
101-
}
102-
103-
// Try to find a template for the first credential scope
104-
for _, scope := range credentialScopes {
98+
// Try to find a template for any scope (including standard OIDC scopes)
99+
for _, scope := range scopes {
105100
if templateID, ok := pb.scopeIndex[scope]; ok {
106101
template := pb.templates[templateID]
107102
dcql := template.GetDCQLQuery()
@@ -196,20 +191,28 @@ func (pb *PresentationBuilder) createGenericDCQL() *DCQL {
196191
}
197192
}
198193

199-
// filterStandardScopes removes standard OIDC scopes from the list
200-
func filterStandardScopes(scopes []string) []string {
201-
standardScopes := map[string]bool{
202-
"openid": true,
203-
"profile": true,
204-
"email": true,
205-
"address": true,
206-
"phone": true,
207-
"offline_access": true,
208-
}
194+
// StandardOIDCScopes contains scopes defined by OpenID Connect Core.
195+
// These are protocol-level scopes that are optional for credential matching.
196+
// The "openid" scope is REQUIRED by the OIDC specification and will always
197+
// be present, but it does not need to be mapped to a credential.
198+
var StandardOIDCScopes = map[string]bool{
199+
"openid": true,
200+
"profile": true,
201+
"email": true,
202+
"address": true,
203+
"phone": true,
204+
"offline_access": true,
205+
}
209206

207+
// FilterStandardScopes removes standard OIDC scopes from the list.
208+
// This is a utility function that can be used when you specifically need
209+
// to identify scopes that are not standard OIDC scopes. Note that credential
210+
// matching logic does NOT use this - all scopes (including standard ones)
211+
// are considered for matching to allow optional credential mappings.
212+
func FilterStandardScopes(scopes []string) []string {
210213
filtered := make([]string, 0, len(scopes))
211214
for _, scope := range scopes {
212-
if !standardScopes[scope] {
215+
if !StandardOIDCScopes[scope] {
213216
filtered = append(filtered, scope)
214217
}
215218
}
@@ -234,24 +237,17 @@ func (pb *PresentationBuilder) FindTemplateByScopes(scopes []string) Presentatio
234237
return nil
235238
}
236239

237-
// scopesMatch checks if the requested scopes match the template scopes
238-
// A template matches if it contains at least one of the requested scopes
239-
// (excluding standard OIDC scopes like "openid", "profile", "email")
240+
// scopesMatch checks if the requested scopes match the template scopes.
241+
// A template matches if it contains at least one of the requested scopes.
242+
// All scopes are considered for matching, including standard OIDC scopes like "openid".
243+
// This allows standard OIDC scopes to optionally map to credentials if configured.
240244
func scopesMatch(requestedScopes []string, templateScopes []string) bool {
241-
// Filter out standard OIDC scopes from requested scopes
242-
credentialScopes := make([]string, 0)
243-
for _, scope := range requestedScopes {
244-
if scope != "openid" && scope != "profile" && scope != "email" {
245-
credentialScopes = append(credentialScopes, scope)
246-
}
247-
}
248-
249-
if len(credentialScopes) == 0 {
245+
if len(requestedScopes) == 0 {
250246
return false
251247
}
252248

253-
// Check if template contains any of the credential scopes
254-
for _, requestedScope := range credentialScopes {
249+
// Check if template contains any of the requested scopes
250+
for _, requestedScope := range requestedScopes {
255251
for _, templateScope := range templateScopes {
256252
if requestedScope == templateScope {
257253
return true // Match found

0 commit comments

Comments
 (0)