[#4253 6/8] feat(auth): validate credential_selection keys (Policy 1, 400 invalid_request)#4288
Draft
stevenvegt wants to merge 1 commit into
Draft
[#4253 6/8] feat(auth): validate credential_selection keys (Policy 1, 400 invalid_request)#4288stevenvegt wants to merge 1 commit into
stevenvegt wants to merge 1 commit into
Conversation
Assisted-by: AI
Contributor
|
Coverage Impact This PR will not change total coverage. 🚦 See full report on Qlty Cloud »🛟 Help
|
This was referenced May 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Parent PRD
#4253
Item 6 of 8. Depends on item #2 (#4281,
ValidateSelectionKeys) — branched off4253-2-validators. Independent of the #3/#4/#5 chain.Summary
Validate
credential_selectionkeys against the union of the PDs the request will evaluate, returning400OAuthinvalid_requestlisting every unknown key (Policy 1). Moves typo protection to the request entry, replacing the per-input-descriptor strictness that the deletedNewFieldSelectorused to provide.Placement
credential_selectionis accepted by exactly one endpoint:RequestServiceAccessToken(body type atauth/api/iam/generated.go:162). The HTTP handler (auth/api/iam/api.go:727) does not resolve PDs — it delegates to the client (auth/client/iam/openid4vp.go:272), which resolves them just before building. So validation lives in the client, right after PD resolution and before any submission build (the PRD's "API handler" is imprecise; this is where the in-scope PDs are actually known):requestVPTokenAccessToken— afterpdResolver.Resolve(line 307):ValidateSelectionKeys(credentialSelection, resolved.PresentationDefinition).requestJwtBearerAccessToken— afterloadAndValidateProfileresolvesorgPD+spPD(lines 355-356):ValidateSelectionKeys(credentialSelection, orgPD, spPD).A small shared helper performs the call and maps the result, so both sites stay one line.
Error mapping
On
*pe.UnknownSelectionKeysError, returnoauth.OAuth2Error{Code: oauth.InvalidRequest, Description: err.Error()}(the typed error'sError()already readsunknown credential_selection keys: a, b, sorted). This matches how the client already emits OAuth errors (e.g.UnsupportedGrantTypeat lines 275/286/358); the error propagates throughapi.go:797-799and renders as a400.Notes / non-issues
accessTokenRequestCacheKey(api.go:1034) hashes the whole request includingcredential_selection, so a typo'd request gets a distinct key, misses the cache, and reaches the client. 400s aren't cached (only successful tokens, line 808).handleAuthorizeRequestFromVerifierpassesnilcredentialSelection(openid4vp.go:321) — nothing to validate.pdResolverfetches a remote PD, keys are validated against that resolved PD — correct: a key with no matching field id there is meaningless.Testing
auth/client/iamtests:oauth.OAuth2ErrorwithInvalidRequestand a description naming all unknown keys; assert it surfaces as a 400 at the handler;Acceptance Criteria
ValidateSelectionKeysagainst their resolved in-scope PD(s) before building.400OAuthinvalid_requestnaming all offending keys.go build ./...andgo test ./auth/...pass.