@mattrglobal/verifier-sdk-web 2.1.2-unstable.98 → 2.2.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 +259 -90
- package/dist/lib/verifier-js-no-deps.cjs.js +64 -19
- package/dist/lib/verifier-js-no-deps.cjs.js.map +1 -1
- package/dist/lib/verifier-js.cjs.js +82 -19
- package/dist/lib/verifier-js.cjs.js.map +1 -1
- package/dist/typings/verifier/handleRedirectCallback.d.ts +2 -1
- package/dist/typings/verifier/requestCredentials.d.ts +8 -0
- package/dist/typings/verifier/types/credential-presentation.d.ts +6 -0
- package/dist/typings/verifier/types/verifier-web-sdk.d.ts +26 -3
- package/dist/typings/verifier/utils.d.ts +1 -1
- package/dist/verifier-js.development.js +81 -19
- 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 +5 -3
|
@@ -11,7 +11,8 @@ export declare enum HandleRedirectCallbackErrorMessage {
|
|
|
11
11
|
FailedToFindResponseCode = "Failed to find response code",
|
|
12
12
|
FailedToFindChallenge = "Failed to find challenge",
|
|
13
13
|
FailedToFindActiveSession = "Failed to find active session",
|
|
14
|
-
FailedToGetSessionResult = "Failed to get session result"
|
|
14
|
+
FailedToGetSessionResult = "Failed to get session result",
|
|
15
|
+
StateMismatch = "State mismatch between stored session and back-channel result"
|
|
15
16
|
}
|
|
16
17
|
export type HandleRedirectCallbackError = BaseError<HandleRedirectCallbackErrorType>;
|
|
17
18
|
/**
|
|
@@ -3,6 +3,14 @@ import { RequestCredentialsError, RequestCredentialsOptions, RequestCredentialsR
|
|
|
3
3
|
/**
|
|
4
4
|
* Requests credentials based on the provided options.
|
|
5
5
|
*
|
|
6
|
+
* An optional `state` correlation reference can be supplied via {@link RequestCredentialsOptions} to link the session to a
|
|
7
|
+
* record in your own system. When provided it must be a non-empty string, and is carried through the presentation session
|
|
8
|
+
* and echoed back on the result: on {@link RequestCredentialsResponse} for cross-device flows, and via
|
|
9
|
+
* `handleRedirectCallback()` for same-device flows. Because `state` is transmitted in plain text on the wallet-facing
|
|
10
|
+
* request URI and stored in session records, it must not contain personally identifiable information (PII), credentials, or
|
|
11
|
+
* other sensitive data. When omitted, the OpenID4VP `state` claim falls back to the internal `sessionId` and no `state` is
|
|
12
|
+
* returned.
|
|
13
|
+
*
|
|
6
14
|
* @param options - The options to request credentials with {@link RequestCredentialsOptions}.
|
|
7
15
|
* @returns A promise that resolves to a result containing either the {@link RequestCredentialsResponse | requested credentials} or an {@link RequestCredentialsError | error}.
|
|
8
16
|
*/
|
|
@@ -249,6 +249,7 @@ export type PresentationSuccessResult = {
|
|
|
249
249
|
credentialQuery: CredentialQuery[];
|
|
250
250
|
credentials?: MobileCredentialPresentationCredential[];
|
|
251
251
|
credentialErrors?: MobileCredentialError[];
|
|
252
|
+
state?: string;
|
|
252
253
|
};
|
|
253
254
|
export type PresentationResultRelax = {
|
|
254
255
|
sessionId: string;
|
|
@@ -257,6 +258,7 @@ export type PresentationResultRelax = {
|
|
|
257
258
|
credentials?: unknown;
|
|
258
259
|
credentialErrors?: unknown;
|
|
259
260
|
error?: unknown;
|
|
261
|
+
state?: string;
|
|
260
262
|
};
|
|
261
263
|
export declare const PresentationResultRelaxValidator: v.ObjectSchema<{
|
|
262
264
|
readonly sessionId: v.StringSchema<undefined>;
|
|
@@ -266,6 +268,7 @@ export declare const PresentationResultRelaxValidator: v.ObjectSchema<{
|
|
|
266
268
|
readonly credentials: v.OptionalSchema<v.UnknownSchema, undefined>;
|
|
267
269
|
readonly credentialErrors: v.OptionalSchema<v.UnknownSchema, undefined>;
|
|
268
270
|
readonly error: v.OptionalSchema<v.UnknownSchema, undefined>;
|
|
271
|
+
readonly state: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
269
272
|
}, undefined>;
|
|
270
273
|
export type PresentationFailureResult = {
|
|
271
274
|
sessionId: string;
|
|
@@ -275,6 +278,7 @@ export type PresentationFailureResult = {
|
|
|
275
278
|
type: PresentationErrorType;
|
|
276
279
|
message: string;
|
|
277
280
|
};
|
|
281
|
+
state?: string;
|
|
278
282
|
};
|
|
279
283
|
/**
|
|
280
284
|
* The result of a hidden credential presentation request, retured when `resultAvailableInFrontChannel` is set to false in verifier's configuration
|
|
@@ -338,6 +342,7 @@ export declare const CreateSessionRequestValidator: v.ObjectSchema<{
|
|
|
338
342
|
readonly redirectUri: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
339
343
|
readonly walletProviderId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
340
344
|
readonly dcApiSupported: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
345
|
+
readonly state: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
341
346
|
}, undefined>;
|
|
342
347
|
export type CreateSessionResponse = v.InferOutput<typeof CreateSessionResponseValidator>;
|
|
343
348
|
export declare const CreateSessionResponseValidator: v.UnionSchema<[v.ObjectSchema<{
|
|
@@ -351,6 +356,7 @@ export declare const CreateSessionResponseValidator: v.UnionSchema<[v.ObjectSche
|
|
|
351
356
|
readonly sessionId: v.StringSchema<undefined>;
|
|
352
357
|
readonly sessionKey: v.StringSchema<undefined>;
|
|
353
358
|
readonly sessionUrl: v.StringSchema<undefined>;
|
|
359
|
+
readonly state: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
354
360
|
}, undefined>], undefined>;
|
|
355
361
|
export type AbortSessionRequest = v.InferOutput<typeof AbortSessionRequestValidator>;
|
|
356
362
|
export declare const AbortSessionRequestValidator: v.ObjectSchema<{
|
|
@@ -3,10 +3,11 @@ import { BaseError } from "../../common";
|
|
|
3
3
|
import { CredentialQuery, Mode, PresentationSessionResult } from "./credential-presentation";
|
|
4
4
|
export declare enum LocalStorageKey {
|
|
5
5
|
challenge = "mattr_chg",
|
|
6
|
-
sessionId = "mattr_sid"
|
|
6
|
+
sessionId = "mattr_sid",
|
|
7
|
+
state = "mattr_st"
|
|
7
8
|
}
|
|
8
9
|
export declare const MATTR_SDK_VERSION_HEADER = "x-mattr-sdk-version";
|
|
9
|
-
export declare const MATTR_SDK_VERSION_VALUE = "2.
|
|
10
|
+
export declare const MATTR_SDK_VERSION_VALUE = "2.2.0";
|
|
10
11
|
export declare enum MessageEventDataType {
|
|
11
12
|
PresentationCompleted = "PresentationCompleted",// { type: "PresentationCompleted", responseCode, sessionId }
|
|
12
13
|
PresentationTimeout = "PresentationTimeout",// { type: "PresentationTimeout", sessionId }
|
|
@@ -19,6 +20,7 @@ export type SameDeviceRequestCredentialsOptions = {
|
|
|
19
20
|
sessionUrl: string;
|
|
20
21
|
sessionKey: string;
|
|
21
22
|
sessionId: string;
|
|
23
|
+
state?: string;
|
|
22
24
|
};
|
|
23
25
|
export type CrossDeviceRequestCredentialsOptions = {
|
|
24
26
|
challenge: string;
|
|
@@ -26,6 +28,7 @@ export type CrossDeviceRequestCredentialsOptions = {
|
|
|
26
28
|
sessionUrl: string;
|
|
27
29
|
sessionKey: string;
|
|
28
30
|
sessionId: string;
|
|
31
|
+
state?: string;
|
|
29
32
|
};
|
|
30
33
|
/**
|
|
31
34
|
* Options for openid4vpConfiguration to request credentials via a same-device flow.
|
|
@@ -123,6 +126,12 @@ export type RequestCredentialsOptions = {
|
|
|
123
126
|
* Optional configuration for openid4vp presentation flow
|
|
124
127
|
*/
|
|
125
128
|
openid4vpConfiguration?: OpenIdvpConfiguration;
|
|
129
|
+
/**
|
|
130
|
+
* An optional customer-supplied correlation reference attached to the presentation session.
|
|
131
|
+
* When provided, replaces the internal sessionId as the OpenID4VP `state` claim and is
|
|
132
|
+
* appended as a plain query parameter to the auth request URI.
|
|
133
|
+
*/
|
|
134
|
+
state?: string;
|
|
126
135
|
};
|
|
127
136
|
export declare const RequestCredentialsOptionsValidator: v.ObjectSchema<{
|
|
128
137
|
readonly credentialQuery: v.SchemaWithPipe<readonly [v.ArraySchema<v.ObjectSchema<{
|
|
@@ -155,6 +164,7 @@ export declare const RequestCredentialsOptionsValidator: v.ObjectSchema<{
|
|
|
155
164
|
readonly redirectUri: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, "Must not be empty">, v.UrlAction<string, undefined>]>;
|
|
156
165
|
readonly mode: v.OptionalSchema<v.PicklistSchema<[Mode.CrossDevice, Mode.SameDevice], undefined>, undefined>;
|
|
157
166
|
}, undefined>], undefined>, undefined>;
|
|
167
|
+
readonly state: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, "state must not be empty">]>, undefined>;
|
|
158
168
|
}, undefined>;
|
|
159
169
|
export type MessageEvent = {
|
|
160
170
|
data: {
|
|
@@ -181,6 +191,12 @@ export type RequestCredentialsResponse = {
|
|
|
181
191
|
* Indicates that session is completed and the flow will continue on the redirected page
|
|
182
192
|
*/
|
|
183
193
|
sessionCompletedInRedirect?: boolean;
|
|
194
|
+
/**
|
|
195
|
+
* The customer-supplied correlation reference echoed back from the session.
|
|
196
|
+
* Present only when `state` was provided to `requestCredentials()`.
|
|
197
|
+
* For same-device flows, surfaced via `handleRedirectCallback()` instead.
|
|
198
|
+
*/
|
|
199
|
+
state?: string;
|
|
184
200
|
};
|
|
185
201
|
/**
|
|
186
202
|
* The error type for the requestCredentials function
|
|
@@ -199,7 +215,8 @@ export declare enum RequestCredentialsErrorMessage {
|
|
|
199
215
|
DcApiError = "Failed to request credentials with Digital Credentials API",
|
|
200
216
|
DcApiResponseParseError = "Failed to parse response from Digital Credentials API",
|
|
201
217
|
Abort = "User aborted the session",
|
|
202
|
-
Timeout = "User session timeout"
|
|
218
|
+
Timeout = "User session timeout",
|
|
219
|
+
StateMismatch = "State mismatch between requested session and back-channel result"
|
|
203
220
|
}
|
|
204
221
|
/**
|
|
205
222
|
* The error type for the `abortCredentialRequest` function
|
|
@@ -230,6 +247,12 @@ export type HandleRedirectCallbackResponse = {
|
|
|
230
247
|
* Session identifier which can be used to fetch a presentation result via a back channel.
|
|
231
248
|
*/
|
|
232
249
|
sessionId: string;
|
|
250
|
+
/**
|
|
251
|
+
* The customer-supplied correlation reference.
|
|
252
|
+
* Prefers the back-channel `result.state` value when available; falls back to the
|
|
253
|
+
* value stored in `localStorage` before the wallet redirect.
|
|
254
|
+
*/
|
|
255
|
+
state?: string;
|
|
233
256
|
};
|
|
234
257
|
/**
|
|
235
258
|
* Options for the initialize function
|
|
@@ -26,7 +26,7 @@ export declare const getHashParamValue: (hash: string, param: string) => string
|
|
|
26
26
|
* @param walletProviderId - optional, The ID of the wallet provider, if not provided the default wallet provider will be used.
|
|
27
27
|
* @returns A promise that resolves to a result containing either the created session response or an error.
|
|
28
28
|
*/
|
|
29
|
-
export declare const createSession: ({ credentialQuery, challenge, redirectUri, apiBaseUrl, walletProviderId, dcApiSupported, applicationId, }: CreateSessionRequest & InitializeOptions) => Promise<Result<CreateSessionResponse, SafeFetchValidateResponseError>>;
|
|
29
|
+
export declare const createSession: ({ credentialQuery, challenge, redirectUri, apiBaseUrl, walletProviderId, dcApiSupported, applicationId, state, }: CreateSessionRequest & InitializeOptions) => Promise<Result<CreateSessionResponse, SafeFetchValidateResponseError>>;
|
|
30
30
|
/**
|
|
31
31
|
* Abort a session with the provided parameters.
|
|
32
32
|
*
|
|
@@ -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-06-08
|
|
11
|
+
* Version: 2.2.0
|
|
12
12
|
* Dependencies:
|
|
13
13
|
*
|
|
14
14
|
* neverthrow -- 4.3.0
|
|
@@ -624,6 +624,23 @@
|
|
|
624
624
|
if (list.length > 1) return "(".concat(list.join(" ".concat(separator, " ")), ")");
|
|
625
625
|
return (_list$ = list[0]) !== null && _list$ !== void 0 ? _list$ : "never";
|
|
626
626
|
}
|
|
627
|
+
function minLength(requirement, message$1) {
|
|
628
|
+
return {
|
|
629
|
+
kind: "validation",
|
|
630
|
+
type: "min_length",
|
|
631
|
+
reference: minLength,
|
|
632
|
+
async: false,
|
|
633
|
+
expects: ">=".concat(requirement),
|
|
634
|
+
requirement: requirement,
|
|
635
|
+
message: message$1,
|
|
636
|
+
"~run": function run(dataset, config$1) {
|
|
637
|
+
if (dataset.typed && dataset.value.length < this.requirement) _addIssue(this, "length", dataset, config$1, {
|
|
638
|
+
received: "".concat(dataset.value.length)
|
|
639
|
+
});
|
|
640
|
+
return dataset;
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
}
|
|
627
644
|
function nonEmpty(message$1) {
|
|
628
645
|
return {
|
|
629
646
|
kind: "validation",
|
|
@@ -1320,7 +1337,8 @@
|
|
|
1320
1337
|
credentialQuery: optional(unknown()),
|
|
1321
1338
|
credentials: optional(unknown()),
|
|
1322
1339
|
credentialErrors: optional(unknown()),
|
|
1323
|
-
error: optional(unknown())
|
|
1340
|
+
error: optional(unknown()),
|
|
1341
|
+
state: optional(string())
|
|
1324
1342
|
});
|
|
1325
1343
|
exports.Mode = void 0;
|
|
1326
1344
|
(function(Mode) {
|
|
@@ -1337,7 +1355,8 @@
|
|
|
1337
1355
|
challenge: string(),
|
|
1338
1356
|
redirectUri: optional(string()),
|
|
1339
1357
|
walletProviderId: optional(string()),
|
|
1340
|
-
dcApiSupported: optional(_boolean())
|
|
1358
|
+
dcApiSupported: optional(_boolean()),
|
|
1359
|
+
state: optional(pipe(string(), minLength(1)))
|
|
1341
1360
|
});
|
|
1342
1361
|
const CreateSessionDigitalCredentialsValidator = object({
|
|
1343
1362
|
type: literal(SessionType.DigitalCredentialsApi),
|
|
@@ -1350,7 +1369,8 @@
|
|
|
1350
1369
|
type: optional(literal(SessionType.Openid4vp)),
|
|
1351
1370
|
sessionId: string(),
|
|
1352
1371
|
sessionKey: string(),
|
|
1353
|
-
sessionUrl: string()
|
|
1372
|
+
sessionUrl: string(),
|
|
1373
|
+
state: optional(string())
|
|
1354
1374
|
});
|
|
1355
1375
|
const CreateSessionResponseValidator = union([ CreateSessionDigitalCredentialsValidator, CreateSessionOpenId4vpResponseValidator ]);
|
|
1356
1376
|
const GetSessionStatusResponseValidator = union([ object({
|
|
@@ -1363,9 +1383,10 @@
|
|
|
1363
1383
|
(function(LocalStorageKey) {
|
|
1364
1384
|
LocalStorageKey["challenge"] = "mattr_chg";
|
|
1365
1385
|
LocalStorageKey["sessionId"] = "mattr_sid";
|
|
1386
|
+
LocalStorageKey["state"] = "mattr_st";
|
|
1366
1387
|
})(LocalStorageKey || (LocalStorageKey = {}));
|
|
1367
1388
|
const MATTR_SDK_VERSION_HEADER = "x-mattr-sdk-version";
|
|
1368
|
-
const MATTR_SDK_VERSION_VALUE = "2.
|
|
1389
|
+
const MATTR_SDK_VERSION_VALUE = "2.2.0";
|
|
1369
1390
|
var MessageEventDataType;
|
|
1370
1391
|
(function(MessageEventDataType) {
|
|
1371
1392
|
MessageEventDataType["PresentationCompleted"] = "PresentationCompleted";
|
|
@@ -1389,7 +1410,8 @@
|
|
|
1389
1410
|
const RequestCredentialsOptionsValidator = object({
|
|
1390
1411
|
credentialQuery: pipe(array(CredentialQueryValidator), nonEmpty()),
|
|
1391
1412
|
challenge: optional(string()),
|
|
1392
|
-
openid4vpConfiguration: optional(union([ OpenId4vpConfigSameDeviceOptionsValidator, OpenId4vpConfigCrossDeviceOptionsValidator, OpenId4vpConfigAutoDetectOptionsValidator ]))
|
|
1413
|
+
openid4vpConfiguration: optional(union([ OpenId4vpConfigSameDeviceOptionsValidator, OpenId4vpConfigCrossDeviceOptionsValidator, OpenId4vpConfigAutoDetectOptionsValidator ])),
|
|
1414
|
+
state: optional(pipe(string(), minLength(1, "state must not be empty")))
|
|
1393
1415
|
});
|
|
1394
1416
|
exports.RequestCredentialsErrorType = void 0;
|
|
1395
1417
|
(function(RequestCredentialsErrorType) {
|
|
@@ -1408,6 +1430,7 @@
|
|
|
1408
1430
|
RequestCredentialsErrorMessage["DcApiResponseParseError"] = "Failed to parse response from Digital Credentials API";
|
|
1409
1431
|
RequestCredentialsErrorMessage["Abort"] = "User aborted the session";
|
|
1410
1432
|
RequestCredentialsErrorMessage["Timeout"] = "User session timeout";
|
|
1433
|
+
RequestCredentialsErrorMessage["StateMismatch"] = "State mismatch between requested session and back-channel result";
|
|
1411
1434
|
})(RequestCredentialsErrorMessage || (RequestCredentialsErrorMessage = {}));
|
|
1412
1435
|
exports.AbortSessionErrorType = void 0;
|
|
1413
1436
|
(function(AbortSessionErrorType) {
|
|
@@ -1590,18 +1613,20 @@
|
|
|
1590
1613
|
const urlParams = new URLSearchParams(hash.split("#")[1]);
|
|
1591
1614
|
return urlParams.get(param);
|
|
1592
1615
|
};
|
|
1593
|
-
const createSession = async ({credentialQuery: credentialQuery, challenge: challenge, redirectUri: redirectUri, apiBaseUrl: apiBaseUrl, walletProviderId: walletProviderId, dcApiSupported: dcApiSupported, applicationId: applicationId}) => {
|
|
1616
|
+
const createSession = async ({credentialQuery: credentialQuery, challenge: challenge, redirectUri: redirectUri, apiBaseUrl: apiBaseUrl, walletProviderId: walletProviderId, dcApiSupported: dcApiSupported, applicationId: applicationId, state: state}) => {
|
|
1594
1617
|
const openid4vpConfiguration = !!walletProviderId || !!redirectUri ? {
|
|
1595
1618
|
redirectUri: redirectUri,
|
|
1596
1619
|
walletProviderId: walletProviderId
|
|
1597
1620
|
} : undefined;
|
|
1598
|
-
const postData = {
|
|
1621
|
+
const postData = Object.assign({
|
|
1599
1622
|
credentialQuery: credentialQuery,
|
|
1600
1623
|
challenge: challenge,
|
|
1601
1624
|
applicationId: applicationId,
|
|
1602
1625
|
dcApiSupported: dcApiSupported,
|
|
1603
1626
|
openid4vpConfiguration: openid4vpConfiguration
|
|
1604
|
-
}
|
|
1627
|
+
}, state !== undefined ? {
|
|
1628
|
+
state: state
|
|
1629
|
+
} : {});
|
|
1605
1630
|
const responseResult = await safeFetch(`${apiBaseUrl}/v2/presentations/web/sessions`, {
|
|
1606
1631
|
method: "POST",
|
|
1607
1632
|
headers: {
|
|
@@ -1708,7 +1733,7 @@
|
|
|
1708
1733
|
removeWindowMessageEventListener();
|
|
1709
1734
|
};
|
|
1710
1735
|
const receiveMessageHandler = options => async event => {
|
|
1711
|
-
const {onComplete: onComplete, onFailure: onFailure, container: container, sessionId: sessionId, apiBaseUrl: apiBaseUrl, challenge: challenge} = options;
|
|
1736
|
+
const {onComplete: onComplete, onFailure: onFailure, container: container, sessionId: sessionId, apiBaseUrl: apiBaseUrl, challenge: challenge, state: state} = options;
|
|
1712
1737
|
if (event.origin !== apiBaseUrl) {
|
|
1713
1738
|
return;
|
|
1714
1739
|
}
|
|
@@ -1730,10 +1755,22 @@
|
|
|
1730
1755
|
});
|
|
1731
1756
|
return;
|
|
1732
1757
|
}
|
|
1758
|
+
const resultState = "challenge" in result.value ? result.value.state : undefined;
|
|
1759
|
+
if (state !== undefined && resultState !== undefined && state !== resultState) {
|
|
1760
|
+
onFailure({
|
|
1761
|
+
type: exports.RequestCredentialsErrorType.RequestCredentialsFailed,
|
|
1762
|
+
message: RequestCredentialsErrorMessage.StateMismatch
|
|
1763
|
+
});
|
|
1764
|
+
closeCrossDeviceModal({
|
|
1765
|
+
container: container
|
|
1766
|
+
});
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1733
1769
|
onComplete({
|
|
1734
1770
|
result: "challenge" in result.value ? result.value : undefined,
|
|
1735
1771
|
sessionId: result.value.sessionId,
|
|
1736
|
-
sessionCompletedInRedirect: false
|
|
1772
|
+
sessionCompletedInRedirect: false,
|
|
1773
|
+
state: resultState !== null && resultState !== void 0 ? resultState : state
|
|
1737
1774
|
});
|
|
1738
1775
|
closeCrossDeviceModal({
|
|
1739
1776
|
container: container
|
|
@@ -1777,7 +1814,7 @@
|
|
|
1777
1814
|
return modalContainer;
|
|
1778
1815
|
};
|
|
1779
1816
|
const requestCredentialsWithCrossDevice = async options => {
|
|
1780
|
-
const {challenge: challenge, apiBaseUrl: apiBaseUrl, sessionUrl: sessionUrl, sessionId: sessionId, sessionKey: sessionKey} = options;
|
|
1817
|
+
const {challenge: challenge, apiBaseUrl: apiBaseUrl, sessionUrl: sessionUrl, sessionId: sessionId, sessionKey: sessionKey, state: state} = options;
|
|
1781
1818
|
const container = openCrossDeviceModal({
|
|
1782
1819
|
sessionUrl: sessionUrl
|
|
1783
1820
|
});
|
|
@@ -1801,6 +1838,7 @@
|
|
|
1801
1838
|
sessionId: sessionId,
|
|
1802
1839
|
apiBaseUrl: apiBaseUrl,
|
|
1803
1840
|
challenge: challenge,
|
|
1841
|
+
state: state,
|
|
1804
1842
|
onComplete: data => resolve(ok(data)),
|
|
1805
1843
|
onFailure: error => resolve(err(error))
|
|
1806
1844
|
});
|
|
@@ -1975,13 +2013,18 @@
|
|
|
1975
2013
|
SameDeviceRequestCredentialsErrorMessage["FailedToCreateSession"] = "Failed to create session";
|
|
1976
2014
|
})(SameDeviceRequestCredentialsErrorMessage || (SameDeviceRequestCredentialsErrorMessage = {}));
|
|
1977
2015
|
const requestCredentialsSameDevice = async options => {
|
|
1978
|
-
const {challenge: challenge, apiBaseUrl: apiBaseUrl, applicationId: applicationId, sessionUrl: sessionUrl, sessionKey: sessionKey, sessionId: sessionId} = options;
|
|
2016
|
+
const {challenge: challenge, apiBaseUrl: apiBaseUrl, applicationId: applicationId, sessionUrl: sessionUrl, sessionKey: sessionKey, sessionId: sessionId, state: state} = options;
|
|
1979
2017
|
const abortController = setActiveSession({
|
|
1980
2018
|
sessionId: sessionId,
|
|
1981
2019
|
sessionKey: sessionKey
|
|
1982
2020
|
});
|
|
1983
2021
|
window.localStorage.setItem(LocalStorageKey.sessionId, sessionId);
|
|
1984
2022
|
window.localStorage.setItem(LocalStorageKey.challenge, challenge);
|
|
2023
|
+
if (state !== undefined) {
|
|
2024
|
+
window.localStorage.setItem(LocalStorageKey.state, state);
|
|
2025
|
+
} else {
|
|
2026
|
+
window.localStorage.removeItem(LocalStorageKey.state);
|
|
2027
|
+
}
|
|
1985
2028
|
window.location.assign(sessionUrl);
|
|
1986
2029
|
await sleep(SESSION_STATUS_POLLING_INITIAL_DELAY_MS);
|
|
1987
2030
|
const checkResult = await withRetry((async () => {
|
|
@@ -2031,7 +2074,7 @@
|
|
|
2031
2074
|
}
|
|
2032
2075
|
assertType(RequestCredentialsOptionsValidator, "Invalid request credential options")(options);
|
|
2033
2076
|
const {apiBaseUrl: apiBaseUrl, applicationId: applicationId} = initializeOptions;
|
|
2034
|
-
const {challenge: challenge = generateChallenge(), credentialQuery: credentialQuery, openid4vpConfiguration: openid4vpConfiguration} = options;
|
|
2077
|
+
const {challenge: challenge = generateChallenge(), credentialQuery: credentialQuery, openid4vpConfiguration: openid4vpConfiguration, state: state} = options;
|
|
2035
2078
|
const dcApiSupported = isDigitalCredentialsApiSupported();
|
|
2036
2079
|
const openId4VpRedirectUri = deriveOpenId4vpRedirectUri(openid4vpConfiguration);
|
|
2037
2080
|
const createSessionResult = await createSession({
|
|
@@ -2041,7 +2084,8 @@
|
|
|
2041
2084
|
walletProviderId: (_a = openid4vpConfiguration === null || openid4vpConfiguration === void 0 ? void 0 : openid4vpConfiguration.walletProviderId) !== null && _a !== void 0 ? _a : undefined,
|
|
2042
2085
|
apiBaseUrl: apiBaseUrl,
|
|
2043
2086
|
applicationId: applicationId,
|
|
2044
|
-
dcApiSupported: dcApiSupported
|
|
2087
|
+
dcApiSupported: dcApiSupported,
|
|
2088
|
+
state: state
|
|
2045
2089
|
});
|
|
2046
2090
|
if (createSessionResult.isErr()) {
|
|
2047
2091
|
return err({
|
|
@@ -2077,7 +2121,8 @@
|
|
|
2077
2121
|
applicationId: applicationId,
|
|
2078
2122
|
sessionUrl: sessionUrl,
|
|
2079
2123
|
sessionKey: sessionKey,
|
|
2080
|
-
sessionId: sessionId
|
|
2124
|
+
sessionId: sessionId,
|
|
2125
|
+
state: state
|
|
2081
2126
|
});
|
|
2082
2127
|
}
|
|
2083
2128
|
return await requestCredentialsWithCrossDevice({
|
|
@@ -2085,7 +2130,8 @@
|
|
|
2085
2130
|
apiBaseUrl: apiBaseUrl,
|
|
2086
2131
|
sessionUrl: sessionUrl,
|
|
2087
2132
|
sessionKey: sessionKey,
|
|
2088
|
-
sessionId: sessionId
|
|
2133
|
+
sessionId: sessionId,
|
|
2134
|
+
state: state
|
|
2089
2135
|
});
|
|
2090
2136
|
};
|
|
2091
2137
|
const deriveOpenId4vpRedirectUri = openid4vpConfiguration => {
|
|
@@ -2113,8 +2159,10 @@
|
|
|
2113
2159
|
HandleRedirectCallbackErrorMessage["FailedToFindChallenge"] = "Failed to find challenge";
|
|
2114
2160
|
HandleRedirectCallbackErrorMessage["FailedToFindActiveSession"] = "Failed to find active session";
|
|
2115
2161
|
HandleRedirectCallbackErrorMessage["FailedToGetSessionResult"] = "Failed to get session result";
|
|
2162
|
+
HandleRedirectCallbackErrorMessage["StateMismatch"] = "State mismatch between stored session and back-channel result";
|
|
2116
2163
|
})(HandleRedirectCallbackErrorMessage || (HandleRedirectCallbackErrorMessage = {}));
|
|
2117
2164
|
const handleRedirectCallback = async () => {
|
|
2165
|
+
var _a;
|
|
2118
2166
|
const initializeOptions = getInitializeOptions();
|
|
2119
2167
|
if (!initializeOptions) {
|
|
2120
2168
|
throw new Exception(InitializeErrorMessage.SdkNotInitialized);
|
|
@@ -2129,6 +2177,7 @@
|
|
|
2129
2177
|
}
|
|
2130
2178
|
const sessionId = window.localStorage.getItem(LocalStorageKey.sessionId);
|
|
2131
2179
|
const challenge = window.localStorage.getItem(LocalStorageKey.challenge);
|
|
2180
|
+
const storedState = (_a = window.localStorage.getItem(LocalStorageKey.state)) !== null && _a !== void 0 ? _a : undefined;
|
|
2132
2181
|
if (!sessionId || !challenge) {
|
|
2133
2182
|
return err({
|
|
2134
2183
|
type: exports.HandleRedirectCallbackErrorType.HandleRedirectCallbackFailed,
|
|
@@ -2148,9 +2197,22 @@
|
|
|
2148
2197
|
cause: result.error
|
|
2149
2198
|
});
|
|
2150
2199
|
}
|
|
2200
|
+
const resultState = "challenge" in result.value ? result.value.state : undefined;
|
|
2201
|
+
if (storedState !== undefined && resultState !== undefined && storedState !== resultState) {
|
|
2202
|
+
window.localStorage.removeItem(LocalStorageKey.challenge);
|
|
2203
|
+
window.localStorage.removeItem(LocalStorageKey.sessionId);
|
|
2204
|
+
window.localStorage.removeItem(LocalStorageKey.state);
|
|
2205
|
+
return err({
|
|
2206
|
+
type: exports.HandleRedirectCallbackErrorType.HandleRedirectCallbackFailed,
|
|
2207
|
+
message: HandleRedirectCallbackErrorMessage.StateMismatch
|
|
2208
|
+
});
|
|
2209
|
+
}
|
|
2210
|
+
const state = resultState !== null && resultState !== void 0 ? resultState : storedState;
|
|
2211
|
+
window.localStorage.removeItem(LocalStorageKey.state);
|
|
2151
2212
|
return ok({
|
|
2152
2213
|
result: "challenge" in result.value ? result.value : undefined,
|
|
2153
|
-
sessionId: result.value.sessionId
|
|
2214
|
+
sessionId: result.value.sessionId,
|
|
2215
|
+
state: state
|
|
2154
2216
|
});
|
|
2155
2217
|
};
|
|
2156
2218
|
const utils = {
|