@mattrglobal/verifier-sdk-web 2.2.1-unstable.9 → 2.3.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/README.md +94 -22
- package/dist/lib/verifier-js-no-deps.cjs.js +140 -60
- package/dist/lib/verifier-js-no-deps.cjs.js.map +1 -1
- package/dist/lib/verifier-js.cjs.js +140 -60
- package/dist/lib/verifier-js.cjs.js.map +1 -1
- package/dist/typings/verifier/types/credential-presentation.d.ts +21 -0
- package/dist/typings/verifier/types/verifier-web-sdk.d.ts +21 -5
- package/dist/verifier-js.development.js +140 -60
- package/dist/verifier-js.development.js.map +1 -1
- package/dist/verifier-js.production.esm.js +3 -3
- package/dist/verifier-js.production.esm.js.map +1 -1
- package/dist/verifier-js.production.js +3 -3
- package/dist/verifier-js.production.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
- [Define wallet identifiers](#define-wallet-identifiers)
|
|
22
22
|
- [Configure redirectUri](#configure-redirecturi)
|
|
23
23
|
- [Correlate sessions with your own records using state](#correlate-sessions-with-your-own-records-using-state)
|
|
24
|
+
- [React to presentation requests using onPresentationRequest](#react-to-presentation-requests-using-onpresentationrequest)
|
|
24
25
|
- [Request credentials using the DC API](#request-credentials-using-the-dc-api)
|
|
25
26
|
- [Request credentials examples](#request-credentials-examples)
|
|
26
27
|
- [Request credentials with automatic flow selection](#request-credentials-with-automatic-flow-selection)
|
|
@@ -39,12 +40,10 @@ Request or download the
|
|
|
39
40
|
## Overview
|
|
40
41
|
|
|
41
42
|
The Verifier Web SDK is a powerful tool for integrating online credential verification capabilities into your web
|
|
42
|
-
applications. It enables secure and efficient verification of [mDocs](https://learn.mattr.global/docs/mdocs),
|
|
43
|
-
both [same-device](https://learn.mattr.global/docs/verification/
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
verification workflows. The SDK leverages the MATTR VII platform to handle credential presentation and verification
|
|
47
|
-
processes.
|
|
43
|
+
applications. It enables secure and efficient verification of [mDocs](https://learn.mattr.global/docs/concepts/mdocs),
|
|
44
|
+
supporting both [same-device](https://learn.mattr.global/docs/verification/remote-overview#verification-flows) and
|
|
45
|
+
[cross-device](https://learn.mattr.global/docs/verification/remote-overview#verification-flows) verification workflows.
|
|
46
|
+
The SDK leverages the MATTR VII platform to handle credential presentation and verification processes.
|
|
48
47
|
|
|
49
48
|
## Features
|
|
50
49
|
|
|
@@ -60,8 +59,8 @@ processes.
|
|
|
60
59
|
|
|
61
60
|
## How to get access to the MATTR Pi Verifier Web SDK
|
|
62
61
|
|
|
63
|
-
Refer to our [SDK Docs landing page](https://learn.mattr.global/
|
|
64
|
-
gain access to any of our SDKs.
|
|
62
|
+
Refer to our [SDK Docs landing page](https://learn.mattr.global/docs/resources/get-started) for step-by-step
|
|
63
|
+
instructions to gain access to any of our SDKs.
|
|
65
64
|
|
|
66
65
|
> Please [reach out](mailto:dev-support@mattr.global) if you need any assistance.
|
|
67
66
|
|
|
@@ -115,6 +114,8 @@ the following configurations and settings:
|
|
|
115
114
|
same-device flows).
|
|
116
115
|
- Optionally pass a `state` string to correlate the session with a record in your own system (see
|
|
117
116
|
[Correlate sessions with your own records using state](#correlate-sessions-with-your-own-records-using-state)).
|
|
117
|
+
- Optionally pass an `onPresentationRequest` hook to run your own logic each time a presentation request is issued (see
|
|
118
|
+
[React to presentation requests using onPresentationRequest](#react-to-presentation-requests-using-onpresentationrequest)).
|
|
118
119
|
|
|
119
120
|
## Initialize the SDK
|
|
120
121
|
|
|
@@ -126,9 +127,9 @@ MATTRVerifierSDK.initialize({ apiBaseUrl, applicationId });
|
|
|
126
127
|
|
|
127
128
|
- `apiBaseUrl` (required): URL of the MATTR VII verifier tenant.
|
|
128
129
|
- `applicationId` (required): Unique identifier of the verifier application. This must match the
|
|
129
|
-
[`id`](https://learn.mattr.global/api-reference/
|
|
130
|
-
|
|
131
|
-
[creating a Verifier application](https://learn.mattr.global/api-reference/
|
|
130
|
+
[`id`](https://learn.mattr.global/docs/api-reference/platform/verifier-applications/postVerifierApplication) parameter
|
|
131
|
+
in the response returned when
|
|
132
|
+
[creating a Verifier application](https://learn.mattr.global/docs/api-reference/platform/verifier-applications/postVerifierApplication)
|
|
132
133
|
on the MATTR VII verifier tenant.
|
|
133
134
|
|
|
134
135
|
## Prepare a credential query
|
|
@@ -227,13 +228,13 @@ This behavior can be explicitly overridden by specifying the desired mode in the
|
|
|
227
228
|
|
|
228
229
|
You can define an identifier of a specific wallet you want to invoke with this verification request. The identifier
|
|
229
230
|
defined by the SDK in the credential request must match one of the identifiers of a
|
|
230
|
-
[wallet provider](https://learn.mattr.global/api-reference/
|
|
231
|
-
|
|
231
|
+
[wallet provider](https://learn.mattr.global/docs/api-reference/platform/wallet-providers/postWalletProvider) created on
|
|
232
|
+
the MATTR VII verifier tenant.
|
|
232
233
|
|
|
233
234
|
- If an identifier is provided and matches the `id` of one of the objects in the `walletProviders` array, the verifier
|
|
234
235
|
tenant will invoke that specific wallet using its corresponding
|
|
235
|
-
[`authorizationEndpoint`](https://learn.mattr.global/api-reference/
|
|
236
|
-
- If an identifier is provided and does not match the `id` of any of the objects in the `walletProviders array
|
|
236
|
+
[`authorizationEndpoint`](https://learn.mattr.global/docs/api-reference/platform/wallet-providers/postWalletProvider).
|
|
237
|
+
- If an identifier is provided and does not match the `id` of any of the objects in the `walletProviders` array, the
|
|
237
238
|
request will fail.
|
|
238
239
|
- If an identifier is not provided, the verifier tenant will use `mdoc-openid4vp://` (default OID4VP scheme) to invoke
|
|
239
240
|
any wallet.
|
|
@@ -243,7 +244,7 @@ created on the MATTR VII verifier tenant.
|
|
|
243
244
|
When using the same-device presentation flow, the SDK must define what URI to redirect the user to once they complete
|
|
244
245
|
the verification workflow in their wallet app. This can be any URI (including custom URI schemes), and must match one of
|
|
245
246
|
the values defined in the
|
|
246
|
-
[`redirectUris` array](https://learn.mattr.global/api-reference/
|
|
247
|
+
[`redirectUris` array](https://learn.mattr.global/docs/api-reference/platform/verifier-applications/postVerifierApplication)
|
|
247
248
|
when creating a verifier application.
|
|
248
249
|
|
|
249
250
|
## Correlate sessions with your own records using `state`
|
|
@@ -340,6 +341,67 @@ MATTR VII platform and the SDK, see the
|
|
|
340
341
|
[Correlating verification sessions with your system](https://learn.mattr.global/docs/verification/remote-web-verifiers/guides/correlating-verification-sessions)
|
|
341
342
|
guide on MATTR Learn.
|
|
342
343
|
|
|
344
|
+
## React to presentation requests using `onPresentationRequest`
|
|
345
|
+
|
|
346
|
+
`requestCredentials()` accepts an optional `onPresentationRequest` hook that the SDK invokes once immediately after the
|
|
347
|
+
presentation session has been created on the MATTR VII verifier tenant, and again every time the OpenID4VP auth-request
|
|
348
|
+
URI is regenerated — for cross-device sessions this happens whenever the QR code shown in the iframe is refreshed.
|
|
349
|
+
Same-device and Digital Credentials API sessions only ever invoke the hook once, since there is no refreshable request
|
|
350
|
+
URI in those flows. Use it to run your own logic against the current request, for example mirroring the `authRequestUri`
|
|
351
|
+
into your own system, starting a timer, or emitting analytics.
|
|
352
|
+
|
|
353
|
+
The hook receives the current presentation request and may be synchronous or asynchronous; the SDK awaits it before
|
|
354
|
+
continuing:
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
const options: MATTRVerifierSDK.RequestCredentialsOptions = {
|
|
358
|
+
credentialQuery: [credentialQuery],
|
|
359
|
+
challenge: MATTRVerifierSDK.utils.generateChallenge(),
|
|
360
|
+
openid4vpConfiguration: {
|
|
361
|
+
redirectUri: window.location.origin,
|
|
362
|
+
walletProviderId,
|
|
363
|
+
},
|
|
364
|
+
onPresentationRequest: async (request) => {
|
|
365
|
+
// e.g. record the current auth-request URI against your own system
|
|
366
|
+
if (request.type === "openid4vp") {
|
|
367
|
+
await fetch("/api/presentation-requests", {
|
|
368
|
+
method: "POST",
|
|
369
|
+
headers: { "Content-Type": "application/json" },
|
|
370
|
+
body: JSON.stringify({ sessionId: request.sessionId, authRequestUri: request.authRequestUri }),
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const results = await MATTRVerifierSDK.requestCredentials(options);
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
- The hook fires once for same-device and Digital Credentials API sessions, and once immediately plus again on every QR
|
|
380
|
+
refresh for cross-device sessions.
|
|
381
|
+
- The `request` argument is a discriminated union on `type`: for OpenID4VP sessions (`type: "openid4vp"`) it exposes
|
|
382
|
+
`sessionId` and `authRequestUri`; for Digital Credentials API sessions (`type: "digital-credentials-api"`) it exposes
|
|
383
|
+
only `sessionId`.
|
|
384
|
+
- If the hook throws or its returned promise rejects on any firing, the in-progress presentation is aborted — the
|
|
385
|
+
session is aborted server-side and, for cross-device flows, the iframe is closed — and `requestCredentials()` resolves
|
|
386
|
+
with an `OnPresentationRequestFailed` error (see `RequestCredentialsErrorType`). This applies consistently whether the
|
|
387
|
+
failing firing was the initial one or a later cross-device refresh. If you want the presentation flow to continue
|
|
388
|
+
regardless of an error in the hook, catch and handle the error yourself inside the hook, for example:
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
onPresentationRequest: async (request) => {
|
|
392
|
+
try {
|
|
393
|
+
await fetch("/api/presentation-requests", {
|
|
394
|
+
method: "POST",
|
|
395
|
+
headers: { "Content-Type": "application/json" },
|
|
396
|
+
body: JSON.stringify(request),
|
|
397
|
+
});
|
|
398
|
+
} catch (error) {
|
|
399
|
+
// Swallow the error here so requestCredentials() continues with the presentation flow
|
|
400
|
+
console.error("Failed to record presentation request", error);
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
```
|
|
404
|
+
|
|
343
405
|
## Request credentials using the DC API
|
|
344
406
|
|
|
345
407
|
> DC API support is currently offered as a **tech preview**. As such, functionality may be limited, may not work in all
|
|
@@ -396,6 +458,10 @@ const result = await MATTRVerifierSDK.requestCredentials({
|
|
|
396
458
|
walletProviderId, // Define the wallet identifier
|
|
397
459
|
redirectUri, // Define the redirect URI (not required for cross-device only requests)
|
|
398
460
|
},
|
|
461
|
+
onPresentationRequest: (request) => {
|
|
462
|
+
// Optional: run your own logic each time a presentation request is issued
|
|
463
|
+
console.info("Presentation request", request.sessionId);
|
|
464
|
+
},
|
|
399
465
|
});
|
|
400
466
|
|
|
401
467
|
if (result.isErr()) {
|
|
@@ -405,24 +471,30 @@ if (result.isErr()) {
|
|
|
405
471
|
}
|
|
406
472
|
```
|
|
407
473
|
|
|
408
|
-
- `apiBaseUrl
|
|
409
|
-
your MATTR VII verifier
|
|
474
|
+
- `apiBaseUrl`: Replace with the
|
|
475
|
+
[`tenant_url`](https://learn.mattr.global/docs/platform-management/management-api/overview) of your MATTR VII verifier
|
|
476
|
+
tenant.
|
|
410
477
|
- `credentialQuery`: The credential query to be used in the request.
|
|
411
478
|
- `challenge`: The challenge that will be passed to the MATTR VII tenant with the request to create a presentation
|
|
412
479
|
session. This example uses the SDK built-in method to generate the challenge, but you can replace it with a challenge
|
|
413
480
|
generated by your backend system.
|
|
414
481
|
- `walletProviderId`: Replace with a wallet identifier that matches one of the identifiers of a
|
|
415
|
-
[wallet provider](https://learn.mattr.global/api-reference/
|
|
416
|
-
|
|
482
|
+
[wallet provider](https://learn.mattr.global/docs/api-reference/platform/wallet-providers/postWalletProvider) created
|
|
483
|
+
on the MATTR VII verifier tenant.
|
|
417
484
|
- `state` (optional): Your own opaque correlation reference for this presentation session. Do not include personally
|
|
418
485
|
identifiable information (PII), credentials, or anything sensitive, as the value is transmitted in plain text on the
|
|
419
486
|
wallet-facing request URI and stored in session records. Returned in the response from `requestCredentials()`
|
|
420
487
|
(cross-device) or `handleRedirectCallback()` (same-device). See
|
|
421
488
|
[Correlate sessions with your own records using state](#correlate-sessions-with-your-own-records-using-state).
|
|
489
|
+
- `onPresentationRequest` (optional): A hook invoked once with the current presentation request after the session is
|
|
490
|
+
created, and again on every cross-device QR refresh. If the hook throws or rejects on any firing, the in-progress
|
|
491
|
+
presentation is aborted and `requestCredentials()` resolves with an `OnPresentationRequestFailed` error; catch and
|
|
492
|
+
handle errors inside the hook yourself if you want the flow to continue regardless. See
|
|
493
|
+
[React to presentation requests using onPresentationRequest](#react-to-presentation-requests-using-onpresentationrequest).
|
|
422
494
|
- `mode`: When omitted, the SDK defaults to automatically selecting a flow based on the browser's user agent (set to
|
|
423
495
|
`undefined` in the example for clarity).
|
|
424
|
-
- `redirectUri
|
|
425
|
-
[`redirectUris` array](https://learn.mattr.global/api-reference/
|
|
496
|
+
- `redirectUri`: Replace with a URI that matches one of the values in the
|
|
497
|
+
[`redirectUris` array](https://learn.mattr.global/docs/api-reference/platform/verifier-applications/postVerifierApplication)
|
|
426
498
|
in the MATTR VII tenant's verifier application.
|
|
427
499
|
|
|
428
500
|
## Request credentials with explicit same-device flow
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* Do Not Translate or Localize
|
|
8
8
|
*
|
|
9
9
|
* Bundle of @mattrglobal/verifier-sdk-web
|
|
10
|
-
* Generated: 2026-
|
|
11
|
-
* Version: 2.
|
|
10
|
+
* Generated: 2026-08-17
|
|
11
|
+
* Version: 2.3.0
|
|
12
12
|
* Dependencies:
|
|
13
13
|
*/
|
|
14
14
|
|
|
@@ -221,6 +221,7 @@ const CreateSessionOpenId4vpResponseValidator = v__namespace.object({
|
|
|
221
221
|
sessionId: v__namespace.string(),
|
|
222
222
|
sessionKey: v__namespace.string(),
|
|
223
223
|
sessionUrl: v__namespace.string(),
|
|
224
|
+
authRequestUri: v__namespace.string(),
|
|
224
225
|
state: v__namespace.optional(v__namespace.string())
|
|
225
226
|
});
|
|
226
227
|
|
|
@@ -254,7 +255,7 @@ var LocalStorageKey;
|
|
|
254
255
|
|
|
255
256
|
const MATTR_SDK_VERSION_HEADER = "x-mattr-sdk-version";
|
|
256
257
|
|
|
257
|
-
const MATTR_SDK_VERSION_VALUE = "2.
|
|
258
|
+
const MATTR_SDK_VERSION_VALUE = "2.3.0";
|
|
258
259
|
|
|
259
260
|
var MessageEventDataType;
|
|
260
261
|
|
|
@@ -262,6 +263,7 @@ var MessageEventDataType;
|
|
|
262
263
|
MessageEventDataType["PresentationCompleted"] = "PresentationCompleted";
|
|
263
264
|
MessageEventDataType["PresentationTimeout"] = "PresentationTimeout";
|
|
264
265
|
MessageEventDataType["PresentationAbort"] = "PresentationAbort";
|
|
266
|
+
MessageEventDataType["PresentationRequestUpdated"] = "PresentationRequestUpdated";
|
|
265
267
|
})(MessageEventDataType || (MessageEventDataType = {}));
|
|
266
268
|
|
|
267
269
|
const OpenId4vpConfigSameDeviceOptionsValidator = v__namespace.object({
|
|
@@ -294,6 +296,7 @@ exports.RequestCredentialsErrorType = void 0;
|
|
|
294
296
|
RequestCredentialsErrorType["RequestCredentialsFailed"] = "RequestCredentialsFailed";
|
|
295
297
|
RequestCredentialsErrorType["Timeout"] = "Timeout";
|
|
296
298
|
RequestCredentialsErrorType["Abort"] = "Abort";
|
|
299
|
+
RequestCredentialsErrorType["OnPresentationRequestFailed"] = "OnPresentationRequestFailed";
|
|
297
300
|
})(exports.RequestCredentialsErrorType || (exports.RequestCredentialsErrorType = {}));
|
|
298
301
|
|
|
299
302
|
var RequestCredentialsErrorMessage;
|
|
@@ -309,6 +312,7 @@ var RequestCredentialsErrorMessage;
|
|
|
309
312
|
RequestCredentialsErrorMessage["Abort"] = "User aborted the session";
|
|
310
313
|
RequestCredentialsErrorMessage["Timeout"] = "User session timeout";
|
|
311
314
|
RequestCredentialsErrorMessage["StateMismatch"] = "State mismatch between requested session and back-channel result";
|
|
315
|
+
RequestCredentialsErrorMessage["OnPresentationRequestFailed"] = "The onPresentationRequest hook threw an error or rejected";
|
|
312
316
|
})(RequestCredentialsErrorMessage || (RequestCredentialsErrorMessage = {}));
|
|
313
317
|
|
|
314
318
|
exports.AbortSessionErrorType = void 0;
|
|
@@ -579,7 +583,7 @@ const exchangeSessionResult = async ({challenge: challenge, responseCode: respon
|
|
|
579
583
|
body: JSON.stringify(postData)
|
|
580
584
|
});
|
|
581
585
|
const responseResult = await withRetrySafeFetch(fetchResultFn, {
|
|
582
|
-
retries:
|
|
586
|
+
retries: 4,
|
|
583
587
|
retryHttpStatus: 404
|
|
584
588
|
});
|
|
585
589
|
if (responseResult.isErr()) {
|
|
@@ -628,71 +632,111 @@ const closeCrossDeviceModal = options => {
|
|
|
628
632
|
removeWindowMessageEventListener();
|
|
629
633
|
};
|
|
630
634
|
|
|
631
|
-
const receiveMessageHandler = options =>
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
635
|
+
const receiveMessageHandler = options => {
|
|
636
|
+
let queue = Promise.resolve();
|
|
637
|
+
const processEvent = async event => {
|
|
638
|
+
const {onComplete: onComplete, onFailure: onFailure, container: container, sessionId: sessionId, sessionKey: sessionKey, apiBaseUrl: apiBaseUrl, challenge: challenge, state: state, onPresentationRequest: onPresentationRequest, isSettled: isSettled} = options;
|
|
639
|
+
if (isSettled() || event.origin !== apiBaseUrl) {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (event.data.type === MessageEventDataType.PresentationRequestUpdated && event.data.authRequestUri) {
|
|
643
|
+
if (onPresentationRequest) {
|
|
644
|
+
try {
|
|
645
|
+
await onPresentationRequest({
|
|
646
|
+
type: SessionType.Openid4vp,
|
|
647
|
+
sessionId: sessionId,
|
|
648
|
+
authRequestUri: event.data.authRequestUri
|
|
649
|
+
});
|
|
650
|
+
} catch (cause) {
|
|
651
|
+
if (isSettled()) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
onFailure({
|
|
655
|
+
type: exports.RequestCredentialsErrorType.OnPresentationRequestFailed,
|
|
656
|
+
message: RequestCredentialsErrorMessage.OnPresentationRequestFailed,
|
|
657
|
+
cause: cause
|
|
658
|
+
});
|
|
659
|
+
closeCrossDeviceModal({
|
|
660
|
+
container: container
|
|
661
|
+
});
|
|
662
|
+
await abortSession({
|
|
663
|
+
apiBaseUrl: apiBaseUrl,
|
|
664
|
+
sessionId: sessionId,
|
|
665
|
+
sessionKey: sessionKey
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
if (event.data.type === MessageEventDataType.PresentationCompleted && event.data.responseCode && event.data.sessionId) {
|
|
672
|
+
const responseCode = event.data.responseCode;
|
|
673
|
+
const result = await exchangeSessionResult({
|
|
674
|
+
challenge: challenge,
|
|
675
|
+
responseCode: responseCode,
|
|
676
|
+
sessionId: sessionId,
|
|
677
|
+
apiBaseUrl: apiBaseUrl
|
|
678
|
+
});
|
|
679
|
+
if (isSettled()) {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (result.isErr()) {
|
|
683
|
+
onFailure({
|
|
684
|
+
type: exports.RequestCredentialsErrorType.RequestCredentialsFailed,
|
|
685
|
+
message: RequestCredentialsErrorMessage.FailedToGetSessionResult
|
|
686
|
+
});
|
|
687
|
+
closeCrossDeviceModal({
|
|
688
|
+
container: container
|
|
689
|
+
});
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
const resultState = "challenge" in result.value ? result.value.state : undefined;
|
|
693
|
+
if (state !== undefined && resultState !== undefined && state !== resultState) {
|
|
694
|
+
onFailure({
|
|
695
|
+
type: exports.RequestCredentialsErrorType.RequestCredentialsFailed,
|
|
696
|
+
message: RequestCredentialsErrorMessage.StateMismatch
|
|
697
|
+
});
|
|
698
|
+
closeCrossDeviceModal({
|
|
699
|
+
container: container
|
|
700
|
+
});
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
onComplete({
|
|
704
|
+
result: "challenge" in result.value ? result.value : undefined,
|
|
705
|
+
sessionId: result.value.sessionId,
|
|
706
|
+
sessionCompletedInRedirect: false,
|
|
707
|
+
state: resultState !== null && resultState !== void 0 ? resultState : state
|
|
708
|
+
});
|
|
709
|
+
closeCrossDeviceModal({
|
|
710
|
+
container: container
|
|
711
|
+
});
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (event.data.type === MessageEventDataType.PresentationTimeout) {
|
|
645
715
|
onFailure({
|
|
646
|
-
type: exports.RequestCredentialsErrorType.
|
|
647
|
-
message: RequestCredentialsErrorMessage.
|
|
716
|
+
type: exports.RequestCredentialsErrorType.Timeout,
|
|
717
|
+
message: RequestCredentialsErrorMessage.Timeout
|
|
648
718
|
});
|
|
649
719
|
closeCrossDeviceModal({
|
|
650
720
|
container: container
|
|
651
721
|
});
|
|
652
722
|
return;
|
|
653
723
|
}
|
|
654
|
-
|
|
655
|
-
if (state !== undefined && resultState !== undefined && state !== resultState) {
|
|
724
|
+
if (event.data.type === MessageEventDataType.PresentationAbort) {
|
|
656
725
|
onFailure({
|
|
657
|
-
type: exports.RequestCredentialsErrorType.
|
|
658
|
-
message: RequestCredentialsErrorMessage.
|
|
726
|
+
type: exports.RequestCredentialsErrorType.Abort,
|
|
727
|
+
message: RequestCredentialsErrorMessage.Abort
|
|
659
728
|
});
|
|
660
729
|
closeCrossDeviceModal({
|
|
661
730
|
container: container
|
|
662
731
|
});
|
|
663
732
|
return;
|
|
664
733
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
closeCrossDeviceModal({
|
|
672
|
-
container: container
|
|
673
|
-
});
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
if (event.data.type === MessageEventDataType.PresentationTimeout) {
|
|
677
|
-
onFailure({
|
|
678
|
-
type: exports.RequestCredentialsErrorType.Timeout,
|
|
679
|
-
message: RequestCredentialsErrorMessage.Timeout
|
|
680
|
-
});
|
|
681
|
-
closeCrossDeviceModal({
|
|
682
|
-
container: container
|
|
683
|
-
});
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
if (event.data.type === MessageEventDataType.PresentationAbort) {
|
|
687
|
-
onFailure({
|
|
688
|
-
type: exports.RequestCredentialsErrorType.Abort,
|
|
689
|
-
message: RequestCredentialsErrorMessage.Abort
|
|
690
|
-
});
|
|
691
|
-
closeCrossDeviceModal({
|
|
692
|
-
container: container
|
|
693
|
-
});
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
734
|
+
};
|
|
735
|
+
return event => {
|
|
736
|
+
const next = queue.then((() => processEvent(event)));
|
|
737
|
+
queue = next.catch((() => undefined));
|
|
738
|
+
return next;
|
|
739
|
+
};
|
|
696
740
|
};
|
|
697
741
|
|
|
698
742
|
const openCrossDeviceModal = options => {
|
|
@@ -712,16 +756,18 @@ const openCrossDeviceModal = options => {
|
|
|
712
756
|
};
|
|
713
757
|
|
|
714
758
|
const requestCredentialsWithCrossDevice = async options => {
|
|
715
|
-
const {challenge: challenge, apiBaseUrl: apiBaseUrl, sessionUrl: sessionUrl, sessionId: sessionId, sessionKey: sessionKey, state: state} = options;
|
|
759
|
+
const {challenge: challenge, apiBaseUrl: apiBaseUrl, sessionUrl: sessionUrl, sessionId: sessionId, sessionKey: sessionKey, state: state, onPresentationRequest: onPresentationRequest} = options;
|
|
716
760
|
const container = openCrossDeviceModal({
|
|
717
761
|
sessionUrl: sessionUrl
|
|
718
762
|
});
|
|
763
|
+
let settled = false;
|
|
719
764
|
return await new Promise((resolve => {
|
|
720
765
|
const abortController = setActiveSession({
|
|
721
766
|
sessionId: sessionId,
|
|
722
767
|
sessionKey: sessionKey
|
|
723
768
|
});
|
|
724
769
|
abortController.signal.addEventListener("abort", (() => {
|
|
770
|
+
settled = true;
|
|
725
771
|
closeCrossDeviceModal({
|
|
726
772
|
container: container
|
|
727
773
|
});
|
|
@@ -734,11 +780,20 @@ const requestCredentialsWithCrossDevice = async options => {
|
|
|
734
780
|
listener = receiveMessageHandler({
|
|
735
781
|
container: container,
|
|
736
782
|
sessionId: sessionId,
|
|
783
|
+
sessionKey: sessionKey,
|
|
737
784
|
apiBaseUrl: apiBaseUrl,
|
|
738
785
|
challenge: challenge,
|
|
739
786
|
state: state,
|
|
740
|
-
|
|
741
|
-
|
|
787
|
+
onPresentationRequest: onPresentationRequest,
|
|
788
|
+
isSettled: () => settled,
|
|
789
|
+
onComplete: data => {
|
|
790
|
+
settled = true;
|
|
791
|
+
resolve(neverthrow.ok(data));
|
|
792
|
+
},
|
|
793
|
+
onFailure: error => {
|
|
794
|
+
settled = true;
|
|
795
|
+
resolve(neverthrow.err(error));
|
|
796
|
+
}
|
|
742
797
|
});
|
|
743
798
|
window.addEventListener(WindowEventListenerType.message, listener, false);
|
|
744
799
|
}));
|
|
@@ -986,7 +1041,7 @@ const requestCredentials = async options => {
|
|
|
986
1041
|
}
|
|
987
1042
|
assertType(RequestCredentialsOptionsValidator, "Invalid request credential options")(options);
|
|
988
1043
|
const {apiBaseUrl: apiBaseUrl, applicationId: applicationId} = initializeOptions;
|
|
989
|
-
const {challenge: challenge = generateChallenge(), credentialQuery: credentialQuery, openid4vpConfiguration: openid4vpConfiguration, state: state} = options;
|
|
1044
|
+
const {challenge: challenge = generateChallenge(), credentialQuery: credentialQuery, openid4vpConfiguration: openid4vpConfiguration, state: state, onPresentationRequest: onPresentationRequest} = options;
|
|
990
1045
|
const dcApiSupported = isDigitalCredentialsApiSupported();
|
|
991
1046
|
const openId4VpRedirectUri = deriveOpenId4vpRedirectUri(openid4vpConfiguration);
|
|
992
1047
|
const createSessionResult = await createSession({
|
|
@@ -1008,6 +1063,30 @@ const requestCredentials = async options => {
|
|
|
1008
1063
|
}
|
|
1009
1064
|
const session = createSessionResult.value;
|
|
1010
1065
|
const {sessionKey: sessionKey, sessionId: sessionId} = session;
|
|
1066
|
+
if (onPresentationRequest) {
|
|
1067
|
+
const presentationRequest = session.type === SessionType.DigitalCredentialsApi ? {
|
|
1068
|
+
type: SessionType.DigitalCredentialsApi,
|
|
1069
|
+
sessionId: sessionId
|
|
1070
|
+
} : {
|
|
1071
|
+
type: SessionType.Openid4vp,
|
|
1072
|
+
sessionId: sessionId,
|
|
1073
|
+
authRequestUri: session.authRequestUri
|
|
1074
|
+
};
|
|
1075
|
+
try {
|
|
1076
|
+
await onPresentationRequest(presentationRequest);
|
|
1077
|
+
} catch (cause) {
|
|
1078
|
+
await abortSession({
|
|
1079
|
+
apiBaseUrl: apiBaseUrl,
|
|
1080
|
+
sessionId: sessionId,
|
|
1081
|
+
sessionKey: sessionKey
|
|
1082
|
+
});
|
|
1083
|
+
return neverthrow.err({
|
|
1084
|
+
type: exports.RequestCredentialsErrorType.OnPresentationRequestFailed,
|
|
1085
|
+
message: RequestCredentialsErrorMessage.OnPresentationRequestFailed,
|
|
1086
|
+
cause: cause
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1011
1090
|
if (session.type === SessionType.DigitalCredentialsApi) {
|
|
1012
1091
|
const {request: request, sessionTtl: sessionTtl} = session;
|
|
1013
1092
|
return await requestCredentialsWithDigitalCredentialsApi({
|
|
@@ -1043,7 +1122,8 @@ const requestCredentials = async options => {
|
|
|
1043
1122
|
sessionUrl: sessionUrl,
|
|
1044
1123
|
sessionKey: sessionKey,
|
|
1045
1124
|
sessionId: sessionId,
|
|
1046
|
-
state: state
|
|
1125
|
+
state: state,
|
|
1126
|
+
onPresentationRequest: onPresentationRequest
|
|
1047
1127
|
});
|
|
1048
1128
|
};
|
|
1049
1129
|
|