tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 269c9bd63b4c43db525158f52a8468ac7c72918c
parent 14e523cf736d0c165daa2bf4165515f224f99e3d
Author: moz-wptsync-bot <wptsync@mozilla.com>
Date:   Thu,  4 Dec 2025 18:27:01 +0000

Bug 2003207 [wpt PR 56351] - Digital Credentials: add mDoc support and generic protocol selection mechanism, a=testonly

Automatic update from web-platform-tests
Digital Credentials: add mDoc support and generic protocol selection mechanism (#56351)

* Digital Credentials: add mDoc support and generic protocol selection mechanism

- Add org-iso-mdoc protocol support and mDocRequest interface
- Implement automatic protocol selection using DigitalCredential.userAgentAllowsProtocol
- Update protocol type definitions to separate OpenID and mDoc protocols
- Add DigitalCredentialStatic interface for userAgentAllowsProtocol method
- Update helper functions to use automatic protocol detection as defaults
- Tests now use makeGetOptions() and makeCreateOptions() without explicit protocols
--

wpt-commits: afce258ce1915a1a34522670d664031042e70f62
wpt-pr: 56351

Diffstat:
Mtesting/web-platform/tests/digital-credentials/create.tentative.https.html | 6+++---
Mtesting/web-platform/tests/digital-credentials/dc-types.ts | 39+++++++++++++++++++++++++++++++++++++--
Mtesting/web-platform/tests/digital-credentials/get.tentative.https.html | 6+++---
Mtesting/web-platform/tests/digital-credentials/support/helper.js | 46++++++++++++++++++++++++++++++++++++++++++----
Mtesting/web-platform/tests/digital-credentials/user-activation.https.html | 4++--
5 files changed, 87 insertions(+), 14 deletions(-)

diff --git a/testing/web-platform/tests/digital-credentials/create.tentative.https.html b/testing/web-platform/tests/digital-credentials/create.tentative.https.html @@ -182,7 +182,7 @@ promise_test(async (t) => { const abortController = new AbortController(); const { signal } = abortController; - const options = makeCreateOptions({ protocol: "openid4vci", signal }); + const options = makeCreateOptions({ signal }); await test_driver.bless("user activation"); const promise = promise_rejects_dom( t, @@ -200,7 +200,7 @@ abort: "after", action: "create", needsActivation: true, - options: makeCreateOptions({protocol: "openid4vci"}), + options: makeCreateOptions(), }); assert_equals(result.constructor, "DOMException"); assert_equals(result.name, "AbortError"); @@ -236,7 +236,7 @@ ]; for (const badValue of throwingValues) { - const options = makeCreateOptions({protocol: "openid4vci"}); + const options = makeCreateOptions(); options.digital.requests[0].data = badValue; await promise_rejects_js( diff --git a/testing/web-platform/tests/digital-credentials/dc-types.ts b/testing/web-platform/tests/digital-credentials/dc-types.ts @@ -1,5 +1,10 @@ -export type GetProtocol = "default" | "openid4vp-v1-unsigned" | "openid4vp-v1-signed" | "openid4vp-v1-multisigned"; -export type CreateProtocol = "default" | "openid4vci"; +export type OpenIDPresentationProtocol = + | "openid4vp-v1-unsigned" + | "openid4vp-v1-signed" + | "openid4vp-v1-multisigned"; +export type OpenIDIssuanceProtocol = "openid4vci"; +export type GetProtocol = OpenIDPresentationProtocol | "org-iso-mdoc"; +export type CreateProtocol = OpenIDIssuanceProtocol; export type CredentialMediationRequirement = | "conditional" @@ -8,6 +13,22 @@ export type CredentialMediationRequirement = | "silent"; /** + * @see https://www.iso.org/obp/ui#iso:std:iso-iec:ts:18013:-7:ed-2:v1:en + */ +export interface MobileDocumentRequest { + /** + * Information required for encryption, typically a base64-encoded string or JSON object as a string. + * The format should comply with the requirements specified in ISO/IEC TS 18013-7. + */ + readonly encryptionInfo: string; + /** + * The device request payload, usually a stringified JSON object containing the request details. + * This should follow the structure defined in ISO/IEC TS 18013-7 for device requests. + */ + readonly deviceRequest: string; +} + +/** * Configuration for makeGetOptions function */ export interface MakeGetOptionsConfig { @@ -135,3 +156,17 @@ export interface SendMessageData { action: IframeActionType; options?: CredentialRequestOptions; } + +/** + * The DigitalCredential interface + */ +export interface DigitalCredentialStatic { + /** + * Check if the user agent allows a specific protocol + */ + userAgentAllowsProtocol(protocol: string): boolean; +} + +declare global { + var DigitalCredential: DigitalCredentialStatic; +} diff --git a/testing/web-platform/tests/digital-credentials/get.tentative.https.html b/testing/web-platform/tests/digital-credentials/get.tentative.https.html @@ -206,7 +206,7 @@ promise_test(async (t) => { const abortController = new AbortController(); const { signal } = abortController; - const options = makeGetOptions({protocol: "openid4vp-v1-unsigned", signal}); + const options = makeGetOptions({signal}); await test_driver.bless("user activation"); const promise = promise_rejects_dom( t, @@ -224,7 +224,7 @@ abort: "after", action: "get", needsActivation: true, - options: makeGetOptions({protocol: "openid4vp-v1-signed"}), + options: makeGetOptions(), }); assert_equals(result.constructor, "DOMException"); assert_equals(result.name, "AbortError"); @@ -253,7 +253,7 @@ promise_test(async t => { ]; for (const badValue of throwingValues) { - const options = makeGetOptions({protocol: "openid4vp-v1-multisigned"}); + const options = makeGetOptions(); options.digital.requests[0].data = badValue; await promise_rejects_js( diff --git a/testing/web-platform/tests/digital-credentials/support/helper.js b/testing/web-platform/tests/digital-credentials/support/helper.js @@ -13,8 +13,27 @@ * @typedef {import('../dc-types').MakeGetOptionsConfig} MakeGetOptionsConfig * @typedef {import('../dc-types').MakeCreateOptionsConfig} MakeCreateOptionsConfig * @typedef {import('../dc-types').CredentialMediationRequirement} CredentialMediationRequirement + * @typedef {import('../dc-types').MobileDocumentRequest} MobileDocumentRequest */ +/** @type {GetProtocol[]} */ +const GET_PROTOCOLS = /** @type {const} */ ([ + "openid4vp-v1-unsigned", + "openid4vp-v1-signed", + "openid4vp-v1-multisigned", + "org-iso-mdoc", +]); + +/** @type {CreateProtocol[]} */ +const CREATE_PROTOCOLS = /** @type {const} */ (["openid4vci"]); + +const SUPPORTED_GET_PROTOCOL = GET_PROTOCOLS.find( + (protocol) => DigitalCredential.userAgentAllowsProtocol(protocol) +); +const SUPPORTED_CREATE_PROTOCOL = CREATE_PROTOCOLS.find( + (protocol) => DigitalCredential.userAgentAllowsProtocol(protocol) +); + /** * Internal helper to build the request array from validated input. * Assumes requestsInputArray is a non-empty array of strings. @@ -37,6 +56,7 @@ function _makeOptionsInternal(requestsInputArray, mediation, requestMapping, sig throw new Error(`Unknown request type within array: ${request}`); } } + /** @type {{ digital: { requests: any[] }, mediation: CredentialMediationRequirement, signal?: AbortSignal }} */ const result = { digital: { requests }, mediation }; if (signal !== undefined) { result.signal = signal; @@ -46,14 +66,13 @@ function _makeOptionsInternal(requestsInputArray, mediation, requestMapping, sig const allMappings = { get: { + "org-iso-mdoc": () => makeMDocRequest(), "openid4vp-v1-unsigned": () => makeOID4VPDict("openid4vp-v1-unsigned"), "openid4vp-v1-signed": () => makeOID4VPDict("openid4vp-v1-signed"), "openid4vp-v1-multisigned": () => makeOID4VPDict("openid4vp-v1-multisigned"), - "default": () => makeDigitalCredentialGetRequest(undefined, undefined), }, create: { "openid4vci": () => makeOID4VCIDict(), - "default": () => makeDigitalCredentialCreateRequest(), }, }; @@ -91,6 +110,7 @@ function _makeOptionsUnified(type, protocol, mediation, signal) { if (Array.isArray(protocol)) { if (protocol.length === 0) { // Handle empty array explicitly + /** @type {{ digital: { requests: any[] }, mediation: CredentialMediationRequirement, signal?: AbortSignal }} */ const result = { digital: { requests: [] }, mediation }; if (signal !== undefined) { result.signal = signal; @@ -102,6 +122,7 @@ function _makeOptionsUnified(type, protocol, mediation, signal) { } // 4. Handle invalid input types (neither string nor array) + /** @type {{ digital: { requests: any[] }, mediation: CredentialMediationRequirement, signal?: AbortSignal }} */ const result = { digital: { requests: [] }, mediation }; if (signal !== undefined) { result.signal = signal; @@ -116,7 +137,10 @@ function _makeOptionsUnified(type, protocol, mediation, signal) { * @returns {CredentialRequestOptions} */ export function makeGetOptions(config = {}) { - const { protocol = "default", mediation = "required", signal } = config; + const { protocol = SUPPORTED_GET_PROTOCOL, mediation = "required", signal } = config; + if (!protocol) { + throw new Error("No Protocol. Can't make get options."); + } return _makeOptionsUnified('get', protocol, mediation, signal); } @@ -127,7 +151,10 @@ export function makeGetOptions(config = {}) { * @returns {CredentialCreationOptions} */ export function makeCreateOptions(config = {}) { - const { protocol = "default", mediation = "required", signal } = config; + const { protocol = SUPPORTED_CREATE_PROTOCOL, mediation = "required", signal } = config; + if (!protocol) { + throw new Error("No protocol. Can't make create options."); + } return _makeOptionsUnified('create', protocol, mediation, signal); } @@ -181,6 +208,17 @@ function makeOID4VCIDict() { } /** + * Representation of an mDoc request. + * + * @returns {DigitalCredentialGetRequest} + **/ +function makeMDocRequest() { + return makeDigitalCredentialGetRequest("org-iso-mdoc", { + // Canonical example of an mDoc request coming soon. + }); +} + +/** * Sends a message to an iframe and return the response. * * @param {HTMLIFrameElement} iframe - The iframe element to send the message to. diff --git a/testing/web-platform/tests/digital-credentials/user-activation.https.html b/testing/web-platform/tests/digital-credentials/user-activation.https.html @@ -14,7 +14,7 @@ navigator.userActivation.isActive, "User activation should not be active" ); - const options = makeGetOptions({protocol: "openid4vp-v1-unsigned"}); + const options = makeGetOptions(); await promise_rejects_dom( t, "NotAllowedError", @@ -25,7 +25,7 @@ promise_test(async (t) => { await test_driver.bless(); const abort = new AbortController(); - const options = makeGetOptions({protocol: "openid4vp-v1-unsigned", signal: abort.signal}); + const options = makeGetOptions({ signal: abort.signal }); assert_true( navigator.userActivation.isActive, "User activation should be active after test_driver.bless()."