@sirosfoundation/dc-api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2026, SIROS Foundation
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # @sirosfoundation/dc-api
2
+
3
+ W3C Digital Credentials API utilities for OpenID4VP.
4
+
5
+ Zero-dependency, backend-agnostic library providing:
6
+
7
+ - **Protocol constants** — versioned OpenID4VP protocol identifiers per the W3C DC API spec
8
+ - **Feature detection** — check DC API availability and protocol support
9
+ - **Native DC API invocation** — call `navigator.credentials.get()` with proper parameters
10
+ - **Error helpers** — classify errors and generate user-friendly messages
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ npm install @sirosfoundation/dc-api
16
+ ```
17
+
18
+ A pre-built ESM bundle is also available at `dist/dc-api.bundle.js` for use in
19
+ importmaps or environments without a bundler.
20
+
21
+ ## Usage
22
+
23
+ ### Requesting credentials
24
+
25
+ ```ts
26
+ import {
27
+ isDCAPIAvailable,
28
+ getBestProtocol,
29
+ requestCredential,
30
+ isUserCancel,
31
+ } from '@sirosfoundation/dc-api';
32
+
33
+ // 1. Check if DC API + openid4vp is available
34
+ const protocol = getBestProtocol();
35
+
36
+ if (protocol) {
37
+ try {
38
+ // 2. Call the native DC API
39
+ const result = await requestCredential(protocol, {
40
+ request: signedJWT, // JAR for "openid4vp-v1-signed"
41
+ });
42
+
43
+ // 3. Submit result.data to your backend
44
+ await submitToBackend(result.data);
45
+ } catch (err) {
46
+ if (isUserCancel(err)) {
47
+ // User cancelled — show alternative flow
48
+ } else {
49
+ throw err;
50
+ }
51
+ }
52
+ } else {
53
+ // No DC API support — use QR code / redirect fallback
54
+ showQRCode();
55
+ }
56
+ ```
57
+
58
+ ### Protocol constants
59
+
60
+ ```ts
61
+ import { OID4VP_PROTOCOLS, isOID4VPProtocol } from '@sirosfoundation/dc-api';
62
+
63
+ // Use shared constants instead of hardcoding protocol strings
64
+ const supportedProtocols = [
65
+ OID4VP_PROTOCOLS.UNSIGNED,
66
+ OID4VP_PROTOCOLS.SIGNED,
67
+ OID4VP_PROTOCOLS.MULTISIGNED,
68
+ ];
69
+
70
+ // Type guard for filtering
71
+ const known = requests.filter(r => isOID4VPProtocol(r.protocol));
72
+ ```
73
+
74
+ ## API
75
+
76
+ ### Protocol Constants
77
+
78
+ ```ts
79
+ OID4VP_PROTOCOLS.UNSIGNED // "openid4vp-v1-unsigned"
80
+ OID4VP_PROTOCOLS.SIGNED // "openid4vp-v1-signed"
81
+ OID4VP_PROTOCOLS.MULTISIGNED // "openid4vp-v1-multisigned"
82
+ OID4VP_PROTOCOLS.LEGACY // "openid4vp"
83
+
84
+ OID4VP_SPEC_PROTOCOLS // [UNSIGNED, SIGNED, MULTISIGNED]
85
+ OID4VP_ALL_PROTOCOLS // [UNSIGNED, SIGNED, MULTISIGNED, LEGACY]
86
+ isOID4VPProtocol(value) // Type guard — true for any known protocol
87
+ ```
88
+
89
+ ### Detection
90
+
91
+ | Function | Description |
92
+ |---|---|
93
+ | `isDCAPIAvailable()` | `true` when `typeof DigitalCredential !== "undefined"` |
94
+ | `isProtocolAllowed(protocol)` | Delegates to `DigitalCredential.userAgentAllowsProtocol()` |
95
+ | `getBestProtocol(preference?)` | Returns the first allowed protocol from a preference-ordered list (default: signed > multisigned > unsigned) |
96
+
97
+ ### Request
98
+
99
+ | Function | Description |
100
+ |---|---|
101
+ | `requestCredential(protocol, data, options?)` | Calls `navigator.credentials.get({digital: {requests: [{protocol, data}]}})`, returns `{ protocol, data }` |
102
+
103
+ `options.signal` accepts an `AbortSignal` for cancellation.
104
+
105
+ ### Error Helpers
106
+
107
+ | Function | Description |
108
+ |---|---|
109
+ | `getUserFriendlyErrorMessage(error)` | Returns a human-readable message for DC API errors |
110
+ | `isUserCancel(error)` | `true` for `NotAllowedError` or `AbortError` |
111
+ | `isProtocolUnsupported(error)` | `true` for `NotSupportedError` |
112
+ | `ERROR_MESSAGES` | Map of DOMException names to user-facing strings |
113
+
114
+ ## Design Principles
115
+
116
+ - **Zero dependencies** — no runtime dependencies
117
+ - **Backend-agnostic** — no knowledge of specific verifier or wallet endpoints
118
+ - **Transport-agnostic** — no QR codes, redirects, or SSE; those belong in the consumer
119
+ - **Spec-aligned** — uses the W3C DC API spec's detection and invocation patterns exactly
120
+
121
+ ## References
122
+
123
+ - [W3C Digital Credentials API](https://w3c-fedid.github.io/digital-credentials/)
124
+ - [OpenID4VP (DC API profile)](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html)
125
+
126
+ ## License
127
+
128
+ BSD-2-Clause
@@ -0,0 +1,106 @@
1
+ // src/protocols.ts
2
+ const OID4VP_PROTOCOLS = {
3
+ /** Unsigned request — client_id derived from web-origin */
4
+ UNSIGNED: "openid4vp-v1-unsigned",
5
+ /** Signed request — JAR with single JWS compact serialization */
6
+ SIGNED: "openid4vp-v1-signed",
7
+ /** Multi-signed request — JWS JSON serialization */
8
+ MULTISIGNED: "openid4vp-v1-multisigned",
9
+ /** Legacy protocol string (pre-spec, used by some implementations) */
10
+ LEGACY: "openid4vp"
11
+ };
12
+ const OID4VP_SPEC_PROTOCOLS = [
13
+ OID4VP_PROTOCOLS.UNSIGNED,
14
+ OID4VP_PROTOCOLS.SIGNED,
15
+ OID4VP_PROTOCOLS.MULTISIGNED
16
+ ];
17
+ const OID4VP_ALL_PROTOCOLS = [
18
+ ...OID4VP_SPEC_PROTOCOLS,
19
+ OID4VP_PROTOCOLS.LEGACY
20
+ ];
21
+
22
+ // src/detect.ts
23
+ function isDCAPIAvailable() {
24
+ return typeof DigitalCredential !== "undefined";
25
+ }
26
+ function isProtocolAllowed(protocol) {
27
+ if (typeof DigitalCredential === "undefined") return false;
28
+ if (typeof DigitalCredential.userAgentAllowsProtocol !== "function") return false;
29
+ return DigitalCredential.userAgentAllowsProtocol(protocol);
30
+ }
31
+ const DEFAULT_PREFERENCE = [
32
+ OID4VP_PROTOCOLS.SIGNED,
33
+ OID4VP_PROTOCOLS.MULTISIGNED,
34
+ OID4VP_PROTOCOLS.UNSIGNED
35
+ ];
36
+ function getBestProtocol(preference) {
37
+ const candidates = preference ?? DEFAULT_PREFERENCE;
38
+ for (const proto of candidates) {
39
+ if (isProtocolAllowed(proto)) return proto;
40
+ }
41
+ return null;
42
+ }
43
+
44
+ // src/request.ts
45
+ async function requestCredential(protocol, data, options) {
46
+ const dcOptions = {
47
+ digital: {
48
+ requests: [{
49
+ protocol,
50
+ data
51
+ }]
52
+ }
53
+ };
54
+ if (options?.signal) {
55
+ dcOptions.signal = options.signal;
56
+ }
57
+ const credential = await navigator.credentials.get(dcOptions);
58
+ if (!credential) {
59
+ throw new DOMException("No credential received", "NotAllowedError");
60
+ }
61
+ return normalizeCredential(credential, protocol);
62
+ }
63
+ function normalizeCredential(credential, fallbackProtocol) {
64
+ const dc = credential;
65
+ return {
66
+ protocol: dc.protocol ?? fallbackProtocol,
67
+ data: dc.data ?? credential
68
+ };
69
+ }
70
+
71
+ // src/errors.ts
72
+ const ERROR_MESSAGES = {
73
+ NotAllowedError: "You denied the credential request or no wallet is available.",
74
+ NotSupportedError: "Your browser or wallet does not support this credential type.",
75
+ SecurityError: "Security error \u2014 ensure you are on HTTPS.",
76
+ AbortError: "The request was cancelled or timed out.",
77
+ InvalidStateError: "A credential request is already in progress.",
78
+ TypeError: "The credential request data is malformed."
79
+ };
80
+ const DEFAULT_MESSAGE = "An unexpected error occurred. Please try again.";
81
+ function getUserFriendlyErrorMessage(error) {
82
+ if (error instanceof DOMException) {
83
+ return ERROR_MESSAGES[error.name] ?? DEFAULT_MESSAGE;
84
+ }
85
+ if (error instanceof Error) {
86
+ return ERROR_MESSAGES[error.name] ?? DEFAULT_MESSAGE;
87
+ }
88
+ return DEFAULT_MESSAGE;
89
+ }
90
+ function isUserCancel(error) {
91
+ return error instanceof DOMException && (error.name === "NotAllowedError" || error.name === "AbortError");
92
+ }
93
+ function isProtocolUnsupported(error) {
94
+ return error instanceof DOMException && error.name === "NotSupportedError";
95
+ }
96
+ export {
97
+ ERROR_MESSAGES,
98
+ OID4VP_PROTOCOLS,
99
+ getBestProtocol,
100
+ getUserFriendlyErrorMessage,
101
+ isDCAPIAvailable,
102
+ isProtocolAllowed,
103
+ isProtocolUnsupported,
104
+ isUserCancel,
105
+ requestCredential
106
+ };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * DC API feature detection.
3
+ *
4
+ * (a) isDCAPIAvailable() — checks typeof DigitalCredential !== "undefined"
5
+ * Per spec §2.1.
6
+ *
7
+ * (b) isProtocolAllowed(protocol) — calls DigitalCredential.userAgentAllowsProtocol()
8
+ * Per spec §2.2, §7.7.3. Returns false safely for unknown protocols.
9
+ *
10
+ * (c) getBestProtocol(preference?) — finds the best supported protocol
11
+ * from a preference-ordered list.
12
+ */
13
+ import { type OID4VPProtocol } from './protocols.js';
14
+ /**
15
+ * Check if the W3C Digital Credentials API is available in this browser.
16
+ *
17
+ * Returns true when:
18
+ * - Chrome 141+ (native)
19
+ * - Safari/iOS 26+ (native, but may only support org-iso-mdoc)
20
+ * - Any browser with wallet-companion installed (shims DigitalCredential)
21
+ */
22
+ export declare function isDCAPIAvailable(): boolean;
23
+ /**
24
+ * Check if the browser allows a specific protocol for digital credentials.
25
+ *
26
+ * Uses the static method DigitalCredential.userAgentAllowsProtocol()
27
+ * defined in the DC API spec §7.7.3.
28
+ *
29
+ * Returns false safely when:
30
+ * - DigitalCredential is not defined
31
+ * - userAgentAllowsProtocol is not implemented
32
+ * - The protocol is unknown to the browser
33
+ *
34
+ * NOTE: This method reflects browser capability, NOT wallet availability.
35
+ * A true result means the browser will accept the protocol in a
36
+ * navigator.credentials.get() call, but a wallet must still be present
37
+ * to fulfill the request.
38
+ */
39
+ export declare function isProtocolAllowed(protocol: string): boolean;
40
+ /**
41
+ * Find the best OpenID4VP protocol supported by this browser.
42
+ *
43
+ * @param preference Ordered list of protocols to try (most preferred first).
44
+ * Defaults to [SIGNED, MULTISIGNED, UNSIGNED].
45
+ * @returns The first allowed protocol, or null if none are supported.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * const protocol = getBestProtocol();
50
+ * if (protocol) {
51
+ * const result = await requestCredential(protocol, requestData);
52
+ * } else {
53
+ * // No DC API support for OpenID4VP — use QR/redirect fallback
54
+ * }
55
+ * ```
56
+ */
57
+ export declare function getBestProtocol(preference?: readonly string[]): OID4VPProtocol | null;
package/dist/detect.js ADDED
@@ -0,0 +1,81 @@
1
+ /**
2
+ * DC API feature detection.
3
+ *
4
+ * (a) isDCAPIAvailable() — checks typeof DigitalCredential !== "undefined"
5
+ * Per spec §2.1.
6
+ *
7
+ * (b) isProtocolAllowed(protocol) — calls DigitalCredential.userAgentAllowsProtocol()
8
+ * Per spec §2.2, §7.7.3. Returns false safely for unknown protocols.
9
+ *
10
+ * (c) getBestProtocol(preference?) — finds the best supported protocol
11
+ * from a preference-ordered list.
12
+ */
13
+ import { OID4VP_PROTOCOLS } from './protocols.js';
14
+ /**
15
+ * Check if the W3C Digital Credentials API is available in this browser.
16
+ *
17
+ * Returns true when:
18
+ * - Chrome 141+ (native)
19
+ * - Safari/iOS 26+ (native, but may only support org-iso-mdoc)
20
+ * - Any browser with wallet-companion installed (shims DigitalCredential)
21
+ */
22
+ export function isDCAPIAvailable() {
23
+ return typeof DigitalCredential !== 'undefined';
24
+ }
25
+ /**
26
+ * Check if the browser allows a specific protocol for digital credentials.
27
+ *
28
+ * Uses the static method DigitalCredential.userAgentAllowsProtocol()
29
+ * defined in the DC API spec §7.7.3.
30
+ *
31
+ * Returns false safely when:
32
+ * - DigitalCredential is not defined
33
+ * - userAgentAllowsProtocol is not implemented
34
+ * - The protocol is unknown to the browser
35
+ *
36
+ * NOTE: This method reflects browser capability, NOT wallet availability.
37
+ * A true result means the browser will accept the protocol in a
38
+ * navigator.credentials.get() call, but a wallet must still be present
39
+ * to fulfill the request.
40
+ */
41
+ export function isProtocolAllowed(protocol) {
42
+ if (typeof DigitalCredential === 'undefined')
43
+ return false;
44
+ if (typeof DigitalCredential.userAgentAllowsProtocol !== 'function')
45
+ return false;
46
+ return DigitalCredential.userAgentAllowsProtocol(protocol);
47
+ }
48
+ /**
49
+ * Default protocol preference order.
50
+ * Signed first (most verifiers use JAR), then multisigned, then unsigned.
51
+ */
52
+ const DEFAULT_PREFERENCE = [
53
+ OID4VP_PROTOCOLS.SIGNED,
54
+ OID4VP_PROTOCOLS.MULTISIGNED,
55
+ OID4VP_PROTOCOLS.UNSIGNED,
56
+ ];
57
+ /**
58
+ * Find the best OpenID4VP protocol supported by this browser.
59
+ *
60
+ * @param preference Ordered list of protocols to try (most preferred first).
61
+ * Defaults to [SIGNED, MULTISIGNED, UNSIGNED].
62
+ * @returns The first allowed protocol, or null if none are supported.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * const protocol = getBestProtocol();
67
+ * if (protocol) {
68
+ * const result = await requestCredential(protocol, requestData);
69
+ * } else {
70
+ * // No DC API support for OpenID4VP — use QR/redirect fallback
71
+ * }
72
+ * ```
73
+ */
74
+ export function getBestProtocol(preference) {
75
+ const candidates = preference ?? DEFAULT_PREFERENCE;
76
+ for (const proto of candidates) {
77
+ if (isProtocolAllowed(proto))
78
+ return proto;
79
+ }
80
+ return null;
81
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Error classification and user-friendly messages for DC API errors.
3
+ */
4
+ /**
5
+ * User-friendly error messages keyed by DOMException name.
6
+ */
7
+ export declare const ERROR_MESSAGES: Record<string, string>;
8
+ /**
9
+ * Get a user-friendly message for a DC API error.
10
+ */
11
+ export declare function getUserFriendlyErrorMessage(error: unknown): string;
12
+ /**
13
+ * Check if the error represents a user cancellation (not a failure).
14
+ */
15
+ export declare function isUserCancel(error: unknown): boolean;
16
+ /**
17
+ * Check if the error means the protocol is not supported by this browser.
18
+ */
19
+ export declare function isProtocolUnsupported(error: unknown): boolean;
package/dist/errors.js ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Error classification and user-friendly messages for DC API errors.
3
+ */
4
+ /**
5
+ * User-friendly error messages keyed by DOMException name.
6
+ */
7
+ export const ERROR_MESSAGES = {
8
+ NotAllowedError: 'You denied the credential request or no wallet is available.',
9
+ NotSupportedError: 'Your browser or wallet does not support this credential type.',
10
+ SecurityError: 'Security error — ensure you are on HTTPS.',
11
+ AbortError: 'The request was cancelled or timed out.',
12
+ InvalidStateError: 'A credential request is already in progress.',
13
+ TypeError: 'The credential request data is malformed.',
14
+ };
15
+ const DEFAULT_MESSAGE = 'An unexpected error occurred. Please try again.';
16
+ /**
17
+ * Get a user-friendly message for a DC API error.
18
+ */
19
+ export function getUserFriendlyErrorMessage(error) {
20
+ if (error instanceof DOMException) {
21
+ return ERROR_MESSAGES[error.name] ?? DEFAULT_MESSAGE;
22
+ }
23
+ if (error instanceof Error) {
24
+ return ERROR_MESSAGES[error.name] ?? DEFAULT_MESSAGE;
25
+ }
26
+ return DEFAULT_MESSAGE;
27
+ }
28
+ /**
29
+ * Check if the error represents a user cancellation (not a failure).
30
+ */
31
+ export function isUserCancel(error) {
32
+ return (error instanceof DOMException &&
33
+ (error.name === 'NotAllowedError' || error.name === 'AbortError'));
34
+ }
35
+ /**
36
+ * Check if the error means the protocol is not supported by this browser.
37
+ */
38
+ export function isProtocolUnsupported(error) {
39
+ return error instanceof DOMException && error.name === 'NotSupportedError';
40
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @sirosfoundation/dc-api
3
+ *
4
+ * W3C Digital Credentials API utilities for OpenID4VP.
5
+ *
6
+ * Backend-agnostic library providing:
7
+ * - Protocol constants (versioned OpenID4VP protocol identifiers)
8
+ * - DC API feature detection (API availability + protocol support)
9
+ * - Native DC API invocation with response normalization
10
+ * - Error classification helpers
11
+ *
12
+ * Does NOT include:
13
+ * - Fallback transports (QR, redirect, SSE, polling) — those are verifier-specific
14
+ * - Wallet popup management — that's the wallet-companion's job
15
+ * - Backend endpoint URLs — pass your own
16
+ *
17
+ * References:
18
+ * - W3C Digital Credentials API: https://w3c-fedid.github.io/digital-credentials/
19
+ * - OpenID4VP (DC API profile): https://openid.net/specs/openid-4-verifiable-presentations-1_0.html
20
+ */
21
+ export { OID4VP_PROTOCOLS, type OID4VPProtocol } from './protocols.js';
22
+ export { isDCAPIAvailable, isProtocolAllowed, getBestProtocol, } from './detect.js';
23
+ export { requestCredential, type DigitalCredentialResponse, type RequestCredentialOptions, } from './request.js';
24
+ export { getUserFriendlyErrorMessage, isUserCancel, isProtocolUnsupported, ERROR_MESSAGES, } from './errors.js';
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @sirosfoundation/dc-api
3
+ *
4
+ * W3C Digital Credentials API utilities for OpenID4VP.
5
+ *
6
+ * Backend-agnostic library providing:
7
+ * - Protocol constants (versioned OpenID4VP protocol identifiers)
8
+ * - DC API feature detection (API availability + protocol support)
9
+ * - Native DC API invocation with response normalization
10
+ * - Error classification helpers
11
+ *
12
+ * Does NOT include:
13
+ * - Fallback transports (QR, redirect, SSE, polling) — those are verifier-specific
14
+ * - Wallet popup management — that's the wallet-companion's job
15
+ * - Backend endpoint URLs — pass your own
16
+ *
17
+ * References:
18
+ * - W3C Digital Credentials API: https://w3c-fedid.github.io/digital-credentials/
19
+ * - OpenID4VP (DC API profile): https://openid.net/specs/openid-4-verifiable-presentations-1_0.html
20
+ */
21
+ export { OID4VP_PROTOCOLS } from './protocols.js';
22
+ export { isDCAPIAvailable, isProtocolAllowed, getBestProtocol, } from './detect.js';
23
+ export { requestCredential, } from './request.js';
24
+ export { getUserFriendlyErrorMessage, isUserCancel, isProtocolUnsupported, ERROR_MESSAGES, } from './errors.js';
@@ -0,0 +1,34 @@
1
+ /**
2
+ * W3C DigitalCredentialPresentationProtocol enum values for OpenID4VP.
3
+ *
4
+ * Per the DC API spec (§5, §7.8.2), protocol strings are versioned:
5
+ * "openid4vp-v1-unsigned" — unsigned request (client_id from origin)
6
+ * "openid4vp-v1-signed" — signed request (JAR with single signature)
7
+ * "openid4vp-v1-multisigned" — multi-signed request (JWS JSON Serialization)
8
+ *
9
+ * The legacy "openid4vp" string is NOT a valid DC API protocol identifier
10
+ * but is included for compatibility with older implementations.
11
+ */
12
+ export declare const OID4VP_PROTOCOLS: {
13
+ /** Unsigned request — client_id derived from web-origin */
14
+ readonly UNSIGNED: "openid4vp-v1-unsigned";
15
+ /** Signed request — JAR with single JWS compact serialization */
16
+ readonly SIGNED: "openid4vp-v1-signed";
17
+ /** Multi-signed request — JWS JSON serialization */
18
+ readonly MULTISIGNED: "openid4vp-v1-multisigned";
19
+ /** Legacy protocol string (pre-spec, used by some implementations) */
20
+ readonly LEGACY: "openid4vp";
21
+ };
22
+ export type OID4VPProtocol = (typeof OID4VP_PROTOCOLS)[keyof typeof OID4VP_PROTOCOLS];
23
+ /**
24
+ * All spec-defined OpenID4VP protocol identifiers (excludes legacy).
25
+ */
26
+ export declare const OID4VP_SPEC_PROTOCOLS: readonly OID4VPProtocol[];
27
+ /**
28
+ * All protocol identifiers including legacy.
29
+ */
30
+ export declare const OID4VP_ALL_PROTOCOLS: readonly OID4VPProtocol[];
31
+ /**
32
+ * Check if a string is a known OpenID4VP protocol identifier.
33
+ */
34
+ export declare function isOID4VPProtocol(value: unknown): value is OID4VPProtocol;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * W3C DigitalCredentialPresentationProtocol enum values for OpenID4VP.
3
+ *
4
+ * Per the DC API spec (§5, §7.8.2), protocol strings are versioned:
5
+ * "openid4vp-v1-unsigned" — unsigned request (client_id from origin)
6
+ * "openid4vp-v1-signed" — signed request (JAR with single signature)
7
+ * "openid4vp-v1-multisigned" — multi-signed request (JWS JSON Serialization)
8
+ *
9
+ * The legacy "openid4vp" string is NOT a valid DC API protocol identifier
10
+ * but is included for compatibility with older implementations.
11
+ */
12
+ export const OID4VP_PROTOCOLS = {
13
+ /** Unsigned request — client_id derived from web-origin */
14
+ UNSIGNED: 'openid4vp-v1-unsigned',
15
+ /** Signed request — JAR with single JWS compact serialization */
16
+ SIGNED: 'openid4vp-v1-signed',
17
+ /** Multi-signed request — JWS JSON serialization */
18
+ MULTISIGNED: 'openid4vp-v1-multisigned',
19
+ /** Legacy protocol string (pre-spec, used by some implementations) */
20
+ LEGACY: 'openid4vp',
21
+ };
22
+ /**
23
+ * All spec-defined OpenID4VP protocol identifiers (excludes legacy).
24
+ */
25
+ export const OID4VP_SPEC_PROTOCOLS = [
26
+ OID4VP_PROTOCOLS.UNSIGNED,
27
+ OID4VP_PROTOCOLS.SIGNED,
28
+ OID4VP_PROTOCOLS.MULTISIGNED,
29
+ ];
30
+ /**
31
+ * All protocol identifiers including legacy.
32
+ */
33
+ export const OID4VP_ALL_PROTOCOLS = [
34
+ ...OID4VP_SPEC_PROTOCOLS,
35
+ OID4VP_PROTOCOLS.LEGACY,
36
+ ];
37
+ /**
38
+ * Check if a string is a known OpenID4VP protocol identifier.
39
+ */
40
+ export function isOID4VPProtocol(value) {
41
+ return OID4VP_ALL_PROTOCOLS.includes(value);
42
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Native DC API credential request.
3
+ *
4
+ * Calls navigator.credentials.get() with the DC API options:
5
+ * { digital: { requests: [{ protocol, data }] } }
6
+ *
7
+ * This module is backend-agnostic — it only handles the browser API call
8
+ * and response normalization. Fallback transports (QR, redirect, SSE)
9
+ * are the consumer's responsibility.
10
+ */
11
+ import type { OID4VPProtocol } from './protocols.js';
12
+ /**
13
+ * Normalized response from a DC API credential request.
14
+ */
15
+ export interface DigitalCredentialResponse {
16
+ /** The protocol used for this credential exchange */
17
+ protocol: string;
18
+ /** The credential response data (opaque to this library) */
19
+ data: unknown;
20
+ }
21
+ /**
22
+ * Options for requestCredential().
23
+ */
24
+ export interface RequestCredentialOptions {
25
+ /** AbortSignal for cancellation */
26
+ signal?: AbortSignal;
27
+ }
28
+ /**
29
+ * Request a credential via the native DC API.
30
+ *
31
+ * @param protocol The protocol identifier (e.g. "openid4vp-v1-signed")
32
+ * @param data The request data object. For JAR: { request: "<JWT>" }.
33
+ * For unsigned: the individual OID4VP parameters as an object.
34
+ * @param options Optional AbortSignal for cancellation.
35
+ * @returns A normalized { protocol, data } response.
36
+ * @throws DOMException with name "NotAllowedError" if user cancels
37
+ * @throws DOMException with name "NotSupportedError" if protocol unsupported
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * // Signed request (JAR)
42
+ * const result = await requestCredential("openid4vp-v1-signed", {
43
+ * request: signedJWT,
44
+ * });
45
+ *
46
+ * // Unsigned request
47
+ * const result = await requestCredential("openid4vp-v1-unsigned", {
48
+ * response_type: "vp_token",
49
+ * nonce: "abc123",
50
+ * dcql_query: { credentials: [...] },
51
+ * });
52
+ * ```
53
+ */
54
+ export declare function requestCredential(protocol: OID4VPProtocol | string, data: object, options?: RequestCredentialOptions): Promise<DigitalCredentialResponse>;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Native DC API credential request.
3
+ *
4
+ * Calls navigator.credentials.get() with the DC API options:
5
+ * { digital: { requests: [{ protocol, data }] } }
6
+ *
7
+ * This module is backend-agnostic — it only handles the browser API call
8
+ * and response normalization. Fallback transports (QR, redirect, SSE)
9
+ * are the consumer's responsibility.
10
+ */
11
+ /**
12
+ * Request a credential via the native DC API.
13
+ *
14
+ * @param protocol The protocol identifier (e.g. "openid4vp-v1-signed")
15
+ * @param data The request data object. For JAR: { request: "<JWT>" }.
16
+ * For unsigned: the individual OID4VP parameters as an object.
17
+ * @param options Optional AbortSignal for cancellation.
18
+ * @returns A normalized { protocol, data } response.
19
+ * @throws DOMException with name "NotAllowedError" if user cancels
20
+ * @throws DOMException with name "NotSupportedError" if protocol unsupported
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * // Signed request (JAR)
25
+ * const result = await requestCredential("openid4vp-v1-signed", {
26
+ * request: signedJWT,
27
+ * });
28
+ *
29
+ * // Unsigned request
30
+ * const result = await requestCredential("openid4vp-v1-unsigned", {
31
+ * response_type: "vp_token",
32
+ * nonce: "abc123",
33
+ * dcql_query: { credentials: [...] },
34
+ * });
35
+ * ```
36
+ */
37
+ export async function requestCredential(protocol, data, options) {
38
+ const dcOptions = {
39
+ digital: {
40
+ requests: [{
41
+ protocol,
42
+ data,
43
+ }],
44
+ },
45
+ };
46
+ if (options?.signal) {
47
+ dcOptions.signal = options.signal;
48
+ }
49
+ const credential = await navigator.credentials.get(dcOptions);
50
+ if (!credential) {
51
+ throw new DOMException('No credential received', 'NotAllowedError');
52
+ }
53
+ return normalizeCredential(credential, protocol);
54
+ }
55
+ /**
56
+ * Normalize a Credential into our standard response shape.
57
+ */
58
+ function normalizeCredential(credential, fallbackProtocol) {
59
+ // The DC API returns a DigitalCredential with .protocol and .data
60
+ const dc = credential;
61
+ return {
62
+ protocol: dc.protocol ?? fallbackProtocol,
63
+ data: dc.data ?? credential,
64
+ };
65
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@sirosfoundation/dc-api",
3
+ "version": "0.1.0",
4
+ "description": "W3C Digital Credentials API utilities for OpenID4VP — detection, protocol constants, and native DC API invocation",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./bundle": {
14
+ "import": "./dist/dc-api.bundle.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc && npm run bundle",
22
+ "bundle": "esbuild src/index.ts --bundle --format=esm --target=es2022 --outfile=dist/dc-api.bundle.js && sed -i 's/^var /const /g' dist/dc-api.bundle.js",
23
+ "prepare": "npm run build",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "lint": "tsc --noEmit"
27
+ },
28
+ "devDependencies": {
29
+ "esbuild": "^0.28.1",
30
+ "typescript": "^5.7.0",
31
+ "vitest": "^3.2.0"
32
+ },
33
+ "license": "BSD-2-Clause",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/sirosfoundation/dc-api.git"
37
+ },
38
+ "keywords": [
39
+ "digital-credentials",
40
+ "dc-api",
41
+ "openid4vp",
42
+ "verifiable-credentials",
43
+ "w3c"
44
+ ]
45
+ }