Skip to content

fix: use @sirosfoundation/dc-api v0.5.0's authorization-request bridge - #534

Merged
masv3971 merged 2 commits into
SUNET:mainfrom
sirosfoundation:fix/dc-api-authz-request-bridge
Aug 3, 2026
Merged

fix: use @sirosfoundation/dc-api v0.5.0's authorization-request bridge#534
masv3971 merged 2 commits into
SUNET:mainfrom
sirosfoundation:fix/dc-api-authz-request-bridge

Conversation

@leifj

@leifj leifj commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #504 (the DC API polyfill integration, merged 2026-07-29). Live end-to-end testing against a real Android device (Chrome DC API + native mdoc credential presentation) surfaced a real bug in the native DC API path introduced there, plus two related bugs found in the same testing pass.

Bugs fixed

  1. Native DC API path never worked: the verifier passed the redirect-flow authorization_request URI (openid4vp://...?client_id=...&request_uri=...) directly as the DC API request value. That URI is neither a valid JWT (openid4vp-v1-signed needs one) nor a valid raw-params object (openid4vp-v1-unsigned needs that instead) - no protocol handler could parse it, so every native DC API attempt failed silently ("Your info wasn't found"). Fixed by bumping the vendored @sirosfoundation/dc-api bundle to v0.5.0 and wiring the native path through its new requestCredentialFromAuthorizationRequestURI() helper, which resolves a standard authorization request URI into whatever data shape the detected protocol actually needs (fetching request_uri for the JWT when required).
  2. /ui/metadata failed to parse when no wallets are configured: supported_wallets can arrive as JSON null (an unconfigured Go map marshals that way) - the schema had no fallback for it, so the whole response failed to parse and the page hung on its loading spinner before a user could ever select a credential.
  3. mdoc claim selection built the wrong DCQL constraint: the claim-selection form always built meta.vct_values, even for mso_mdoc credentials, which have no vct - the correct constraint there is meta.doctype_value (OpenID4VP 1.0 §6.4.1). Sending vct_values for an mdoc credential matches nothing on the wallet side.

Test plan

  • go build ./... - clean.
  • make test-js - 49/49 passing.
  • Verified end-to-end against a real Android device (Chrome DC API + native mdoc credential presentation).

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

Bumps the vendored dc-api.js bundle to v0.5.0 and wires the verifier's
native DC API path through the new requestCredentialFromAuthorizationRequestURI
helper instead of passing the redirect-flow authorization_request URI
directly as the DC API request value - that URI is neither a valid JWT
(openid4vp-v1-signed) nor a valid raw-params object (openid4vp-v1-unsigned),
so no protocol handler could ever parse it and every native DC API attempt
failed. The new helper resolves the authorization request into whatever
shape the detected protocol actually needs.

Also fixes two bugs found in the same testing pass, both required for the
above to actually reach a working state:
- supported_wallets can arrive as JSON null (an unconfigured Go map
  marshals that way) - the schema had no fallback for it, so the whole
  /ui/metadata response failed to parse and the page hung on its loading
  spinner before a user could ever select a credential.
- The claim-selection form always built DCQL meta.vct_values, even for
  mso_mdoc credentials, which have no vct - the correct constraint there
  is meta.doctype_value (OpenID4VP 1.0 6.4.1). Sending vct_values for an
  mdoc credential matches nothing on the wallet side.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NhWrEC7D4b3vxZ4wm3gDML
(cherry picked from commit 30c3e69)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes verifier-side W3C Digital Credentials API (DC API) integration issues discovered during Android end-to-end testing, improving native DC API request handling and DCQL query construction so wallet interactions succeed in real devices and edge configurations.

Changes:

  • Routes native DC API requests through requestCredentialFromAuthorizationRequestURI() to turn an openid4vp://... authorization request URI into the protocol-specific data shape expected by navigator.credentials.get().
  • Makes /ui/metadata parsing resilient when supported_wallets is null (e.g., no wallets configured).
  • Fixes mdoc (mso_mdoc) DCQL meta constraint generation to use meta.doctype_value instead of meta.vct_values.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
internal/verifier/staticembed/presentation-definition.js Adjusts metadata parsing defaults and corrects DCQL meta construction for mso_mdoc in the claim-selection flow; switches native request path to the new bridge helper.
internal/verifier/staticembed/dc-api.js Updates vendored dc-api bundle with helpers to resolve authorization request URIs into protocol-specific request data for native DC API calls.
internal/verifier/staticembed/dc-api-polyfill.js Re-exports the new authorization-request bridge function from the dc-api package for use by the verifier UI.
Suppressed comments (1)

internal/verifier/staticembed/dc-api.js:141

  • requestCredentialFromAuthorizationRequestURI() currently calls buildRequestData() without passing options.signal, so even if buildRequestData supports cancellation the fetch path can’t use the AbortController from the caller.
async function requestCredentialFromAuthorizationRequestURI(authorizationRequestUri, options) {
  const protocol = getBestProtocol(options?.protocolPreference);
  if (!protocol) return null;
  const data = await buildRequestData(protocol, authorizationRequestUri, { fetchFn: options?.fetchFn });
  return requestCredential(protocol, data, options);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/verifier/staticembed/dc-api.js
requestCredentialFromAuthorizationRequestURI's caller
(_tryNativeDCAPI in presentation-definition.js) creates an
AbortController and passes its signal specifically so the native DC
API attempt can be cancelled if the user picks another flow or
navigates away. The signal was silently dropped before it could do
anything:

- requestCredentialFromAuthorizationRequestURI forwarded only
  { fetchFn } to buildRequestData, not signal.
- buildRequestData never read options.signal in the first place.
- _fetchJwt's fetchImpl(requestUri) call had no way to pass a signal
  even if one had reached it.

A slow/hung request_uri fetch could therefore never be aborted, and
the UI would appear stuck even after the user cancelled.

Patched directly in the vendored bundle (internal/verifier/staticembed/
dc-api.js) rather than only in the @sirosfoundation/dc-api source,
since that's what's actually shipped by this PR; the same fix should
be upstreamed to dc-api separately so the next version bump doesn't
reintroduce it.

No JS test harness exists for internal/verifier/staticembed/ (make
test-js only covers internal/apigw/staticembed/tests/*.test.js), so
verified this by tracing the full call chain by hand rather than
adding new test infrastructure for this fix alone.
@leifj

leifj commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Fixed the AbortController.signal issue Copilot flagged (commit 6c9280d) — it's actually a real, live bug: `_tryNativeDCAPI` (presentation-definition.js) creates an `AbortController` specifically so a native DC API attempt can be cancelled, and passes `{ signal: abortController.signal }` into `requestCredentialFromAuthorizationRequestURI` expecting it to work.

The signal was silently dropped at all three points along the chain:

  • `requestCredentialFromAuthorizationRequestURI` only forwarded `{ fetchFn }` to `buildRequestData`, not `signal`.
  • `buildRequestData` never read `options.signal`.
  • `_fetchJwt`'s `fetchImpl(requestUri)` call had no way to pass a signal even if one arrived.

So a slow/hung `request_uri` fetch could never actually be aborted — the UI would appear stuck even after cancelling. Threaded `signal` through all three.

Patched directly in the vendored bundle since that's what's actually shipped here; flagging that the same fix should go upstream to `@sirosfoundation/dc-api` too so the next version bump doesn't reintroduce it.

No JS test harness exists for `internal/verifier/staticembed/` (`make test-js` only covers `internal/apigw/staticembed/tests/`), so I verified this by tracing the full call chain by hand rather than standing up new test infrastructure for one fix. Go build, vet, and make test all green (unaffected, this is a JS-only change).

@sonarqubecloud

sonarqubecloud Bot commented Aug 3, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Suppressed comments (2)

internal/verifier/staticembed/presentation-definition.js:511

  • The comment examples use openid4vp://...? but the verifier constructs authorization requests as an opaque URI (openid4vp:cb?...) (see pkg/openid4vp/context.go). Using the wrong shape in comments is misleading when debugging URL parsing behavior across browsers/wallets; consider updating the example to match the actual URI format (or use the more general openid4vp: form).
     * The authorization_request URI is the same openid4vp://...?client_id=...
     * &request_uri=... deep link used for QR/redirect - NOT itself a valid DC

internal/verifier/staticembed/presentation-definition.js:437

  • This block says “mso_mdoc credentials have no vct” but then reads this.credentialAttributes.vct for the doctype. Since the UI schema requires a vct string for all formats, it would be clearer to state that this field carries the VCT for SD-JWT credentials and the ISO doctype for mso_mdoc credentials, to avoid confusion for future maintainers.
        // mso_mdoc credentials have no vct - the DCQL equivalent constraint
        // is doctype_value (OpenID4VP 1.0 6.4.1), not vct_values. Sending
        // vct_values for an mdoc credential matches nothing on the wallet
        // side (no mdoc credential has a vct), so the request always comes
        // back empty.
        const meta = this.credentialAttributes.format === "mso_mdoc"
            ? { doctype_value: this.credentialAttributes.vct }
            : { vct_values: [this.credentialAttributes.vct] };

@masv3971
masv3971 merged commit 3833f12 into SUNET:main Aug 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants