@mattrglobal/verifier-sdk-web 2.1.2-unstable.17 → 2.1.2-unstable.173
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 +153 -90
- package/dist/lib/verifier-js-no-deps.cjs.js +62 -17
- package/dist/lib/verifier-js-no-deps.cjs.js.map +1 -1
- package/dist/lib/verifier-js.cjs.js +80 -17
- package/dist/lib/verifier-js.cjs.js.map +1 -1
- package/dist/typings/verifier/handleRedirectCallback.d.ts +2 -1
- package/dist/typings/verifier/types/credential-presentation.d.ts +6 -0
- package/dist/typings/verifier/types/verifier-web-sdk.d.ts +25 -2
- package/dist/typings/verifier/utils.d.ts +1 -1
- package/dist/verifier-js.development.js +79 -17
- package/dist/verifier-js.development.js.map +1 -1
- package/dist/verifier-js.production.esm.js +2 -2
- package/dist/verifier-js.production.esm.js.map +1 -1
- package/dist/verifier-js.production.js +2 -2
- package/dist/verifier-js.production.js.map +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
# Table of Contents
|
|
4
4
|
|
|
5
|
+
- [verifier-sdk-web](#verifier-sdk-web)
|
|
6
|
+
- [Table of Contents](#table-of-contents)
|
|
5
7
|
- [General](#general)
|
|
6
8
|
- [Licensing](#licensing)
|
|
7
9
|
- [Overview](#overview)
|
|
@@ -35,15 +37,21 @@ Request or download the
|
|
|
35
37
|
|
|
36
38
|
## Overview
|
|
37
39
|
|
|
38
|
-
The Verifier Web SDK is a powerful tool for integrating online credential verification capabilities into your web
|
|
40
|
+
The Verifier Web SDK is a powerful tool for integrating online credential verification capabilities into your web
|
|
41
|
+
applications. It enables secure and efficient verification of [mDocs](https://learn.mattr.global/docs/mdocs), supporting
|
|
42
|
+
both [same-device](https://learn.mattr.global/docs/verification/online/mdocs/overview#same-device-verification-workflow)
|
|
43
|
+
and
|
|
44
|
+
[cross-device](https://learn.mattr.global/docs/verification/online/mdocs/overview#cross-device-verification-workflow)
|
|
45
|
+
verification workflows. The SDK leverages the MATTR VII platform to handle credential presentation and verification
|
|
46
|
+
processes.
|
|
39
47
|
|
|
40
48
|
## Features
|
|
41
49
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
- Simple integration into web applications.
|
|
51
|
+
- Supports both same-device and cross-device presentation flows.
|
|
52
|
+
- Secure handling of mDocs requests and responses.
|
|
53
|
+
- Compliant with [ISO/IEC DTS 18013-7](https://www.iso.org/standard/82772.html).
|
|
54
|
+
- Offers [DC API](https://wicg.github.io/digital-credentials/) support as a **tech-preview**.
|
|
47
55
|
|
|
48
56
|
> In this SDK mDocs are referred to as Mobile Credentials.
|
|
49
57
|
|
|
@@ -51,8 +59,8 @@ The Verifier Web SDK is a powerful tool for integrating online credential verifi
|
|
|
51
59
|
|
|
52
60
|
## How to get access to the MATTR Pi Verifier Web SDK
|
|
53
61
|
|
|
54
|
-
Refer to our [SDK Docs landing page](https://learn.mattr.global/guides/get-started-pi) for step-by-step instructions to
|
|
55
|
-
to any of our SDKs.
|
|
62
|
+
Refer to our [SDK Docs landing page](https://learn.mattr.global/guides/get-started-pi) for step-by-step instructions to
|
|
63
|
+
gain access to any of our SDKs.
|
|
56
64
|
|
|
57
65
|
> Please [reach out](mailto:dev-support@mattr.global) if you need any assistance.
|
|
58
66
|
|
|
@@ -82,25 +90,28 @@ MATTRVerifierSDK.initialize(...);
|
|
|
82
90
|
<script src="https://cdn.mattr.global/js/verifier-sdk-web/{VERSION}/verifier-js.production.js"></script>
|
|
83
91
|
```
|
|
84
92
|
|
|
85
|
-
> This script will automatically pick up any SDK patch updates. You can lock your implementation to a specific patch
|
|
93
|
+
> This script will automatically pick up any SDK patch updates. You can lock your implementation to a specific patch
|
|
94
|
+
> version by replacing 1.0 with the specific version (e.g.
|
|
95
|
+
> https://cdn.mattr.global/js/verifier-sdk-web/1.0.1/verifier-js.production.js).
|
|
86
96
|
|
|
87
97
|
2. Access SDK functions via global `MATTRVerifierSDK` object.
|
|
88
98
|
|
|
89
99
|
```javascript
|
|
90
|
-
<script>
|
|
91
|
-
MATTRVerifierSDK.initialize(...);
|
|
92
|
-
</script>
|
|
100
|
+
<script>MATTRVerifierSDK.initialize(...);</script>
|
|
93
101
|
```
|
|
94
102
|
|
|
95
103
|
# Usage
|
|
96
104
|
|
|
97
|
-
The SDK can make a request to create a presentation session with a configured MATTR VII verifier tenant. This requires
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
The SDK can make a request to create a presentation session with a configured MATTR VII verifier tenant. This requires
|
|
106
|
+
the following configurations and settings:
|
|
107
|
+
|
|
108
|
+
- Initialize the SDK with the URL of the MATTR VII tenant that will handle the verification request.
|
|
109
|
+
- Prepare a credential query that defines what claims are required for verification.
|
|
110
|
+
- Generate a unique challenge for the presentation session.
|
|
111
|
+
- Determine what presentation flows are supported in the session.
|
|
112
|
+
- Define what wallets can be used to respond to the verification request.
|
|
113
|
+
- Configure the URI the user will be redirected to when the verification workflow is completed (only required for
|
|
114
|
+
same-device flows).
|
|
104
115
|
|
|
105
116
|
## Initialize the SDK
|
|
106
117
|
|
|
@@ -111,96 +122,137 @@ MATTRVerifierSDK.initialize({ apiBaseUrl, applicationId });
|
|
|
111
122
|
```
|
|
112
123
|
|
|
113
124
|
- `apiBaseUrl` (required): URL of the MATTR VII verifier tenant.
|
|
114
|
-
- `applicationId` (required): Unique identifier of the verifier application. This must match the
|
|
125
|
+
- `applicationId` (required): Unique identifier of the verifier application. This must match the
|
|
126
|
+
[`id`](https://learn.mattr.global/api-reference/latest/tag/Verifier-applications#operation/postVerifierApplication!c=201&path=0/id&t=response)
|
|
127
|
+
parameter in the response returned when
|
|
128
|
+
[creating a Verifier application](https://learn.mattr.global/api-reference/latest/tag/Verifier-applications#operation/postVerifierApplication)
|
|
129
|
+
on the MATTR VII verifier tenant.
|
|
115
130
|
|
|
116
131
|
## Prepare a credential query
|
|
117
132
|
|
|
118
|
-
The following example credential query will request the `given_name`, `family_name`, `birth_date` and `portrait` claims
|
|
133
|
+
The following example credential query will request the `given_name`, `family_name`, `birth_date` and `portrait` claims
|
|
134
|
+
from a `mobile` credential profile with `org.iso.18013.5.1.mDL` as a docType:
|
|
119
135
|
|
|
120
136
|
```javascript
|
|
121
137
|
const credentialQuery = [
|
|
122
138
|
{
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
139
|
+
profile: "mobile",
|
|
140
|
+
docType: "org.iso.18013.5.1.mDL",
|
|
141
|
+
nameSpaces: {
|
|
126
142
|
"org.iso.18013.5.1": {
|
|
127
|
-
|
|
128
|
-
|
|
143
|
+
given_name: {
|
|
144
|
+
intentToRetain: false,
|
|
145
|
+
},
|
|
146
|
+
family_name: {
|
|
147
|
+
intentToRetain: false,
|
|
129
148
|
},
|
|
130
|
-
|
|
131
|
-
|
|
149
|
+
birth_date: {
|
|
150
|
+
intentToRetain: false,
|
|
132
151
|
},
|
|
133
|
-
|
|
134
|
-
|
|
152
|
+
portrait: {
|
|
153
|
+
intentToRetain: false,
|
|
135
154
|
},
|
|
136
|
-
|
|
137
|
-
|
|
155
|
+
resident_postal_code: {
|
|
156
|
+
intentToRetain: false,
|
|
138
157
|
},
|
|
139
|
-
"resident_postal_code": {
|
|
140
|
-
"intentToRetain": false
|
|
141
|
-
}
|
|
142
158
|
},
|
|
143
|
-
}
|
|
144
|
-
}
|
|
159
|
+
},
|
|
160
|
+
},
|
|
145
161
|
];
|
|
146
162
|
```
|
|
147
|
-
* `profile`: Credential format of the credential that will be verified. Currently only mobile (mDocs) is supported.
|
|
148
|
-
* `docType`: the mDL’s type. Confirm with the certificate issuer for what docType they are issuing. Some common examples include:
|
|
149
|
-
* Mobile Driver License (`org.iso.18013.5.1.mDL`).
|
|
150
|
-
* PhotoID (`org.iso.23220.photoid.1`).
|
|
151
|
-
* Mobile Vehicle Registration Card (`org.iso.7367.1.mVRC`).
|
|
152
|
-
* Health certificate (`org.micov.vtr.1`).
|
|
153
|
-
* `nameSpaces`: Each namespace corresponds to a group of claims included in the credential. These can be claims that are part of a specific standard, jurisdiction or any other reference. The namespace would usually correspond to the requested `docType`.
|
|
154
|
-
* `intentToRetain` (Optional): When set to `true`, the holder will be indicated that the verifier intends to retain this claim beyond the verification workflow. Defaults to `false` when not specified.
|
|
155
|
-
|
|
156
|
-
> The API supports including multiple query objects in the `credentialQuery` array in a single request. For simplicity, this example only includes a single query object.
|
|
157
163
|
|
|
158
|
-
|
|
164
|
+
- `profile`: Credential format of the credential that will be verified. Currently only mobile (mDocs) is supported.
|
|
165
|
+
- `docType`: the mDL’s type. Confirm with the certificate issuer for what docType they are issuing. Some common examples
|
|
166
|
+
include:
|
|
167
|
+
- Mobile Driver License (`org.iso.18013.5.1.mDL`).
|
|
168
|
+
- PhotoID (`org.iso.23220.photoid.1`).
|
|
169
|
+
- Mobile Vehicle Registration Card (`org.iso.7367.1.mVRC`).
|
|
170
|
+
- Health certificate (`org.micov.vtr.1`).
|
|
171
|
+
- `nameSpaces`: Each namespace corresponds to a group of claims included in the credential. These can be claims that are
|
|
172
|
+
part of a specific standard, jurisdiction or any other reference. The namespace would usually correspond to the
|
|
173
|
+
requested `docType`.
|
|
174
|
+
- `intentToRetain` (Optional): When set to `true`, the holder will be indicated that the verifier intends to retain
|
|
175
|
+
this claim beyond the verification workflow. Defaults to `false` when not specified.
|
|
176
|
+
|
|
177
|
+
> The API supports including multiple query objects in the `credentialQuery` array in a single request. For simplicity,
|
|
178
|
+
> this example only includes a single query object.
|
|
159
179
|
|
|
160
|
-
|
|
180
|
+
In this example the `credentialQuery` query will request for the `birthdate`, `portrait` and `resident_postal_code`
|
|
181
|
+
claims from any credentials whose `profile` is `mobile` and `docType` is `org.iso.18013.5.1.mDL`.
|
|
161
182
|
|
|
162
|
-
|
|
183
|
+
It also sets `intentToRetain` as `false` for all claims, indicating to the holder that the verifier will not retain any
|
|
184
|
+
of these claims.
|
|
185
|
+
|
|
186
|
+
> While `intentToRetain` defaults to false, it is explicitly set to `false` in the example above for clarity purposes.
|
|
187
|
+
> If there is no intention to retain a claim, it is sufficient to simply exclude `intentToRetain` from the query.
|
|
163
188
|
|
|
164
189
|
## Generate challenge
|
|
165
190
|
|
|
166
|
-
The Verifier Web SDK passes a unique challenge to the MATTR VII verifier tenant with every request to create a new
|
|
167
|
-
|
|
168
|
-
|
|
191
|
+
The Verifier Web SDK passes a unique challenge to the MATTR VII verifier tenant with every request to create a new
|
|
192
|
+
presentation session. The purpose of the challenge is to ensure the security and integrity of the credential
|
|
193
|
+
verification process by preventing replay attacks and verifying the authenticity of each request and response. You can
|
|
194
|
+
either:
|
|
195
|
+
|
|
196
|
+
- Generate this challenge by your backend and pass it to the SDK.
|
|
197
|
+
- Generate it using the SDK built-in method.
|
|
169
198
|
|
|
170
|
-
When the challenge is generated by your backend, you should use it to validate the verification results received from
|
|
199
|
+
When the challenge is generated by your backend, you should use it to validate the verification results received from
|
|
200
|
+
the MATTR VII verifier tenant.
|
|
171
201
|
|
|
172
202
|
## Determine supported presentation flows
|
|
173
203
|
|
|
174
|
-
The **same-device flow** involves the user completing all steps on a single device, such as their smartphone. They
|
|
204
|
+
The **same-device flow** involves the user completing all steps on a single device, such as their smartphone. They
|
|
205
|
+
initiate the credential request on the verifier's web app, are redirected to their wallet app to consent and present
|
|
206
|
+
credentials, and then return to the verifier's web app with the results.
|
|
175
207
|
|
|
176
|
-
In contrast, the **cross-device flow** starts on one device, like a laptop. When the user initiates the request, the web
|
|
208
|
+
In contrast, the **cross-device flow** starts on one device, like a laptop. When the user initiates the request, the web
|
|
209
|
+
app responds by displaying a QR code. The user then scans this QR code with their smartphone, use their wallet app to
|
|
210
|
+
present matching credentials, and the results are sent back to the verifier's web app.
|
|
177
211
|
|
|
178
|
-
The main difference is that the same-device flow uses only one device for the entire process, while the cross-device
|
|
212
|
+
The main difference is that the same-device flow uses only one device for the entire process, while the cross-device
|
|
213
|
+
flow uses two devices for added flexibility.
|
|
179
214
|
|
|
180
215
|
By default, the Verifier Web SDK automatically selects a flow based on the browser's user agent:
|
|
181
|
-
* Same-device flows are used when the agent is a mobile device.
|
|
182
|
-
* Cross-device flows are used in all other cases.
|
|
183
216
|
|
|
184
|
-
|
|
217
|
+
- Same-device flows are used when the agent is a mobile device.
|
|
218
|
+
- Cross-device flows are used in all other cases.
|
|
219
|
+
|
|
220
|
+
This behavior can be explicitly overridden by specifying the desired mode in the SDK configuration, as shown in the
|
|
221
|
+
[examples](#request-credentials-examples) below.
|
|
185
222
|
|
|
186
223
|
## Define wallet identifiers
|
|
187
224
|
|
|
188
|
-
You can define an identifier of a specific wallet you want to invoke with this verification request. The identifier
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
225
|
+
You can define an identifier of a specific wallet you want to invoke with this verification request. The identifier
|
|
226
|
+
defined by the SDK in the credential request must match one of the identifiers of a
|
|
227
|
+
[wallet provider](https://learn.mattr.global/api-reference/latest/tag/Wallet-providers#operation/postWalletProvider!path=name&t=request)
|
|
228
|
+
created on the MATTR VII verifier tenant.
|
|
229
|
+
|
|
230
|
+
- If an identifier is provided and matches the `id` of one of the objects in the `walletProviders` array, the verifier
|
|
231
|
+
tenant will invoke that specific wallet using its corresponding
|
|
232
|
+
[`authorizationEndpoint`](https://learn.mattr.global/api-reference/latest/tag/Wallet-providers#operation/postWalletProvider!path=openid4vpConfiguration/authorizationEndpoint&t=request).
|
|
233
|
+
- If an identifier is provided and does not match the `id` of any of the objects in the `walletProviders array`, the
|
|
234
|
+
request will fail.
|
|
235
|
+
- If an identifier is not provided, the verifier tenant will use `mdoc-openid4vp://` (default OID4VP scheme) to invoke
|
|
236
|
+
any wallet.
|
|
192
237
|
|
|
193
238
|
## Configure redirectUri
|
|
194
239
|
|
|
195
|
-
When using the same-device presentation flow, the SDK must define what URI to redirect the user to once they complete
|
|
240
|
+
When using the same-device presentation flow, the SDK must define what URI to redirect the user to once they complete
|
|
241
|
+
the verification workflow in their wallet app. This can be any URI (including custom URI schemes), and must match one of
|
|
242
|
+
the values defined in the
|
|
243
|
+
[`redirectUris` array](https://learn.mattr.global/api-reference/latest/tag/Verifier-applications#operation/postVerifierApplication!path=0/openid4vpConfiguration/redirectUris&t=request)
|
|
244
|
+
when creating a verifier application.
|
|
196
245
|
|
|
197
246
|
## Request credentials using the DC API
|
|
198
247
|
|
|
199
|
-
> DC API support is currently offered as a **tech preview**. As such, functionality may be limited, may not work in all
|
|
248
|
+
> DC API support is currently offered as a **tech preview**. As such, functionality may be limited, may not work in all
|
|
249
|
+
> scenarios, and could change or break without prior notice.
|
|
200
250
|
|
|
201
|
-
1. Add the `dcApiConfiguration` object to your MATTR VII
|
|
251
|
+
1. Add the `dcApiConfiguration` object to your MATTR VII
|
|
252
|
+
[verifier application configuration](https://learn.mattr.global/docs/verification/remote-verification-api-reference/verifier-applications#update-a-verifier-application).
|
|
202
253
|
|
|
203
|
-
2. Use the SDK's `isDigitalCredentialsApiSupported` method to determine if the user's browser supports the Digital
|
|
254
|
+
2. Use the SDK's `isDigitalCredentialsApiSupported` method to determine if the user's browser supports the Digital
|
|
255
|
+
Credentials API:
|
|
204
256
|
|
|
205
257
|
```typescript
|
|
206
258
|
import * as MattrVerifierSDK from "@mattrglobal/verifier-sdk-web";
|
|
@@ -243,9 +295,9 @@ const result = await MATTRVerifierSDK.requestCredentials({
|
|
|
243
295
|
credentialQuery: credentialQuery, // Define what credential query to use
|
|
244
296
|
challenge: MATTRVerifierSDK.utils.generateChallenge(), // Pass a unique challenge
|
|
245
297
|
openid4vpConfiguration: {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
298
|
+
walletProviderId, // Define the wallet identifier
|
|
299
|
+
redirectUri, // Define the redirect URI (not required for cross-device only requests)
|
|
300
|
+
},
|
|
249
301
|
});
|
|
250
302
|
|
|
251
303
|
if (result.isErr()) {
|
|
@@ -254,12 +306,21 @@ if (result.isErr()) {
|
|
|
254
306
|
console.info("<<< MATTRVerifierSDK.requestCredentials succeed", result.value);
|
|
255
307
|
}
|
|
256
308
|
```
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
309
|
+
|
|
310
|
+
- `apiBaseUrl` : Replace with the [`tenant_url`](https://learn.mattr.global/docs/platform-management/authentication) of
|
|
311
|
+
your MATTR VII verifier tenant.
|
|
312
|
+
- `credentialQuery`: The credential query to be used in the request.
|
|
313
|
+
- `challenge`: The challenge that will be passed to the MATTR VII tenant with the request to create a presentation
|
|
314
|
+
session. This example uses the SDK built-in method to generate the challenge, but you can replace it with a challenge
|
|
315
|
+
generated by your backend system.
|
|
316
|
+
- `walletProviderId`: Replace with a wallet identifier that matches one of the identifiers of a
|
|
317
|
+
[wallet provider](https://learn.mattr.global/api-reference/latest/tag/Wallet-providers#operation/postWalletProvider!path=name&t=request)
|
|
318
|
+
created on the MATTR VII verifier tenant..
|
|
319
|
+
- `mode`: When omitted, the SDK defaults to automatically selecting a flow based on the browser's user agent (set to
|
|
320
|
+
`undefined` in the example for clarity).
|
|
321
|
+
- `redirectUri` Replace with a URI that matches one of the values in the
|
|
322
|
+
[`redirectUris` array](https://learn.mattr.global/api-reference/latest/tag/Verifier-applications#operation/postVerifierApplication!path=0/openid4vpConfiguration/redirectUris&t=request)
|
|
323
|
+
in the MATTR VII tenant's verifier application.
|
|
263
324
|
|
|
264
325
|
## Request credentials with explicit same-device flow
|
|
265
326
|
|
|
@@ -269,10 +330,10 @@ const result = await MATTRVerifierSDK.requestCredentials({
|
|
|
269
330
|
credentialQuery: credentialQuery,
|
|
270
331
|
challenge: MATTRVerifierSDK.utils.generateChallenge(),
|
|
271
332
|
openid4vpConfiguration: {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
333
|
+
redirectUri,
|
|
334
|
+
walletProviderId,
|
|
335
|
+
mode: "sameDevice",
|
|
336
|
+
},
|
|
276
337
|
});
|
|
277
338
|
|
|
278
339
|
// result can be retrieved on redirect uri page. for example
|
|
@@ -282,9 +343,9 @@ window.addEventListener("load", async () => {
|
|
|
282
343
|
});
|
|
283
344
|
```
|
|
284
345
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
346
|
+
- `mode`: When set to `sameDevice`, the SDK will only support same-device flow in this verification workflow.
|
|
347
|
+
- Note that in this case you must define a `redirectUri`.
|
|
348
|
+
- This example shows how you can redirect the user to page that will retrieve and display the verification results.
|
|
288
349
|
|
|
289
350
|
## Request credentials with explicit cross-device flow
|
|
290
351
|
|
|
@@ -294,9 +355,9 @@ const result = await MATTRVerifierSDK.requestCredentials({
|
|
|
294
355
|
credentialQuery: credentialQuery,
|
|
295
356
|
challenge: MATTRVerifierSDK.utils.generateChallenge(),
|
|
296
357
|
openid4vpConfiguration: {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
358
|
+
walletProviderId,
|
|
359
|
+
mode: "crossDevice",
|
|
360
|
+
},
|
|
300
361
|
});
|
|
301
362
|
|
|
302
363
|
if (result.isErr()) {
|
|
@@ -306,8 +367,10 @@ if (result.isErr()) {
|
|
|
306
367
|
}
|
|
307
368
|
```
|
|
308
369
|
|
|
309
|
-
|
|
310
|
-
|
|
370
|
+
- `mode`: When set to `crossDevice`, the SDK will only support cross-device flow in this verification workflow.
|
|
371
|
+
- Note that in this case you must define how to handle verification completion and failure.
|
|
311
372
|
|
|
312
373
|
# Error Handling
|
|
313
|
-
|
|
374
|
+
|
|
375
|
+
The SDK includes mechanisms for handling errors, such as invalid requests, session timeouts, and user aborts. Callbacks
|
|
376
|
+
provide detailed error information to help diagnose and remedy issues.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Do Not Translate or Localize
|
|
8
8
|
*
|
|
9
9
|
* Bundle of @mattrglobal/verifier-sdk-web
|
|
10
|
-
* Generated: 2026-
|
|
10
|
+
* Generated: 2026-06-08
|
|
11
11
|
* Version: 2.1.1
|
|
12
12
|
* Dependencies:
|
|
13
13
|
*/
|
|
@@ -181,7 +181,8 @@ const PresentationResultRelaxValidator = v__namespace.object({
|
|
|
181
181
|
credentialQuery: v__namespace.optional(v__namespace.unknown()),
|
|
182
182
|
credentials: v__namespace.optional(v__namespace.unknown()),
|
|
183
183
|
credentialErrors: v__namespace.optional(v__namespace.unknown()),
|
|
184
|
-
error: v__namespace.optional(v__namespace.unknown())
|
|
184
|
+
error: v__namespace.optional(v__namespace.unknown()),
|
|
185
|
+
state: v__namespace.optional(v__namespace.string())
|
|
185
186
|
});
|
|
186
187
|
|
|
187
188
|
exports.Mode = void 0;
|
|
@@ -203,7 +204,8 @@ v__namespace.object({
|
|
|
203
204
|
challenge: v__namespace.string(),
|
|
204
205
|
redirectUri: v__namespace.optional(v__namespace.string()),
|
|
205
206
|
walletProviderId: v__namespace.optional(v__namespace.string()),
|
|
206
|
-
dcApiSupported: v__namespace.optional(v__namespace.boolean())
|
|
207
|
+
dcApiSupported: v__namespace.optional(v__namespace.boolean()),
|
|
208
|
+
state: v__namespace.optional(v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1)))
|
|
207
209
|
});
|
|
208
210
|
|
|
209
211
|
const CreateSessionDigitalCredentialsValidator = v__namespace.object({
|
|
@@ -218,7 +220,8 @@ const CreateSessionOpenId4vpResponseValidator = v__namespace.object({
|
|
|
218
220
|
type: v__namespace.optional(v__namespace.literal(SessionType.Openid4vp)),
|
|
219
221
|
sessionId: v__namespace.string(),
|
|
220
222
|
sessionKey: v__namespace.string(),
|
|
221
|
-
sessionUrl: v__namespace.string()
|
|
223
|
+
sessionUrl: v__namespace.string(),
|
|
224
|
+
state: v__namespace.optional(v__namespace.string())
|
|
222
225
|
});
|
|
223
226
|
|
|
224
227
|
const CreateSessionResponseValidator = v__namespace.union([ CreateSessionDigitalCredentialsValidator, CreateSessionOpenId4vpResponseValidator ]);
|
|
@@ -246,6 +249,7 @@ var LocalStorageKey;
|
|
|
246
249
|
(function(LocalStorageKey) {
|
|
247
250
|
LocalStorageKey["challenge"] = "mattr_chg";
|
|
248
251
|
LocalStorageKey["sessionId"] = "mattr_sid";
|
|
252
|
+
LocalStorageKey["state"] = "mattr_st";
|
|
249
253
|
})(LocalStorageKey || (LocalStorageKey = {}));
|
|
250
254
|
|
|
251
255
|
const MATTR_SDK_VERSION_HEADER = "x-mattr-sdk-version";
|
|
@@ -280,7 +284,8 @@ const OpenId4vpConfigAutoDetectOptionsValidator = v__namespace.object({
|
|
|
280
284
|
const RequestCredentialsOptionsValidator = v__namespace.object({
|
|
281
285
|
credentialQuery: v__namespace.pipe(v__namespace.array(CredentialQueryValidator), v__namespace.nonEmpty()),
|
|
282
286
|
challenge: v__namespace.optional(v__namespace.string()),
|
|
283
|
-
openid4vpConfiguration: v__namespace.optional(v__namespace.union([ OpenId4vpConfigSameDeviceOptionsValidator, OpenId4vpConfigCrossDeviceOptionsValidator, OpenId4vpConfigAutoDetectOptionsValidator ]))
|
|
287
|
+
openid4vpConfiguration: v__namespace.optional(v__namespace.union([ OpenId4vpConfigSameDeviceOptionsValidator, OpenId4vpConfigCrossDeviceOptionsValidator, OpenId4vpConfigAutoDetectOptionsValidator ])),
|
|
288
|
+
state: v__namespace.optional(v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1, "state must not be empty")))
|
|
284
289
|
});
|
|
285
290
|
|
|
286
291
|
exports.RequestCredentialsErrorType = void 0;
|
|
@@ -303,6 +308,7 @@ var RequestCredentialsErrorMessage;
|
|
|
303
308
|
RequestCredentialsErrorMessage["DcApiResponseParseError"] = "Failed to parse response from Digital Credentials API";
|
|
304
309
|
RequestCredentialsErrorMessage["Abort"] = "User aborted the session";
|
|
305
310
|
RequestCredentialsErrorMessage["Timeout"] = "User session timeout";
|
|
311
|
+
RequestCredentialsErrorMessage["StateMismatch"] = "State mismatch between requested session and back-channel result";
|
|
306
312
|
})(RequestCredentialsErrorMessage || (RequestCredentialsErrorMessage = {}));
|
|
307
313
|
|
|
308
314
|
exports.AbortSessionErrorType = void 0;
|
|
@@ -492,18 +498,20 @@ const getHashParamValue = (hash, param) => {
|
|
|
492
498
|
return urlParams.get(param);
|
|
493
499
|
};
|
|
494
500
|
|
|
495
|
-
const createSession = async ({credentialQuery: credentialQuery, challenge: challenge, redirectUri: redirectUri, apiBaseUrl: apiBaseUrl, walletProviderId: walletProviderId, dcApiSupported: dcApiSupported, applicationId: applicationId}) => {
|
|
501
|
+
const createSession = async ({credentialQuery: credentialQuery, challenge: challenge, redirectUri: redirectUri, apiBaseUrl: apiBaseUrl, walletProviderId: walletProviderId, dcApiSupported: dcApiSupported, applicationId: applicationId, state: state}) => {
|
|
496
502
|
const openid4vpConfiguration = !!walletProviderId || !!redirectUri ? {
|
|
497
503
|
redirectUri: redirectUri,
|
|
498
504
|
walletProviderId: walletProviderId
|
|
499
505
|
} : undefined;
|
|
500
|
-
const postData = {
|
|
506
|
+
const postData = Object.assign({
|
|
501
507
|
credentialQuery: credentialQuery,
|
|
502
508
|
challenge: challenge,
|
|
503
509
|
applicationId: applicationId,
|
|
504
510
|
dcApiSupported: dcApiSupported,
|
|
505
511
|
openid4vpConfiguration: openid4vpConfiguration
|
|
506
|
-
}
|
|
512
|
+
}, state !== undefined ? {
|
|
513
|
+
state: state
|
|
514
|
+
} : {});
|
|
507
515
|
const responseResult = await safeFetch(`${apiBaseUrl}/v2/presentations/web/sessions`, {
|
|
508
516
|
method: "POST",
|
|
509
517
|
headers: {
|
|
@@ -621,7 +629,7 @@ const closeCrossDeviceModal = options => {
|
|
|
621
629
|
};
|
|
622
630
|
|
|
623
631
|
const receiveMessageHandler = options => async event => {
|
|
624
|
-
const {onComplete: onComplete, onFailure: onFailure, container: container, sessionId: sessionId, apiBaseUrl: apiBaseUrl, challenge: challenge} = options;
|
|
632
|
+
const {onComplete: onComplete, onFailure: onFailure, container: container, sessionId: sessionId, apiBaseUrl: apiBaseUrl, challenge: challenge, state: state} = options;
|
|
625
633
|
if (event.origin !== apiBaseUrl) {
|
|
626
634
|
return;
|
|
627
635
|
}
|
|
@@ -643,10 +651,22 @@ const receiveMessageHandler = options => async event => {
|
|
|
643
651
|
});
|
|
644
652
|
return;
|
|
645
653
|
}
|
|
654
|
+
const resultState = "challenge" in result.value ? result.value.state : undefined;
|
|
655
|
+
if (state !== undefined && resultState !== undefined && state !== resultState) {
|
|
656
|
+
onFailure({
|
|
657
|
+
type: exports.RequestCredentialsErrorType.RequestCredentialsFailed,
|
|
658
|
+
message: RequestCredentialsErrorMessage.StateMismatch
|
|
659
|
+
});
|
|
660
|
+
closeCrossDeviceModal({
|
|
661
|
+
container: container
|
|
662
|
+
});
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
646
665
|
onComplete({
|
|
647
666
|
result: "challenge" in result.value ? result.value : undefined,
|
|
648
667
|
sessionId: result.value.sessionId,
|
|
649
|
-
sessionCompletedInRedirect: false
|
|
668
|
+
sessionCompletedInRedirect: false,
|
|
669
|
+
state: resultState !== null && resultState !== void 0 ? resultState : state
|
|
650
670
|
});
|
|
651
671
|
closeCrossDeviceModal({
|
|
652
672
|
container: container
|
|
@@ -692,7 +712,7 @@ const openCrossDeviceModal = options => {
|
|
|
692
712
|
};
|
|
693
713
|
|
|
694
714
|
const requestCredentialsWithCrossDevice = async options => {
|
|
695
|
-
const {challenge: challenge, apiBaseUrl: apiBaseUrl, sessionUrl: sessionUrl, sessionId: sessionId, sessionKey: sessionKey} = options;
|
|
715
|
+
const {challenge: challenge, apiBaseUrl: apiBaseUrl, sessionUrl: sessionUrl, sessionId: sessionId, sessionKey: sessionKey, state: state} = options;
|
|
696
716
|
const container = openCrossDeviceModal({
|
|
697
717
|
sessionUrl: sessionUrl
|
|
698
718
|
});
|
|
@@ -716,6 +736,7 @@ const requestCredentialsWithCrossDevice = async options => {
|
|
|
716
736
|
sessionId: sessionId,
|
|
717
737
|
apiBaseUrl: apiBaseUrl,
|
|
718
738
|
challenge: challenge,
|
|
739
|
+
state: state,
|
|
719
740
|
onComplete: data => resolve(neverthrow.ok(data)),
|
|
720
741
|
onFailure: error => resolve(neverthrow.err(error))
|
|
721
742
|
});
|
|
@@ -903,13 +924,18 @@ var SameDeviceRequestCredentialsErrorMessage;
|
|
|
903
924
|
})(SameDeviceRequestCredentialsErrorMessage || (SameDeviceRequestCredentialsErrorMessage = {}));
|
|
904
925
|
|
|
905
926
|
const requestCredentialsSameDevice = async options => {
|
|
906
|
-
const {challenge: challenge, apiBaseUrl: apiBaseUrl, applicationId: applicationId, sessionUrl: sessionUrl, sessionKey: sessionKey, sessionId: sessionId} = options;
|
|
927
|
+
const {challenge: challenge, apiBaseUrl: apiBaseUrl, applicationId: applicationId, sessionUrl: sessionUrl, sessionKey: sessionKey, sessionId: sessionId, state: state} = options;
|
|
907
928
|
const abortController = setActiveSession({
|
|
908
929
|
sessionId: sessionId,
|
|
909
930
|
sessionKey: sessionKey
|
|
910
931
|
});
|
|
911
932
|
window.localStorage.setItem(LocalStorageKey.sessionId, sessionId);
|
|
912
933
|
window.localStorage.setItem(LocalStorageKey.challenge, challenge);
|
|
934
|
+
if (state !== undefined) {
|
|
935
|
+
window.localStorage.setItem(LocalStorageKey.state, state);
|
|
936
|
+
} else {
|
|
937
|
+
window.localStorage.removeItem(LocalStorageKey.state);
|
|
938
|
+
}
|
|
913
939
|
window.location.assign(sessionUrl);
|
|
914
940
|
await sleep(SESSION_STATUS_POLLING_INITIAL_DELAY_MS);
|
|
915
941
|
const checkResult = await withRetry((async () => {
|
|
@@ -960,7 +986,7 @@ const requestCredentials = async options => {
|
|
|
960
986
|
}
|
|
961
987
|
assertType(RequestCredentialsOptionsValidator, "Invalid request credential options")(options);
|
|
962
988
|
const {apiBaseUrl: apiBaseUrl, applicationId: applicationId} = initializeOptions;
|
|
963
|
-
const {challenge: challenge = generateChallenge(), credentialQuery: credentialQuery, openid4vpConfiguration: openid4vpConfiguration} = options;
|
|
989
|
+
const {challenge: challenge = generateChallenge(), credentialQuery: credentialQuery, openid4vpConfiguration: openid4vpConfiguration, state: state} = options;
|
|
964
990
|
const dcApiSupported = isDigitalCredentialsApiSupported();
|
|
965
991
|
const openId4VpRedirectUri = deriveOpenId4vpRedirectUri(openid4vpConfiguration);
|
|
966
992
|
const createSessionResult = await createSession({
|
|
@@ -970,7 +996,8 @@ const requestCredentials = async options => {
|
|
|
970
996
|
walletProviderId: (_a = openid4vpConfiguration === null || openid4vpConfiguration === void 0 ? void 0 : openid4vpConfiguration.walletProviderId) !== null && _a !== void 0 ? _a : undefined,
|
|
971
997
|
apiBaseUrl: apiBaseUrl,
|
|
972
998
|
applicationId: applicationId,
|
|
973
|
-
dcApiSupported: dcApiSupported
|
|
999
|
+
dcApiSupported: dcApiSupported,
|
|
1000
|
+
state: state
|
|
974
1001
|
});
|
|
975
1002
|
if (createSessionResult.isErr()) {
|
|
976
1003
|
return neverthrow.err({
|
|
@@ -1006,7 +1033,8 @@ const requestCredentials = async options => {
|
|
|
1006
1033
|
applicationId: applicationId,
|
|
1007
1034
|
sessionUrl: sessionUrl,
|
|
1008
1035
|
sessionKey: sessionKey,
|
|
1009
|
-
sessionId: sessionId
|
|
1036
|
+
sessionId: sessionId,
|
|
1037
|
+
state: state
|
|
1010
1038
|
});
|
|
1011
1039
|
}
|
|
1012
1040
|
return await requestCredentialsWithCrossDevice({
|
|
@@ -1014,7 +1042,8 @@ const requestCredentials = async options => {
|
|
|
1014
1042
|
apiBaseUrl: apiBaseUrl,
|
|
1015
1043
|
sessionUrl: sessionUrl,
|
|
1016
1044
|
sessionKey: sessionKey,
|
|
1017
|
-
sessionId: sessionId
|
|
1045
|
+
sessionId: sessionId,
|
|
1046
|
+
state: state
|
|
1018
1047
|
});
|
|
1019
1048
|
};
|
|
1020
1049
|
|
|
@@ -1047,9 +1076,11 @@ var HandleRedirectCallbackErrorMessage;
|
|
|
1047
1076
|
HandleRedirectCallbackErrorMessage["FailedToFindChallenge"] = "Failed to find challenge";
|
|
1048
1077
|
HandleRedirectCallbackErrorMessage["FailedToFindActiveSession"] = "Failed to find active session";
|
|
1049
1078
|
HandleRedirectCallbackErrorMessage["FailedToGetSessionResult"] = "Failed to get session result";
|
|
1079
|
+
HandleRedirectCallbackErrorMessage["StateMismatch"] = "State mismatch between stored session and back-channel result";
|
|
1050
1080
|
})(HandleRedirectCallbackErrorMessage || (HandleRedirectCallbackErrorMessage = {}));
|
|
1051
1081
|
|
|
1052
1082
|
const handleRedirectCallback = async () => {
|
|
1083
|
+
var _a;
|
|
1053
1084
|
const initializeOptions = getInitializeOptions();
|
|
1054
1085
|
if (!initializeOptions) {
|
|
1055
1086
|
throw new Exception(InitializeErrorMessage.SdkNotInitialized);
|
|
@@ -1064,6 +1095,7 @@ const handleRedirectCallback = async () => {
|
|
|
1064
1095
|
}
|
|
1065
1096
|
const sessionId = window.localStorage.getItem(LocalStorageKey.sessionId);
|
|
1066
1097
|
const challenge = window.localStorage.getItem(LocalStorageKey.challenge);
|
|
1098
|
+
const storedState = (_a = window.localStorage.getItem(LocalStorageKey.state)) !== null && _a !== void 0 ? _a : undefined;
|
|
1067
1099
|
if (!sessionId || !challenge) {
|
|
1068
1100
|
return neverthrow.err({
|
|
1069
1101
|
type: exports.HandleRedirectCallbackErrorType.HandleRedirectCallbackFailed,
|
|
@@ -1083,9 +1115,22 @@ const handleRedirectCallback = async () => {
|
|
|
1083
1115
|
cause: result.error
|
|
1084
1116
|
});
|
|
1085
1117
|
}
|
|
1118
|
+
const resultState = "challenge" in result.value ? result.value.state : undefined;
|
|
1119
|
+
if (storedState !== undefined && resultState !== undefined && storedState !== resultState) {
|
|
1120
|
+
window.localStorage.removeItem(LocalStorageKey.challenge);
|
|
1121
|
+
window.localStorage.removeItem(LocalStorageKey.sessionId);
|
|
1122
|
+
window.localStorage.removeItem(LocalStorageKey.state);
|
|
1123
|
+
return neverthrow.err({
|
|
1124
|
+
type: exports.HandleRedirectCallbackErrorType.HandleRedirectCallbackFailed,
|
|
1125
|
+
message: HandleRedirectCallbackErrorMessage.StateMismatch
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
const state = resultState !== null && resultState !== void 0 ? resultState : storedState;
|
|
1129
|
+
window.localStorage.removeItem(LocalStorageKey.state);
|
|
1086
1130
|
return neverthrow.ok({
|
|
1087
1131
|
result: "challenge" in result.value ? result.value : undefined,
|
|
1088
|
-
sessionId: result.value.sessionId
|
|
1132
|
+
sessionId: result.value.sessionId,
|
|
1133
|
+
state: state
|
|
1089
1134
|
});
|
|
1090
1135
|
};
|
|
1091
1136
|
|