@glyphteck/veyl 0.66.2 → 0.67.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/dist/account.js CHANGED
@@ -7281,7 +7281,7 @@ function closeSessionResources({ wallet, chatPK, chatPrivateKey, chatSigningSecr
7281
7281
  close(vaultAccess);
7282
7282
  close(vaultSigner);
7283
7283
  close(localCache);
7284
- pending.push(lockWallet(wallet));
7284
+ lockWallet(wallet);
7285
7285
  lockChat(chatPrivateKey, chatPK);
7286
7286
  cleanBytes(chatSigningSecret, notificationPrivateKey, notificationPublicKey);
7287
7287
  return Promise.allSettled(pending).then(() => {
@@ -7857,6 +7857,8 @@ function settingsState(settings = defaultSettings) {
7857
7857
  var defaultUser = {
7858
7858
  uid: null,
7859
7859
  authReady: false,
7860
+ authSessionReady: false,
7861
+ authSessionActive: false,
7860
7862
  authCredential: null,
7861
7863
  profileReady: false,
7862
7864
  username: null,
@@ -7878,12 +7880,16 @@ var defaultUser = {
7878
7880
  settingsReady: false,
7879
7881
  settings: settingsState()
7880
7882
  };
7883
+ function isAccountAuthenticationError(error) {
7884
+ const code = typeof error?.code === "string" ? error.code.toLowerCase() : "";
7885
+ return code === "permission-denied" || code.endsWith("/permission-denied") || code === "unauthenticated" || code.endsWith("/unauthenticated");
7886
+ }
7881
7887
  function createUser({ cloud, network, avatarCache = null, diag = null, onSessionRevoked = null }) {
7882
7888
  if (!cloud) {
7883
7889
  throw new Error("createUser requires { cloud }");
7884
7890
  }
7885
- if (avatarCache && [avatarCache.read, avatarCache.write, avatarCache.remove, avatarCache.removeAll].some((method) => typeof method !== "function")) {
7886
- throw new Error("avatarCache requires read, write, remove, and removeAll");
7891
+ if (avatarCache && [avatarCache.read, avatarCache.write, avatarCache.remove].some((method) => typeof method !== "function")) {
7892
+ throw new Error("avatarCache requires read, write, and remove");
7887
7893
  }
7888
7894
  let activeNetwork = network;
7889
7895
  let state = defaultUser;
@@ -8021,9 +8027,6 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
8021
8027
  return;
8022
8028
  runCacheTask("user.avatar.cache.remove", () => avatarCache.remove(uid));
8023
8029
  }
8024
- function clearCachedAvatars() {
8025
- runCacheTask("user.avatar.cache.clear", () => avatarCache.removeAll());
8026
- }
8027
8030
  async function fetchAvatar(uid, { version = null, force = false, clear = false, persist = true } = {}) {
8028
8031
  if (!uid)
8029
8032
  return;
@@ -8106,6 +8109,54 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
8106
8109
  function isCurrentUser(uid, session) {
8107
8110
  return started && authSession === session && userUid === uid;
8108
8111
  }
8112
+ function revokeAuthentication(authUser, {
8113
+ code = "",
8114
+ local = false,
8115
+ reason = "session-inactive"
8116
+ } = {}) {
8117
+ if (revokingSession)
8118
+ return;
8119
+ revokingSession = true;
8120
+ let callback;
8121
+ try {
8122
+ callback = onSessionRevoked?.({
8123
+ uid: authUser.uid,
8124
+ code,
8125
+ reason
8126
+ });
8127
+ } catch (error) {
8128
+ callback = Promise.reject(error);
8129
+ }
8130
+ markDiag(diag, "user.auth.revoked", {
8131
+ code,
8132
+ local,
8133
+ reason
8134
+ });
8135
+ setState((user) => ({
8136
+ ...user,
8137
+ authSessionReady: true,
8138
+ authSessionActive: false,
8139
+ authCredential: null
8140
+ }));
8141
+ let operation = Promise.resolve(callback).catch((error) => markError(diag, "user.session.revoke.local", Date.now(), error));
8142
+ if (!local) {
8143
+ operation = operation.then(() => cloud.auth.logout()).catch((error) => markError(diag, "user.session.revoke.auth", Date.now(), error));
8144
+ }
8145
+ operation.finally(() => {
8146
+ if (userUid === authUser.uid) {
8147
+ revokingSession = false;
8148
+ }
8149
+ });
8150
+ }
8151
+ function revokeAuthenticationError(authUser, error, reason) {
8152
+ if (!isAccountAuthenticationError(error))
8153
+ return false;
8154
+ revokeAuthentication(authUser, {
8155
+ code: error?.code || "",
8156
+ reason
8157
+ });
8158
+ return true;
8159
+ }
8109
8160
  function watchUser(authUser) {
8110
8161
  if (!started)
8111
8162
  return;
@@ -8115,7 +8166,6 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
8115
8166
  closeUserWatches();
8116
8167
  markDiag(diag, "user.auth.state", { signedIn: !!authUser });
8117
8168
  if (!authUser) {
8118
- const signedOutUid = userUid;
8119
8169
  revokingSession = false;
8120
8170
  userUid = null;
8121
8171
  clearSettingsKey(settingsKey);
@@ -8124,17 +8174,11 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
8124
8174
  agreementStored = null;
8125
8175
  agreementOverride = null;
8126
8176
  agreementAcceptance = null;
8127
- if (signedOutUid) {
8128
- clearCachedAvatars();
8129
- }
8130
8177
  avatarFetch = { uid: null, key: null, promise: null };
8131
8178
  setState({ ...defaultUser, authReady: true });
8132
8179
  return;
8133
8180
  }
8134
8181
  const authUidChanged = userUid !== authUser.uid;
8135
- if (userUid && authUidChanged) {
8136
- clearCachedAvatars();
8137
- }
8138
8182
  userUid = authUser.uid;
8139
8183
  if (authUidChanged) {
8140
8184
  clearSettingsKey(settingsKey);
@@ -8164,32 +8208,28 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
8164
8208
  userWatches.push(cloud.auth.session.watch(authUser.uid, current((authSessionState) => {
8165
8209
  if (authSessionState?.active) {
8166
8210
  const authCredential = authSessionState.authCredential;
8167
- setState((user) => user.authCredential?.kind === authCredential?.kind && user.authCredential?.id === authCredential?.id ? user : { ...user, authCredential });
8168
- return;
8169
- }
8170
- if (revokingSession)
8171
- return;
8172
- revokingSession = true;
8173
- if (cloud.auth.session.isLocalTermination?.(authUser.uid, authSessionState?.generation)) {
8174
- Promise.resolve(onSessionRevoked?.({ uid: authUser.uid })).catch((error) => markError(diag, "user.session.revoke.local", Date.now(), error)).finally(() => {
8175
- if (userUid === authUser.uid) {
8176
- revokingSession = false;
8177
- }
8211
+ setState((user) => user.authSessionReady && user.authSessionActive && user.authCredential?.kind === authCredential?.kind && user.authCredential?.id === authCredential?.id ? user : {
8212
+ ...user,
8213
+ authSessionReady: true,
8214
+ authSessionActive: true,
8215
+ authCredential
8178
8216
  });
8179
8217
  return;
8180
8218
  }
8181
- Promise.resolve(onSessionRevoked?.({ uid: authUser.uid })).catch((error) => markError(diag, "user.session.revoke.local", Date.now(), error)).then(() => cloud.auth.logout()).catch((error) => markError(diag, "user.session.revoke.auth", Date.now(), error)).finally(() => {
8182
- if (userUid === authUser.uid) {
8183
- revokingSession = false;
8184
- }
8219
+ revokeAuthentication(authUser, {
8220
+ local: cloud.auth.session.isLocalTermination?.(authUser.uid, authSessionState?.generation) === true,
8221
+ reason: "session-inactive"
8185
8222
  });
8186
8223
  }), current((error) => {
8187
8224
  markError(diag, "user.session.listen", authStartedAt, error);
8225
+ revokeAuthenticationError(authUser, error, "session-listener");
8188
8226
  })));
8189
8227
  userWatches.push(cloud.user.admin.watch(authUser.uid, current((allowed) => {
8190
8228
  setState((user) => ({ ...user, isAdmin: allowed, adminReady: true }));
8191
8229
  }), current((error) => {
8192
8230
  markError(diag, "user.admin.listen", authStartedAt, error);
8231
+ if (revokeAuthenticationError(authUser, error, "admin-listener"))
8232
+ return;
8193
8233
  setState((user) => ({ ...user, isAdmin: false, adminReady: true }));
8194
8234
  })));
8195
8235
  userWatches.push(cloud.user.private.watch(authUser.uid, current((privateData, info = {}) => {
@@ -8213,6 +8253,8 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
8213
8253
  }));
8214
8254
  }), current((error) => {
8215
8255
  markError(diag, "user.settings.snapshot", authStartedAt, error);
8256
+ if (revokeAuthenticationError(authUser, error, "settings-listener"))
8257
+ return;
8216
8258
  settingsStored = null;
8217
8259
  agreementStored = null;
8218
8260
  setState((user) => ({
@@ -8225,6 +8267,8 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
8225
8267
  setState((user) => ({ ...user, banned: banned ?? null }));
8226
8268
  }), current((error) => {
8227
8269
  markError(diag, "user.moderation.listen", authStartedAt, error);
8270
+ if (revokeAuthenticationError(authUser, error, "moderation-listener"))
8271
+ return;
8228
8272
  setState((user) => ({ ...user, banned: null }));
8229
8273
  })));
8230
8274
  userWatches.push(cloud.user.blocked.watch(authUser.uid, current((blocked, info = {}) => {
@@ -8237,6 +8281,8 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
8237
8281
  });
8238
8282
  }), current((error) => {
8239
8283
  markError(diag, "user.blocked.listen", authStartedAt, error);
8284
+ if (revokeAuthenticationError(authUser, error, "blocked-listener"))
8285
+ return;
8240
8286
  setState((user) => ({ ...user, blockedReady: false }));
8241
8287
  })));
8242
8288
  userWatches.push(cloud.user.profile.watch(authUser.uid, current((profileData, info = {}) => {
@@ -8283,6 +8329,8 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
8283
8329
  }
8284
8330
  }), current((error) => {
8285
8331
  markError(diag, "user.profile.snapshot", authStartedAt, error);
8332
+ if (revokeAuthenticationError(authUser, error, "profile-listener"))
8333
+ return;
8286
8334
  avatarFetch = { uid: null, key: null, promise: null };
8287
8335
  setState((user) => ({
8288
8336
  ...user,
@@ -41653,20 +41701,36 @@ function openAccount(options = {}) {
41653
41701
  let closePromise = null;
41654
41702
  let state;
41655
41703
  let chatSession = null;
41704
+ let revokedUid = null;
41705
+ let revocationPromise = null;
41656
41706
  let nextAgreement = agreementContract(options.agreement || CURRENT_AGREEMENT);
41657
41707
  const listeners = new Set;
41658
41708
  let activeWriteTail = Promise.resolve();
41659
41709
  const sessionCloseWork = new Set;
41660
41710
  const operationBarrier = createAccountOperationCloseBarrier();
41711
+ function revokeSession(details = {}) {
41712
+ const { uid = null } = details;
41713
+ const accountUid = uid || state?.uid || null;
41714
+ if (accountUid && revokedUid === accountUid) {
41715
+ return revocationPromise || Promise.resolve();
41716
+ }
41717
+ revokedUid = accountUid;
41718
+ let callback;
41719
+ try {
41720
+ callback = options.onSessionRevoked?.({ ...details, uid: accountUid });
41721
+ } catch (error) {
41722
+ callback = Promise.reject(error);
41723
+ }
41724
+ lock();
41725
+ revocationPromise = Promise.resolve(callback);
41726
+ return revocationPromise;
41727
+ }
41661
41728
  const user = options.user || createUser({
41662
41729
  cloud,
41663
41730
  network: options.network || defaultNetwork,
41664
41731
  avatarCache,
41665
41732
  diag,
41666
- onSessionRevoked: ({ uid } = {}) => {
41667
- lock();
41668
- options.onSessionRevoked?.({ uid: uid || state?.uid || null });
41669
- }
41733
+ onSessionRevoked: revokeSession
41670
41734
  });
41671
41735
  state = emptyState(user.getSnapshot(), options.network || defaultNetwork);
41672
41736
  const chat = createChatSession({
@@ -41915,6 +41979,24 @@ function openAccount(options = {}) {
41915
41979
  code: error?.code || "",
41916
41980
  message: error?.message || String(error)
41917
41981
  });
41982
+ if (isAccountAuthenticationError(error)) {
41983
+ revokeSession({
41984
+ uid,
41985
+ code: error?.code || "",
41986
+ reason: "vault-listener"
41987
+ }).catch((revocationError) => {
41988
+ diag?.("account.session.revoke.error", {
41989
+ code: revocationError?.code || "",
41990
+ message: revocationError?.message || String(revocationError)
41991
+ });
41992
+ }).then(() => cloud.auth.logout()).catch((logoutError) => {
41993
+ diag?.("account.session.revoke.auth.error", {
41994
+ code: logoutError?.code || "",
41995
+ message: logoutError?.message || String(logoutError)
41996
+ });
41997
+ });
41998
+ return;
41999
+ }
41918
42000
  lock();
41919
42001
  emit2({ vault: null, vaultReady: true, vaultError: error });
41920
42002
  });
@@ -249,6 +249,57 @@ function cleanBytes(...values) {
249
249
  }
250
250
  }
251
251
 
252
+ // ../../core/passkey.js
253
+ var PASSKEY_ENVIRONMENT_MISMATCH = "passkey-environment-mismatch";
254
+ var PASSKEY_PROVIDER_ALREADY_LINKED = "passkey-provider-already-linked";
255
+ var PASSKEY_REGISTER_INVALID = "passkey-register-invalid";
256
+ var PASSKEY_RP_MISMATCH = "passkey-rp-mismatch";
257
+ var PASSKEY_UNLINKED = "passkey-unlinked";
258
+ var PASSKEY_CANCELLED_CODES = new Set(["ERR_USER_CANCELLED"]);
259
+ var PASSKEY_CANCELLED_NAMES = new Set(["UserCancelledException"]);
260
+ var PASSKEY_CONTROLLED_ERRORS = new Set([
261
+ PASSKEY_ENVIRONMENT_MISMATCH,
262
+ PASSKEY_PROVIDER_ALREADY_LINKED,
263
+ PASSKEY_REGISTER_INVALID,
264
+ PASSKEY_RP_MISMATCH,
265
+ PASSKEY_UNLINKED
266
+ ]);
267
+ function canDeletePasskey(row, passkeys, currentPasskeyId) {
268
+ if (row?.type === "pending")
269
+ return true;
270
+ return row?.type === "passkey" && Array.isArray(passkeys) && passkeys.length > 1 && row.id !== cleanText(currentPasskeyId);
271
+ }
272
+ function passkeyError(code, message, cause) {
273
+ const error = new Error(message);
274
+ error.code = code;
275
+ error.cause = cause;
276
+ return error;
277
+ }
278
+ function normalizePasskeyLoginError(error, options = {}) {
279
+ if (options.localhostSilo === true && error?.code === "functions/failed-precondition" && cleanText(error?.message).includes("Localhost uses a separate passkey silo")) {
280
+ return passkeyError(PASSKEY_ENVIRONMENT_MISMATCH, "This passkey belongs to glyphteck.com, not localhost.", error);
281
+ }
282
+ if (error?.code === "functions/not-found") {
283
+ return passkeyError(PASSKEY_UNLINKED, "This passkey is no longer linked to an account.", error);
284
+ }
285
+ if (error?.code === "functions/failed-precondition") {
286
+ return passkeyError(PASSKEY_RP_MISMATCH, "This passkey belongs to a different Glyphteck passkey setup.", error);
287
+ }
288
+ return error;
289
+ }
290
+ function normalizePasskeyRegisterError(error, options = {}) {
291
+ if (error?.name === "CredentialAlreadyExistsException" || error?.name === "InvalidStateError" || error?.code === "functions/already-exists") {
292
+ return passkeyError(PASSKEY_PROVIDER_ALREADY_LINKED, "you already have a passkey for this account", error);
293
+ }
294
+ if (error?.code === "functions/failed-precondition") {
295
+ return passkeyError(PASSKEY_RP_MISMATCH, "This passkey belongs to a different Glyphteck passkey setup.", error);
296
+ }
297
+ if (options.invalidArgument === true && error?.code === "functions/invalid-argument" && error?.message) {
298
+ return passkeyError(PASSKEY_REGISTER_INVALID, error.message, error);
299
+ }
300
+ return error;
301
+ }
302
+
252
303
  // ../../core/account/profiles.js
253
304
  var ACCOUNT_PROFILES_VERSION = 1;
254
305
  var PROFILE_ID_RE = /^[0-9a-f]{32}$/;
@@ -606,7 +657,7 @@ function requirePorts(options) {
606
657
  onError: typeof options.onError === "function" ? options.onError : null
607
658
  };
608
659
  }
609
- async function resolveAuthenticatedProfile(owner, generation, reason) {
660
+ async function resolveAuthenticatedProfile(owner, generation, reason, details = {}) {
610
661
  ensureGeneration(owner, generation);
611
662
  const registry = owner.registry;
612
663
  const context = Object.freeze({
@@ -614,6 +665,7 @@ async function resolveAuthenticatedProfile(owner, generation, reason) {
614
665
  generation,
615
666
  reason,
616
667
  requestedProfileId: owner.requestedProfileId,
668
+ rejectedProfileId: details.rejectedProfileId || null,
617
669
  isCurrent: () => !owner.closing && !owner.closed && owner.generation === generation
618
670
  });
619
671
  const profileId = await owner.ports.resolveAuthenticatedProfile(registry, context);
@@ -981,7 +1033,9 @@ async function applyAuthenticatedProfile(owner, profile, generation, reason, opt
981
1033
  }
982
1034
  async function resumeAuthenticatedProfile(owner, generation, reason, options = {}) {
983
1035
  publish(owner, reason, null);
984
- const profile = await resolveAuthenticatedProfile(owner, generation, reason);
1036
+ const profile = await resolveAuthenticatedProfile(owner, generation, reason, {
1037
+ rejectedProfileId: options.rejectedProfileId
1038
+ });
985
1039
  return applyAuthenticatedProfile(owner, profile, generation, reason, options);
986
1040
  }
987
1041
  function transitionFailure(owner, error, generation, profileId) {
@@ -1009,12 +1063,15 @@ async function initialize(owner) {
1009
1063
  throw error;
1010
1064
  }
1011
1065
  }
1012
- function requestAuthenticatedProfileResume(owner) {
1066
+ function requestAuthenticatedProfileResume(owner, options = {}) {
1013
1067
  ensureReady(owner);
1068
+ const rejectedProfileId = options.rejectedProfileId == null ? null : readAccountProfileId(options.rejectedProfileId);
1014
1069
  const generation = requestSelection(owner, null, false);
1015
1070
  return enqueue(owner, async () => {
1016
1071
  try {
1017
- return await resumeAuthenticatedProfile(owner, generation, "restoring");
1072
+ return await resumeAuthenticatedProfile(owner, generation, "restoring", {
1073
+ rejectedProfileId
1074
+ });
1018
1075
  } catch (error) {
1019
1076
  if (!isStaleAccountProfileTransition(error))
1020
1077
  publish(owner, "error", error);
@@ -1159,9 +1216,11 @@ async function updateStoredProfile(owner, profileId, fields) {
1159
1216
  return profile;
1160
1217
  }
1161
1218
  async function signOutStoredProfile(owner, profile) {
1219
+ const hadActive = !!owner.active;
1220
+ const wasActive = owner.active?.profile.profileId === profile.profileId;
1162
1221
  try {
1163
1222
  publish(owner, "signing-out", null);
1164
- if (!owner.active || owner.active.profile.profileId === profile.profileId) {
1223
+ if (!hadActive || wasActive) {
1165
1224
  const lease = owner.active?.lease || {
1166
1225
  generation: owner.generation,
1167
1226
  profileId: profile.profileId,
@@ -1184,7 +1243,11 @@ async function signOutStoredProfile(owner, profile) {
1184
1243
  await mutateRegistry(owner, (current) => current.selectedProfileId === profile.profileId ? deselectAccountProfile(current, profile.profileId) : current);
1185
1244
  if (owner.requestedProfileId === profile.profileId)
1186
1245
  owner.requestedProfileId = null;
1187
- await resumeAuthenticatedProfile(owner, owner.generation, "restoring", { clearEmpty: false });
1246
+ if (!hadActive || wasActive) {
1247
+ await resumeAuthenticatedProfile(owner, owner.generation, "restoring", { clearEmpty: false });
1248
+ } else {
1249
+ publish(owner, "idle", null);
1250
+ }
1188
1251
  if (signOutFailure)
1189
1252
  throw signOutFailure;
1190
1253
  return profile;
@@ -1193,18 +1256,19 @@ async function signOutStoredProfile(owner, profile) {
1193
1256
  throw error;
1194
1257
  }
1195
1258
  }
1196
- function requestProfileSignOut(owner) {
1259
+ function requestProfileSignOut(owner, requestedProfileId = null) {
1197
1260
  ensureReady(owner);
1198
- const profileId = owner.active?.profile.profileId || owner.registry.selectedProfileId;
1261
+ const profileId = requestedProfileId == null ? owner.active?.profile.profileId || owner.registry.selectedProfileId : readAccountProfileId(requestedProfileId);
1199
1262
  const profile = profileId ? accountProfileById(owner.registry, profileId) : null;
1200
1263
  if (!profile)
1201
- return Promise.reject(new Error("active account profile required"));
1202
- owner.generation += 1;
1203
- owner.requestedProfileId = null;
1204
- if (owner.openingLease)
1205
- owner.openingLease.valid = false;
1206
- if (owner.active)
1264
+ return Promise.reject(new Error("account profile required"));
1265
+ if (owner.active?.profile.profileId === profileId) {
1266
+ owner.generation += 1;
1267
+ owner.requestedProfileId = null;
1268
+ if (owner.openingLease)
1269
+ owner.openingLease.valid = false;
1207
1270
  owner.active.lease.valid = false;
1271
+ }
1208
1272
  publish(owner);
1209
1273
  return enqueue(owner, () => signOutStoredProfile(owner, profile));
1210
1274
  }
@@ -1346,9 +1410,9 @@ function createAccountProfiles(options = {}) {
1346
1410
  const generation = requestSelection(owner, profileId, false);
1347
1411
  return enqueue(owner, () => switchExisting(owner, profileId, generation));
1348
1412
  },
1349
- resumeAuthenticated: () => requestAuthenticatedProfileResume(owner),
1413
+ resumeAuthenticated: (options2) => requestAuthenticatedProfileResume(owner, options2),
1350
1414
  refresh: () => requestProfileRefresh(owner),
1351
- signOut: () => requestProfileSignOut(owner),
1415
+ signOut: (profileId) => requestProfileSignOut(owner, profileId),
1352
1416
  remove: (profileId) => requestProfileRemoval(owner, profileId),
1353
1417
  getActiveRuntime: () => owner.active?.runtime || null,
1354
1418
  close: () => closeProfiles(owner)
package/dist/auth.js CHANGED
@@ -82,8 +82,8 @@ function normalizePasskeyLoginError(error, options = {}) {
82
82
  return error;
83
83
  }
84
84
  function normalizePasskeyRegisterError(error, options = {}) {
85
- if (error?.name === "InvalidStateError" || error?.code === "functions/already-exists") {
86
- return passkeyError(PASSKEY_PROVIDER_ALREADY_LINKED, "this provider already has a passkey for this account. choose another provider, device, or security key", error);
85
+ if (error?.name === "CredentialAlreadyExistsException" || error?.name === "InvalidStateError" || error?.code === "functions/already-exists") {
86
+ return passkeyError(PASSKEY_PROVIDER_ALREADY_LINKED, "you already have a passkey for this account", error);
87
87
  }
88
88
  if (error?.code === "functions/failed-precondition") {
89
89
  return passkeyError(PASSKEY_RP_MISMATCH, "This passkey belongs to a different Glyphteck passkey setup.", error);