@metamask-previews/passkey-controller 2.1.0-preview-789f7f935 → 2.1.0-preview-eade524
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/CHANGELOG.md +21 -0
- package/README.md +85 -20
- package/dist/PasskeyController-method-action-types.cjs.map +1 -1
- package/dist/PasskeyController-method-action-types.d.cts +85 -7
- package/dist/PasskeyController-method-action-types.d.cts.map +1 -1
- package/dist/PasskeyController-method-action-types.d.mts +85 -7
- package/dist/PasskeyController-method-action-types.d.mts.map +1 -1
- package/dist/PasskeyController-method-action-types.mjs.map +1 -1
- package/dist/PasskeyController.cjs +326 -164
- package/dist/PasskeyController.cjs.map +1 -1
- package/dist/PasskeyController.d.cts +88 -50
- package/dist/PasskeyController.d.cts.map +1 -1
- package/dist/PasskeyController.d.mts +88 -50
- package/dist/PasskeyController.d.mts.map +1 -1
- package/dist/PasskeyController.mjs +326 -164
- package/dist/PasskeyController.mjs.map +1 -1
- package/dist/constants.cjs +3 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +4 -1
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.mts +4 -1
- package/dist/constants.d.mts.map +1 -1
- package/dist/constants.mjs +3 -0
- package/dist/constants.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/types.cjs +1 -0
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +38 -0
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +38 -0
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +4 -2
|
@@ -9,9 +9,10 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _PasskeyController_instances, _PasskeyController_ceremonyManager, _PasskeyController_expectedRPIDs, _PasskeyController_rpId, _PasskeyController_rpName, _PasskeyController_expectedOrigin, _PasskeyController_userName, _PasskeyController_userDisplayName, _PasskeyController_requireEnrolled, _PasskeyController_getChallengeFromClientData,
|
|
12
|
+
var _PasskeyController_instances, _PasskeyController_ceremonyManager, _PasskeyController_expectedRPIDs, _PasskeyController_rpId, _PasskeyController_rpName, _PasskeyController_expectedOrigin, _PasskeyController_userName, _PasskeyController_userDisplayName, _PasskeyController_getIsOnboardingCompleted, _PasskeyController_operationMutex, _PasskeyController_protectVaultKeyWithPasskey, _PasskeyController_retrieveVaultKeyWithPasskey, _PasskeyController_verifyPasskeyAuthentication, _PasskeyController_renewVaultKeyProtection, _PasskeyController_changePasswordWithPasskeyVerification, _PasskeyController_removePasskeyWithPasskeyVerification, _PasskeyController_removePasskeyWithPasswordVerification, _PasskeyController_verifyAuthenticationResponse, _PasskeyController_withOperationLock, _PasskeyController_assertEnrollmentAllowed, _PasskeyController_requireEnrolled, _PasskeyController_getChallengeFromClientData, _PasskeyController_removePasskey;
|
|
13
13
|
import { BaseController } from "@metamask/base-controller";
|
|
14
14
|
import { areUint8ArraysEqual, stringToBytes } from "@metamask/utils";
|
|
15
|
+
import { Mutex } from "async-mutex";
|
|
15
16
|
import { WEBAUTHN_TIMEOUT_MS, CeremonyManager } from "./ceremony-manager.mjs";
|
|
16
17
|
import { controllerName, PasskeyControllerErrorCode, PasskeyControllerErrorMessage } from "./constants.mjs";
|
|
17
18
|
import { PasskeyControllerError } from "./errors.mjs";
|
|
@@ -57,9 +58,14 @@ const MESSENGER_EXPOSED_METHODS = [
|
|
|
57
58
|
'generateAuthenticationOptions',
|
|
58
59
|
'protectVaultKeyWithPasskey',
|
|
59
60
|
'retrieveVaultKeyWithPasskey',
|
|
61
|
+
'unlockWithPasskey',
|
|
60
62
|
'verifyPasskeyAuthentication',
|
|
61
63
|
'renewVaultKeyProtection',
|
|
62
|
-
'
|
|
64
|
+
'changePasswordWithPasskeyVerification',
|
|
65
|
+
'exportSeedPhraseWithPasskey',
|
|
66
|
+
'exportAccountsWithPasskey',
|
|
67
|
+
'removePasskeyWithPasskeyVerification',
|
|
68
|
+
'removePasskeyWithPasswordVerification',
|
|
63
69
|
'clearState',
|
|
64
70
|
'destroy',
|
|
65
71
|
];
|
|
@@ -71,20 +77,18 @@ export class PasskeyController extends BaseController {
|
|
|
71
77
|
/**
|
|
72
78
|
* Creates a passkey controller with WebAuthn relying-party settings.
|
|
73
79
|
*
|
|
74
|
-
* @param
|
|
75
|
-
* @param
|
|
76
|
-
* @param
|
|
77
|
-
* @param
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* @param
|
|
81
|
-
*
|
|
82
|
-
* @param
|
|
83
|
-
* @param
|
|
84
|
-
* @param args.userName - Optional passkey user name; defaults to `rpName`.
|
|
85
|
-
* @param args.userDisplayName - Optional display name; defaults to `rpName`.
|
|
80
|
+
* @param options - Constructor options.
|
|
81
|
+
* @param options.messenger - The messenger to use for communication.
|
|
82
|
+
* @param options.state - The initial state of the controller.
|
|
83
|
+
* @param options.rpId - The relying party ID to use for the passkey.
|
|
84
|
+
* @param options.expectedRPID - The expected relying party ID to use for the passkey.
|
|
85
|
+
* @param options.rpName - The relying party name to use for the passkey.
|
|
86
|
+
* @param options.expectedOrigin - The expected origin to use for the passkey.
|
|
87
|
+
* @param options.userName - The user name to use for the passkey.
|
|
88
|
+
* @param options.userDisplayName - The user display name to use for the passkey.
|
|
89
|
+
* @param options.getIsOnboardingCompleted - The callback to use to check if onboarding is complete.
|
|
86
90
|
*/
|
|
87
|
-
constructor({ messenger, state = {}, rpId, expectedRPID, rpName, expectedOrigin, userName, userDisplayName, }) {
|
|
91
|
+
constructor({ messenger, state = {}, rpId, expectedRPID, rpName, expectedOrigin, userName, userDisplayName, getIsOnboardingCompleted, }) {
|
|
88
92
|
super({
|
|
89
93
|
messenger,
|
|
90
94
|
metadata: passkeyControllerMetadata,
|
|
@@ -99,6 +103,8 @@ export class PasskeyController extends BaseController {
|
|
|
99
103
|
_PasskeyController_expectedOrigin.set(this, void 0);
|
|
100
104
|
_PasskeyController_userName.set(this, void 0);
|
|
101
105
|
_PasskeyController_userDisplayName.set(this, void 0);
|
|
106
|
+
_PasskeyController_getIsOnboardingCompleted.set(this, void 0);
|
|
107
|
+
_PasskeyController_operationMutex.set(this, new Mutex());
|
|
102
108
|
const expectedRPIDs = Array.isArray(expectedRPID)
|
|
103
109
|
? expectedRPID
|
|
104
110
|
: [expectedRPID];
|
|
@@ -108,6 +114,7 @@ export class PasskeyController extends BaseController {
|
|
|
108
114
|
__classPrivateFieldSet(this, _PasskeyController_expectedOrigin, expectedOrigin, "f");
|
|
109
115
|
__classPrivateFieldSet(this, _PasskeyController_userName, userName ?? rpName, "f");
|
|
110
116
|
__classPrivateFieldSet(this, _PasskeyController_userDisplayName, userDisplayName ?? rpName, "f");
|
|
117
|
+
__classPrivateFieldSet(this, _PasskeyController_getIsOnboardingCompleted, getIsOnboardingCompleted, "f");
|
|
111
118
|
this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
112
119
|
}
|
|
113
120
|
/**
|
|
@@ -253,113 +260,72 @@ export class PasskeyController extends BaseController {
|
|
|
253
260
|
* Verifies registration and post-registration authentication, then stores the
|
|
254
261
|
* vault key encrypted under the new passkey.
|
|
255
262
|
*
|
|
263
|
+
* Fetches the current vault encryption key from KeyringController before wrapping.
|
|
264
|
+
* When onboarding is complete, requires `password` for step-up verification first.
|
|
265
|
+
*
|
|
256
266
|
* @param params - Enrollment completion inputs.
|
|
257
267
|
* @param params.registrationResponse - Result of `navigator.credentials.create()`.
|
|
258
268
|
* @param params.authenticationResponse - Result of `navigator.credentials.get()` after {@link generatePostRegistrationAuthenticationOptions}.
|
|
259
|
-
* @param params.
|
|
269
|
+
* @param params.password - Wallet password when onboarding is complete (step-up).
|
|
270
|
+
* @returns Resolves when enrollment completes.
|
|
260
271
|
*/
|
|
261
272
|
async protectVaultKeyWithPasskey(params) {
|
|
262
|
-
|
|
263
|
-
throw new PasskeyControllerError(PasskeyControllerErrorMessage.AlreadyEnrolled, { code: PasskeyControllerErrorCode.AlreadyEnrolled });
|
|
264
|
-
}
|
|
265
|
-
const { registrationResponse, authenticationResponse, vaultKey } = params;
|
|
266
|
-
// get registration ceremony
|
|
267
|
-
const challenge = __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_getChallengeFromClientData).call(this, registrationResponse.response.clientDataJSON);
|
|
268
|
-
const registrationCeremony = __classPrivateFieldGet(this, _PasskeyController_ceremonyManager, "f").getRegistrationCeremony(challenge);
|
|
269
|
-
if (!registrationCeremony) {
|
|
270
|
-
log('No active passkey registration ceremony for challenge');
|
|
271
|
-
throw new PasskeyControllerError(PasskeyControllerErrorMessage.NoRegistrationCeremony, { code: PasskeyControllerErrorCode.NoRegistrationCeremony });
|
|
272
|
-
}
|
|
273
|
-
try {
|
|
274
|
-
// verify registration response
|
|
275
|
-
const { verified, registrationInfo } = await verifyRegistrationResponse({
|
|
276
|
-
response: registrationResponse,
|
|
277
|
-
expectedChallenge: registrationCeremony.challenge,
|
|
278
|
-
expectedOrigin: __classPrivateFieldGet(this, _PasskeyController_expectedOrigin, "f"),
|
|
279
|
-
expectedRPIDs: __classPrivateFieldGet(this, _PasskeyController_expectedRPIDs, "f"),
|
|
280
|
-
requireUserVerification: true,
|
|
281
|
-
}).catch((error) => {
|
|
282
|
-
log('Error verifying passkey registration response', error);
|
|
283
|
-
throw new PasskeyControllerError(PasskeyControllerErrorMessage.RegistrationVerificationFailed, {
|
|
284
|
-
code: PasskeyControllerErrorCode.RegistrationVerificationFailed,
|
|
285
|
-
cause: error instanceof Error ? error : new Error(String(error)),
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
if (!verified || !registrationInfo) {
|
|
289
|
-
log('Passkey registration verification returned unverified or missing registration info');
|
|
290
|
-
throw new PasskeyControllerError(PasskeyControllerErrorMessage.RegistrationVerificationFailed, { code: PasskeyControllerErrorCode.RegistrationVerificationFailed });
|
|
291
|
-
}
|
|
292
|
-
// verify authentication response
|
|
293
|
-
const credential = {
|
|
294
|
-
id: registrationInfo.credentialId,
|
|
295
|
-
publicKey: bytesToBase64URL(registrationInfo.publicKey),
|
|
296
|
-
counter: registrationInfo.counter,
|
|
297
|
-
transports: registrationInfo.transports,
|
|
298
|
-
aaguid: registrationInfo.aaguid,
|
|
299
|
-
};
|
|
300
|
-
const { newCounter } = await __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_verifyAuthenticationResponse).call(this, authenticationResponse, credential);
|
|
301
|
-
// determine key derivation method
|
|
302
|
-
const prfFirst = authenticationResponse.clientExtensionResults?.prf?.results?.first;
|
|
303
|
-
const authHasPrfOutput = typeof prfFirst === 'string' && prfFirst.length > 0;
|
|
304
|
-
const keyDerivation = authHasPrfOutput && registrationCeremony.prfSalt
|
|
305
|
-
? { method: 'prf', prfSalt: registrationCeremony.prfSalt }
|
|
306
|
-
: { method: 'userHandle' };
|
|
307
|
-
if (keyDerivation.method === 'userHandle' &&
|
|
308
|
-
authenticationResponse.response.userHandle !==
|
|
309
|
-
registrationCeremony.userHandle) {
|
|
310
|
-
log('Post-registration assertion userHandle does not match registration ceremony');
|
|
311
|
-
throw new PasskeyControllerError(PasskeyControllerErrorMessage.AuthenticationVerificationFailed, { code: PasskeyControllerErrorCode.AuthenticationVerificationFailed });
|
|
312
|
-
}
|
|
313
|
-
// derive key and encrypt vault key
|
|
314
|
-
const encKey = deriveKeyFromAuthenticationResponse(authenticationResponse, { credential, keyDerivation });
|
|
315
|
-
const { ciphertext, iv } = encryptWithKey(vaultKey, encKey);
|
|
316
|
-
// persist passkey record
|
|
317
|
-
this.update((state) => {
|
|
318
|
-
state.passkeyRecord = {
|
|
319
|
-
credential: {
|
|
320
|
-
...credential,
|
|
321
|
-
counter: Math.max(newCounter, credential.counter),
|
|
322
|
-
},
|
|
323
|
-
encryptedVaultKey: { ciphertext, iv },
|
|
324
|
-
keyDerivation,
|
|
325
|
-
};
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
finally {
|
|
329
|
-
// delete registration ceremony
|
|
330
|
-
__classPrivateFieldGet(this, _PasskeyController_ceremonyManager, "f").deleteRegistrationCeremony(challenge);
|
|
331
|
-
}
|
|
273
|
+
return __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_withOperationLock).call(this, () => __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_protectVaultKeyWithPasskey).call(this, params));
|
|
332
274
|
}
|
|
333
275
|
/**
|
|
334
276
|
* Verifies an authentication assertion and returns the decrypted vault key.
|
|
335
277
|
*
|
|
278
|
+
* Prefer orchestrated methods ({@link unlockWithPasskey},
|
|
279
|
+
* {@link exportSeedPhraseWithPasskey}, {@link exportAccountsWithPasskey}) for product
|
|
280
|
+
* flows instead of calling KeyringController with the returned key manually.
|
|
281
|
+
*
|
|
336
282
|
* @param authenticationResponse - Result of `navigator.credentials.get()`.
|
|
337
283
|
* @returns The plaintext vault encryption key.
|
|
338
284
|
*/
|
|
339
285
|
async retrieveVaultKeyWithPasskey(authenticationResponse) {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
286
|
+
return __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_withOperationLock).call(this, () => __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_retrieveVaultKeyWithPasskey).call(this, authenticationResponse));
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Unlocks the keyring using a passkey authentication assertion.
|
|
290
|
+
*
|
|
291
|
+
* @param authenticationResponse - Result of `navigator.credentials.get()`.
|
|
292
|
+
* @returns Resolves when the keyring is unlocked.
|
|
293
|
+
*/
|
|
294
|
+
async unlockWithPasskey(authenticationResponse) {
|
|
295
|
+
return __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_withOperationLock).call(this, async () => {
|
|
296
|
+
const vaultKey = await __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_retrieveVaultKeyWithPasskey).call(this, authenticationResponse);
|
|
297
|
+
await this.messenger.call('KeyringController:submitEncryptionKey', vaultKey);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Exports the seed phrase after passkey step-up authentication.
|
|
302
|
+
*
|
|
303
|
+
* @param authenticationResponse - Result of `navigator.credentials.get()`.
|
|
304
|
+
* @param keyringId - Optional keyring id; defaults to the primary HD keyring.
|
|
305
|
+
* @returns Raw seed phrase bytes from KeyringController.
|
|
306
|
+
*/
|
|
307
|
+
async exportSeedPhraseWithPasskey(authenticationResponse, keyringId) {
|
|
308
|
+
return __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_withOperationLock).call(this, async () => {
|
|
309
|
+
const vaultKey = await __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_retrieveVaultKeyWithPasskey).call(this, authenticationResponse);
|
|
310
|
+
return await this.messenger.call('KeyringController:exportSeedPhrase', { encryptionKey: vaultKey }, keyringId);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Exports private keys for the given addresses after passkey step-up authentication.
|
|
315
|
+
*
|
|
316
|
+
* @param authenticationResponse - Result of `navigator.credentials.get()`.
|
|
317
|
+
* @param addresses - Account addresses to export.
|
|
318
|
+
* @returns Private keys in the same order as `addresses`.
|
|
319
|
+
*/
|
|
320
|
+
async exportAccountsWithPasskey(authenticationResponse, addresses) {
|
|
321
|
+
return __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_withOperationLock).call(this, async () => {
|
|
322
|
+
const vaultKey = await __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_retrieveVaultKeyWithPasskey).call(this, authenticationResponse);
|
|
323
|
+
const privateKeys = [];
|
|
324
|
+
for (const address of addresses) {
|
|
325
|
+
privateKeys.push(await this.messenger.call('KeyringController:exportAccount', { encryptionKey: vaultKey }, address));
|
|
346
326
|
}
|
|
347
|
-
|
|
327
|
+
return privateKeys;
|
|
348
328
|
});
|
|
349
|
-
// derive key
|
|
350
|
-
const encKey = deriveKeyFromAuthenticationResponse(authenticationResponse, passkeyRecord);
|
|
351
|
-
// decrypt vault key
|
|
352
|
-
try {
|
|
353
|
-
const vaultKey = decryptWithKey(passkeyRecord.encryptedVaultKey.ciphertext, passkeyRecord.encryptedVaultKey.iv, encKey);
|
|
354
|
-
return vaultKey;
|
|
355
|
-
}
|
|
356
|
-
catch (cause) {
|
|
357
|
-
log('Error decrypting vault key with passkey', cause instanceof Error ? cause : new Error(String(cause)));
|
|
358
|
-
throw new PasskeyControllerError(PasskeyControllerErrorMessage.VaultKeyDecryptionFailed, {
|
|
359
|
-
code: PasskeyControllerErrorCode.VaultKeyDecryptionFailed,
|
|
360
|
-
cause: cause instanceof Error ? cause : new Error(String(cause)),
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
329
|
}
|
|
364
330
|
/**
|
|
365
331
|
* Checks whether the given authentication assertion is valid for the enrolled passkey.
|
|
@@ -371,16 +337,7 @@ export class PasskeyController extends BaseController {
|
|
|
371
337
|
* @returns `true` if verification succeeds, otherwise `false`.
|
|
372
338
|
*/
|
|
373
339
|
async verifyPasskeyAuthentication(authenticationResponse) {
|
|
374
|
-
|
|
375
|
-
await this.retrieveVaultKeyWithPasskey(authenticationResponse);
|
|
376
|
-
return true;
|
|
377
|
-
}
|
|
378
|
-
catch (error) {
|
|
379
|
-
if (error instanceof PasskeyControllerError && error.code !== undefined) {
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
throw error;
|
|
383
|
-
}
|
|
340
|
+
return __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_withOperationLock).call(this, () => __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_verifyPasskeyAuthentication).call(this, authenticationResponse));
|
|
384
341
|
}
|
|
385
342
|
/**
|
|
386
343
|
* Re-wraps the vault key after rotation. Updates persisted `encryptedVaultKey` on success.
|
|
@@ -390,59 +347,62 @@ export class PasskeyController extends BaseController {
|
|
|
390
347
|
* pass the same `authenticationResponse` you just verified (e.g. from
|
|
391
348
|
* {@link retrieveVaultKeyWithPasskey} / {@link verifyPasskeyAuthentication}).
|
|
392
349
|
*
|
|
350
|
+
* For password change with passkey step-up, prefer
|
|
351
|
+
* {@link changePasswordWithPasskeyVerification}, which orchestrates keyring export,
|
|
352
|
+
* `changePassword`, and re-wrap in one call.
|
|
353
|
+
*
|
|
393
354
|
* @param params - Re-wrap inputs.
|
|
394
355
|
* @param params.authenticationResponse - Used to derive the wrapping key.
|
|
395
356
|
* @param params.oldVaultKey - Expected current vault key.
|
|
396
357
|
* @param params.newVaultKey - New vault key to encrypt under the passkey.
|
|
358
|
+
* @returns Resolves when the passkey record is updated.
|
|
397
359
|
*/
|
|
398
360
|
async renewVaultKeyProtection(params) {
|
|
399
|
-
|
|
400
|
-
const passkeyRecord = __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_requireEnrolled).call(this);
|
|
401
|
-
// derive key
|
|
402
|
-
const encKey = deriveKeyFromAuthenticationResponse(authenticationResponse, passkeyRecord);
|
|
403
|
-
// decrypt vault key
|
|
404
|
-
let decryptedVaultKey;
|
|
405
|
-
try {
|
|
406
|
-
decryptedVaultKey = decryptWithKey(passkeyRecord.encryptedVaultKey.ciphertext, passkeyRecord.encryptedVaultKey.iv, encKey);
|
|
407
|
-
}
|
|
408
|
-
catch (error) {
|
|
409
|
-
log('Error decrypting vault key during passkey vault key renewal', error instanceof Error ? error : new Error(String(error)));
|
|
410
|
-
throw new PasskeyControllerError(PasskeyControllerErrorMessage.VaultKeyDecryptionFailed, {
|
|
411
|
-
code: PasskeyControllerErrorCode.VaultKeyDecryptionFailed,
|
|
412
|
-
cause: error instanceof Error ? error : new Error(String(error)),
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
// check if vault key matches
|
|
416
|
-
const { oldVaultKey, newVaultKey } = params;
|
|
417
|
-
if (!areUint8ArraysEqual(stringToBytes(decryptedVaultKey), stringToBytes(oldVaultKey))) {
|
|
418
|
-
log('Passkey renewal rejected: decrypted vault key does not match oldVaultKey');
|
|
419
|
-
throw new PasskeyControllerError(PasskeyControllerErrorMessage.VaultKeyMismatch, { code: PasskeyControllerErrorCode.VaultKeyMismatch });
|
|
420
|
-
}
|
|
421
|
-
// encrypt new vault key
|
|
422
|
-
const { ciphertext, iv } = encryptWithKey(newVaultKey, encKey);
|
|
423
|
-
// persist passkey record (mutate current state only for vault key material)
|
|
424
|
-
this.update((state) => {
|
|
425
|
-
if (!state.passkeyRecord) {
|
|
426
|
-
throw new PasskeyControllerError(PasskeyControllerErrorMessage.NotEnrolled, {
|
|
427
|
-
code: PasskeyControllerErrorCode.NotEnrolled,
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
state.passkeyRecord.encryptedVaultKey = { ciphertext, iv };
|
|
431
|
-
});
|
|
361
|
+
return __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_withOperationLock).call(this, () => __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_renewVaultKeyProtection).call(this, params));
|
|
432
362
|
}
|
|
433
363
|
/**
|
|
434
|
-
*
|
|
435
|
-
*
|
|
364
|
+
* Changes the wallet password after passkey step-up authentication.
|
|
365
|
+
*
|
|
366
|
+
* When `renewVaultKeyProtection` is `true` (default), re-wraps the vault key under the
|
|
367
|
+
* passkey after rotation. When `false`, removes the passkey instead.
|
|
368
|
+
*
|
|
369
|
+
* @param params - Change-password inputs.
|
|
370
|
+
* @param params.newPassword - New wallet password.
|
|
371
|
+
* @param params.authenticationResponse - Result of `navigator.credentials.get()`.
|
|
372
|
+
* @param params.options - Optional flow controls.
|
|
373
|
+
* @param params.options.renewVaultKeyProtection - Re-wrap vault key after password change.
|
|
374
|
+
* @returns Resolves when the password change completes.
|
|
436
375
|
*/
|
|
437
|
-
|
|
438
|
-
this.
|
|
439
|
-
|
|
376
|
+
async changePasswordWithPasskeyVerification(params) {
|
|
377
|
+
return __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_withOperationLock).call(this, () => __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_changePasswordWithPasskeyVerification).call(this, params));
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Removes the enrolled passkey after verifying a passkey authentication assertion.
|
|
381
|
+
*
|
|
382
|
+
* @param authenticationResponse - Result of `navigator.credentials.get()`.
|
|
383
|
+
* @returns Resolves when the passkey is removed.
|
|
384
|
+
*/
|
|
385
|
+
async removePasskeyWithPasskeyVerification(authenticationResponse) {
|
|
386
|
+
return __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_withOperationLock).call(this, () => __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_removePasskeyWithPasskeyVerification).call(this, authenticationResponse));
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Removes the enrolled passkey after verifying the wallet password.
|
|
390
|
+
*
|
|
391
|
+
* @param password - Wallet password for step-up verification.
|
|
392
|
+
* @returns Resolves when the passkey is removed.
|
|
393
|
+
*/
|
|
394
|
+
async removePasskeyWithPasswordVerification(password) {
|
|
395
|
+
return __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_withOperationLock).call(this, () => __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_removePasskeyWithPasswordVerification).call(this, password));
|
|
440
396
|
}
|
|
441
397
|
/**
|
|
442
398
|
* Resets state and clears in-flight registration/authentication ceremonies.
|
|
399
|
+
*
|
|
400
|
+
* For user-facing passkey removal with step-up, use
|
|
401
|
+
* {@link removePasskeyWithPasskeyVerification} or
|
|
402
|
+
* {@link removePasskeyWithPasswordVerification}.
|
|
443
403
|
*/
|
|
444
404
|
clearState() {
|
|
445
|
-
this.
|
|
405
|
+
__classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_removePasskey).call(this);
|
|
446
406
|
}
|
|
447
407
|
/**
|
|
448
408
|
* Releases all in-flight ceremony state and tears down the messenger.
|
|
@@ -452,16 +412,188 @@ export class PasskeyController extends BaseController {
|
|
|
452
412
|
super.destroy();
|
|
453
413
|
}
|
|
454
414
|
}
|
|
455
|
-
_PasskeyController_ceremonyManager = new WeakMap(), _PasskeyController_expectedRPIDs = new WeakMap(), _PasskeyController_rpId = new WeakMap(), _PasskeyController_rpName = new WeakMap(), _PasskeyController_expectedOrigin = new WeakMap(), _PasskeyController_userName = new WeakMap(), _PasskeyController_userDisplayName = new WeakMap(), _PasskeyController_instances = new WeakSet(),
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
415
|
+
_PasskeyController_ceremonyManager = new WeakMap(), _PasskeyController_expectedRPIDs = new WeakMap(), _PasskeyController_rpId = new WeakMap(), _PasskeyController_rpName = new WeakMap(), _PasskeyController_expectedOrigin = new WeakMap(), _PasskeyController_userName = new WeakMap(), _PasskeyController_userDisplayName = new WeakMap(), _PasskeyController_getIsOnboardingCompleted = new WeakMap(), _PasskeyController_operationMutex = new WeakMap(), _PasskeyController_instances = new WeakSet(), _PasskeyController_protectVaultKeyWithPasskey = async function _PasskeyController_protectVaultKeyWithPasskey(params) {
|
|
416
|
+
if (this.isPasskeyEnrolled()) {
|
|
417
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.AlreadyEnrolled, { code: PasskeyControllerErrorCode.AlreadyEnrolled });
|
|
418
|
+
}
|
|
419
|
+
await __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_assertEnrollmentAllowed).call(this, params.password);
|
|
420
|
+
const vaultKey = await this.messenger.call('KeyringController:exportEncryptionKey');
|
|
421
|
+
const { registrationResponse, authenticationResponse } = params;
|
|
422
|
+
// get registration ceremony
|
|
423
|
+
const challenge = __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_getChallengeFromClientData).call(this, registrationResponse.response.clientDataJSON);
|
|
424
|
+
const registrationCeremony = __classPrivateFieldGet(this, _PasskeyController_ceremonyManager, "f").getRegistrationCeremony(challenge);
|
|
425
|
+
if (!registrationCeremony) {
|
|
426
|
+
log('No active passkey registration ceremony for challenge');
|
|
427
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.NoRegistrationCeremony, { code: PasskeyControllerErrorCode.NoRegistrationCeremony });
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
// verify registration response
|
|
431
|
+
const { verified, registrationInfo } = await verifyRegistrationResponse({
|
|
432
|
+
response: registrationResponse,
|
|
433
|
+
expectedChallenge: registrationCeremony.challenge,
|
|
434
|
+
expectedOrigin: __classPrivateFieldGet(this, _PasskeyController_expectedOrigin, "f"),
|
|
435
|
+
expectedRPIDs: __classPrivateFieldGet(this, _PasskeyController_expectedRPIDs, "f"),
|
|
436
|
+
requireUserVerification: true,
|
|
437
|
+
}).catch((error) => {
|
|
438
|
+
log('Error verifying passkey registration response', error);
|
|
439
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.RegistrationVerificationFailed, {
|
|
440
|
+
code: PasskeyControllerErrorCode.RegistrationVerificationFailed,
|
|
441
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
if (!verified || !registrationInfo) {
|
|
445
|
+
log('Passkey registration verification returned unverified or missing registration info');
|
|
446
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.RegistrationVerificationFailed, { code: PasskeyControllerErrorCode.RegistrationVerificationFailed });
|
|
447
|
+
}
|
|
448
|
+
// verify authentication response
|
|
449
|
+
const credential = {
|
|
450
|
+
id: registrationInfo.credentialId,
|
|
451
|
+
publicKey: bytesToBase64URL(registrationInfo.publicKey),
|
|
452
|
+
counter: registrationInfo.counter,
|
|
453
|
+
transports: registrationInfo.transports,
|
|
454
|
+
aaguid: registrationInfo.aaguid,
|
|
455
|
+
};
|
|
456
|
+
const { newCounter } = await __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_verifyAuthenticationResponse).call(this, authenticationResponse, credential);
|
|
457
|
+
// determine key derivation method
|
|
458
|
+
const prfFirst = authenticationResponse.clientExtensionResults?.prf?.results?.first;
|
|
459
|
+
const authHasPrfOutput = typeof prfFirst === 'string' && prfFirst.length > 0;
|
|
460
|
+
const keyDerivation = authHasPrfOutput && registrationCeremony.prfSalt
|
|
461
|
+
? { method: 'prf', prfSalt: registrationCeremony.prfSalt }
|
|
462
|
+
: { method: 'userHandle' };
|
|
463
|
+
if (keyDerivation.method === 'userHandle' &&
|
|
464
|
+
authenticationResponse.response.userHandle !==
|
|
465
|
+
registrationCeremony.userHandle) {
|
|
466
|
+
log('Post-registration assertion userHandle does not match registration ceremony');
|
|
467
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.AuthenticationVerificationFailed, { code: PasskeyControllerErrorCode.AuthenticationVerificationFailed });
|
|
468
|
+
}
|
|
469
|
+
// derive key and encrypt vault key
|
|
470
|
+
const encKey = deriveKeyFromAuthenticationResponse(authenticationResponse, { credential, keyDerivation });
|
|
471
|
+
const { ciphertext, iv } = encryptWithKey(vaultKey, encKey);
|
|
472
|
+
// persist passkey record
|
|
473
|
+
this.update((state) => {
|
|
474
|
+
state.passkeyRecord = {
|
|
475
|
+
credential: {
|
|
476
|
+
...credential,
|
|
477
|
+
counter: Math.max(newCounter, credential.counter),
|
|
478
|
+
},
|
|
479
|
+
encryptedVaultKey: { ciphertext, iv },
|
|
480
|
+
keyDerivation,
|
|
481
|
+
};
|
|
460
482
|
});
|
|
461
483
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
484
|
+
finally {
|
|
485
|
+
// delete registration ceremony
|
|
486
|
+
__classPrivateFieldGet(this, _PasskeyController_ceremonyManager, "f").deleteRegistrationCeremony(challenge);
|
|
487
|
+
}
|
|
488
|
+
}, _PasskeyController_retrieveVaultKeyWithPasskey = async function _PasskeyController_retrieveVaultKeyWithPasskey(authenticationResponse) {
|
|
489
|
+
const passkeyRecord = __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_requireEnrolled).call(this);
|
|
490
|
+
// verify authentication response and update counter
|
|
491
|
+
const { newCounter } = await __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_verifyAuthenticationResponse).call(this, authenticationResponse, passkeyRecord.credential);
|
|
492
|
+
this.update((state) => {
|
|
493
|
+
if (!state.passkeyRecord) {
|
|
494
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.NotEnrolled, { code: PasskeyControllerErrorCode.NotEnrolled });
|
|
495
|
+
}
|
|
496
|
+
state.passkeyRecord.credential.counter = Math.max(newCounter, state.passkeyRecord.credential.counter);
|
|
497
|
+
});
|
|
498
|
+
// derive key
|
|
499
|
+
const encKey = deriveKeyFromAuthenticationResponse(authenticationResponse, passkeyRecord);
|
|
500
|
+
// decrypt vault key
|
|
501
|
+
try {
|
|
502
|
+
const vaultKey = decryptWithKey(passkeyRecord.encryptedVaultKey.ciphertext, passkeyRecord.encryptedVaultKey.iv, encKey);
|
|
503
|
+
return vaultKey;
|
|
504
|
+
}
|
|
505
|
+
catch (cause) {
|
|
506
|
+
log('Error decrypting vault key with passkey', cause instanceof Error ? cause : new Error(String(cause)));
|
|
507
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.VaultKeyDecryptionFailed, {
|
|
508
|
+
code: PasskeyControllerErrorCode.VaultKeyDecryptionFailed,
|
|
509
|
+
cause: cause instanceof Error ? cause : new Error(String(cause)),
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}, _PasskeyController_verifyPasskeyAuthentication = async function _PasskeyController_verifyPasskeyAuthentication(authenticationResponse) {
|
|
513
|
+
try {
|
|
514
|
+
await __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_retrieveVaultKeyWithPasskey).call(this, authenticationResponse);
|
|
515
|
+
return true;
|
|
516
|
+
}
|
|
517
|
+
catch (error) {
|
|
518
|
+
if (error instanceof PasskeyControllerError && error.code !== undefined) {
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
throw error;
|
|
522
|
+
}
|
|
523
|
+
}, _PasskeyController_renewVaultKeyProtection = async function _PasskeyController_renewVaultKeyProtection(params) {
|
|
524
|
+
const { authenticationResponse } = params;
|
|
525
|
+
const passkeyRecord = __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_requireEnrolled).call(this);
|
|
526
|
+
// derive key
|
|
527
|
+
const encKey = deriveKeyFromAuthenticationResponse(authenticationResponse, passkeyRecord);
|
|
528
|
+
// decrypt vault key
|
|
529
|
+
let decryptedVaultKey;
|
|
530
|
+
try {
|
|
531
|
+
decryptedVaultKey = decryptWithKey(passkeyRecord.encryptedVaultKey.ciphertext, passkeyRecord.encryptedVaultKey.iv, encKey);
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
log('Error decrypting vault key during passkey vault key renewal', error instanceof Error ? error : new Error(String(error)));
|
|
535
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.VaultKeyDecryptionFailed, {
|
|
536
|
+
code: PasskeyControllerErrorCode.VaultKeyDecryptionFailed,
|
|
537
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
// check if vault key matches
|
|
541
|
+
const { oldVaultKey, newVaultKey } = params;
|
|
542
|
+
if (!areUint8ArraysEqual(stringToBytes(decryptedVaultKey), stringToBytes(oldVaultKey))) {
|
|
543
|
+
log('Passkey renewal rejected: decrypted vault key does not match oldVaultKey');
|
|
544
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.VaultKeyMismatch, { code: PasskeyControllerErrorCode.VaultKeyMismatch });
|
|
545
|
+
}
|
|
546
|
+
// encrypt new vault key
|
|
547
|
+
const { ciphertext, iv } = encryptWithKey(newVaultKey, encKey);
|
|
548
|
+
// persist passkey record (mutate current state only for vault key material)
|
|
549
|
+
this.update((state) => {
|
|
550
|
+
if (!state.passkeyRecord) {
|
|
551
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.NotEnrolled, {
|
|
552
|
+
code: PasskeyControllerErrorCode.NotEnrolled,
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
state.passkeyRecord.encryptedVaultKey = { ciphertext, iv };
|
|
556
|
+
});
|
|
557
|
+
}, _PasskeyController_changePasswordWithPasskeyVerification = async function _PasskeyController_changePasswordWithPasskeyVerification(params) {
|
|
558
|
+
__classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_requireEnrolled).call(this);
|
|
559
|
+
const verified = await __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_verifyPasskeyAuthentication).call(this, params.authenticationResponse);
|
|
560
|
+
if (!verified) {
|
|
561
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.AuthenticationVerificationFailed, { code: PasskeyControllerErrorCode.AuthenticationVerificationFailed });
|
|
562
|
+
}
|
|
563
|
+
const renewVaultKeyProtection = params.options?.renewVaultKeyProtection ?? true;
|
|
564
|
+
if (!renewVaultKeyProtection) {
|
|
565
|
+
await this.messenger.call('KeyringController:changePassword', params.newPassword);
|
|
566
|
+
__classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_removePasskey).call(this);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
const vaultKeyBefore = await this.messenger.call('KeyringController:exportEncryptionKey');
|
|
570
|
+
await this.messenger.call('KeyringController:changePassword', params.newPassword);
|
|
571
|
+
try {
|
|
572
|
+
const vaultKeyAfter = await this.messenger.call('KeyringController:exportEncryptionKey');
|
|
573
|
+
await __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_renewVaultKeyProtection).call(this, {
|
|
574
|
+
authenticationResponse: params.authenticationResponse,
|
|
575
|
+
oldVaultKey: vaultKeyBefore,
|
|
576
|
+
newVaultKey: vaultKeyAfter,
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
catch (error) {
|
|
580
|
+
__classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_removePasskey).call(this);
|
|
581
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.VaultKeyRenewalFailed, {
|
|
582
|
+
code: PasskeyControllerErrorCode.VaultKeyRenewalFailed,
|
|
583
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
}, _PasskeyController_removePasskeyWithPasskeyVerification = async function _PasskeyController_removePasskeyWithPasskeyVerification(authenticationResponse) {
|
|
587
|
+
__classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_requireEnrolled).call(this);
|
|
588
|
+
const verified = await __classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_verifyPasskeyAuthentication).call(this, authenticationResponse);
|
|
589
|
+
if (!verified) {
|
|
590
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.AuthenticationVerificationFailed, { code: PasskeyControllerErrorCode.AuthenticationVerificationFailed });
|
|
591
|
+
}
|
|
592
|
+
__classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_removePasskey).call(this);
|
|
593
|
+
}, _PasskeyController_removePasskeyWithPasswordVerification = async function _PasskeyController_removePasskeyWithPasswordVerification(password) {
|
|
594
|
+
__classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_requireEnrolled).call(this);
|
|
595
|
+
await this.messenger.call('KeyringController:verifyPassword', password);
|
|
596
|
+
__classPrivateFieldGet(this, _PasskeyController_instances, "m", _PasskeyController_removePasskey).call(this);
|
|
465
597
|
}, _PasskeyController_verifyAuthenticationResponse =
|
|
466
598
|
/**
|
|
467
599
|
* Validates a WebAuthn authentication response against stored credential data.
|
|
@@ -512,5 +644,35 @@ async function _PasskeyController_verifyAuthenticationResponse(authenticationRes
|
|
|
512
644
|
// delete authentication ceremony
|
|
513
645
|
__classPrivateFieldGet(this, _PasskeyController_ceremonyManager, "f").deleteAuthenticationCeremony(challenge);
|
|
514
646
|
}
|
|
647
|
+
}, _PasskeyController_withOperationLock =
|
|
648
|
+
/**
|
|
649
|
+
* Serializes orchestrated passkey operations that mutate state or call KeyringController.
|
|
650
|
+
*
|
|
651
|
+
* @param callback - Operation to run while the mutex is held.
|
|
652
|
+
* @returns The result of the callback.
|
|
653
|
+
*/
|
|
654
|
+
async function _PasskeyController_withOperationLock(callback) {
|
|
655
|
+
return __classPrivateFieldGet(this, _PasskeyController_operationMutex, "f").runExclusive(callback);
|
|
656
|
+
}, _PasskeyController_assertEnrollmentAllowed = async function _PasskeyController_assertEnrollmentAllowed(password) {
|
|
657
|
+
if (!__classPrivateFieldGet(this, _PasskeyController_getIsOnboardingCompleted, "f").call(this)) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (!password) {
|
|
661
|
+
throw new Error(PasskeyControllerErrorMessage.EnrollmentPasswordRequired);
|
|
662
|
+
}
|
|
663
|
+
await this.messenger.call('KeyringController:verifyPassword', password);
|
|
664
|
+
}, _PasskeyController_requireEnrolled = function _PasskeyController_requireEnrolled() {
|
|
665
|
+
const record = this.state.passkeyRecord;
|
|
666
|
+
if (!record) {
|
|
667
|
+
throw new PasskeyControllerError(PasskeyControllerErrorMessage.NotEnrolled, {
|
|
668
|
+
code: PasskeyControllerErrorCode.NotEnrolled,
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
return record;
|
|
672
|
+
}, _PasskeyController_getChallengeFromClientData = function _PasskeyController_getChallengeFromClientData(clientDataJSON) {
|
|
673
|
+
return decodeClientDataJSON(clientDataJSON).challenge;
|
|
674
|
+
}, _PasskeyController_removePasskey = function _PasskeyController_removePasskey() {
|
|
675
|
+
this.update(() => getDefaultPasskeyControllerState());
|
|
676
|
+
__classPrivateFieldGet(this, _PasskeyController_ceremonyManager, "f").clear();
|
|
515
677
|
};
|
|
516
678
|
//# sourceMappingURL=PasskeyController.mjs.map
|