@ibgib/web-gib 0.0.62 → 0.0.64
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 +4 -0
- package/dist/AUTO-GENERATED-version.d.mts +1 -1
- package/dist/AUTO-GENERATED-version.mjs +1 -1
- package/dist/helpers.web.d.mts +17 -2
- package/dist/helpers.web.d.mts.map +1 -1
- package/dist/helpers.web.mjs +67 -2
- package/dist/helpers.web.mjs.map +1 -1
- package/dist/helpers.web.respec.mjs +37 -10
- package/dist/helpers.web.respec.mjs.map +1 -1
- package/dist/identity/passkey-helper.d.mts +7 -2
- package/dist/identity/passkey-helper.d.mts.map +1 -1
- package/dist/identity/passkey-helper.mjs +33 -12
- package/dist/identity/passkey-helper.mjs.map +1 -1
- package/dist/index.d.mts +3 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +4 -0
- package/dist/index.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-creator/keystone-creator.css +26 -0
- package/dist/ui/component/identity/keystone-creator/keystone-creator.d.mts.map +1 -1
- package/dist/ui/component/identity/keystone-creator/keystone-creator.html +18 -1
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs +10 -2
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts +12 -0
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.html +8 -0
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs +419 -305
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs.map +1 -1
- package/dist/ui/ui-helpers.d.mts.map +1 -1
- package/dist/ui/ui-helpers.mjs +7 -2
- package/dist/ui/ui-helpers.mjs.map +1 -1
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-constants.d.mts +7 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-constants.d.mts.map +1 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-constants.mjs +7 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-constants.mjs.map +1 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-types.d.mts +24 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-types.d.mts.map +1 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-types.mjs +23 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-types.mjs.map +1 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-v1.d.mts +85 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-v1.d.mts.map +1 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-v1.mjs +343 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-v1.mjs.map +1 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-v1.respec.d.mts +8 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-v1.respec.d.mts.map +1 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-v1.respec.mjs +249 -0
- package/dist/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-v1.respec.mjs.map +1 -0
- package/package.json +2 -2
- package/src/AUTO-GENERATED-version.mts +1 -1
- package/src/helpers.mts +1 -1
- package/src/helpers.web.mts +95 -5
- package/src/helpers.web.respec.mts +41 -10
- package/src/identity/passkey-helper.mts +43 -10
- package/src/index.mts +5 -0
- package/src/ui/component/identity/keystone-creator/keystone-creator.css +26 -0
- package/src/ui/component/identity/keystone-creator/keystone-creator.html +18 -1
- package/src/ui/component/identity/keystone-creator/keystone-creator.mts +12 -2
- package/src/ui/component/identity/keystone-details/keystone-details.html +8 -0
- package/src/ui/component/identity/keystone-details/keystone-details.mts +474 -339
- package/src/ui/ui-helpers.mts +6 -2
- package/src/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-constants.mts +8 -0
- package/src/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-types.mts +53 -0
- package/src/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-v1.mts +424 -0
- package/src/witness/space/web-in-memory-workspace-projection-space/web-in-memory-workspace-projection-space-v1.respec.mts +304 -0
- package/tools/auto-generated-agent-skills/skills/ibgib-create-app/SKILL.md +65 -39
- package/tools/auto-generated-agent-skills/skills/ibgib-create-client-app/SKILL.md +51 -0
- package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-core → ibgib-create-client-app-core}/SKILL.md +26 -22
- package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-witness → ibgib-create-client-app-witness}/SKILL.md +11 -7
- package/tools/auto-generated-agent-skills/skills/ibgib-create-client-identity/SKILL.md +78 -0
- package/tools/auto-generated-agent-skills/skills/ibgib-create-client-shell/SKILL.md +42 -0
- package/tools/auto-generated-agent-skills/skills/ibgib-create-server-app/SKILL.md +50 -0
- package/tools/auto-generated-agent-skills/skills/ibgib-create-server-app/templates/Dockerfile.template +14 -0
- package/tools/auto-generated-agent-skills/skills/ibgib-create-server-app/templates/docker-compose.yml.template +33 -0
- package/tools/auto-generated-agent-skills/skills/ibgib-create-server-app/templates/server.mts.template +97 -0
- package/tools/auto-generated-agent-skills/skills/ibgib-create-server-app/templates/tsconfig.server.json.template +29 -0
- package/tools/auto-generated-agent-skills/skills/ibgib-implementation-plan/SKILL.md +4 -1
- package/src/identity/custodian-delegate-helper.mts.bak +0 -7
- package/src/identity/sso/sso-config-helper.mts.bak +0 -52
- package/src/identity/sso/sso-custodian-service.mts.bak +0 -566
- package/src/identity/sso/sso-custodian-service.respec.mts.bak +0 -298
- package/tools/auto-generated-agent-skills/skills/ibgib-create-shell/SKILL.md +0 -40
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-core → ibgib-create-client-app-core}/generate-id.js +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-core → ibgib-create-client-app-core}/generate-version-file.js +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-core → ibgib-create-client-app-core}/templates/bootstrap.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-core → ibgib-create-client-app-core}/templates/constants.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-core → ibgib-create-client-app-core}/templates/declarations.d.ts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-core → ibgib-create-client-app-core}/templates/helpers.web.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-core → ibgib-create-client-app-core}/templates/index.html.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-core → ibgib-create-client-app-core}/templates/index.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-core → ibgib-create-client-app-core}/templates/script.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-core → ibgib-create-client-app-core}/templates/types.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-witness → ibgib-create-client-app-witness}/templates/api/function-infos.web.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-witness → ibgib-create-client-app-witness}/templates/witness/app/app-v1.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-witness → ibgib-create-client-app-witness}/templates/witness/app/constants.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-witness → ibgib-create-client-app-witness}/templates/witness/app/helper.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-app-witness → ibgib-create-client-app-witness}/templates/witness/app/types.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-shell → ibgib-create-client-shell}/templates/ui/shell/shell-constants.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-shell → ibgib-create-client-shell}/templates/ui/shell/shell-service.mts.template +0 -0
- /package/tools/auto-generated-agent-skills/skills/{ibgib-create-shell → ibgib-create-client-shell}/templates/ui/shell/shell-types.mts.template +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import thisCss from "./keystone-details.css";
|
|
2
2
|
import thisHtml from "./keystone-details.html";
|
|
3
3
|
import commonComponentStylesCss from "../../common-component-styles.css";
|
|
4
|
-
import { extractErrorMsg, pretty, getSaferSubstring, HashAlgorithm } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
4
|
+
import { extractErrorMsg, pretty, getSaferSubstring, HashAlgorithm, delay } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
5
5
|
import { getIbAndGib, getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
|
|
6
6
|
import { getGibInfo } from "@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs";
|
|
7
7
|
import { KeystoneService_V1 } from "@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs";
|
|
@@ -14,14 +14,14 @@ import { getTjpAddr, toDto, getTimelinesGroupedByTjp } from "@ibgib/core-gib/dis
|
|
|
14
14
|
import { parseSpaceIb } from "@ibgib/core-gib/dist/witness/space/space-helper.mjs";
|
|
15
15
|
import { executeSsoOAuthPopup } from "../../../../identity/sso-popup-helper.mjs";
|
|
16
16
|
import { getComponentCtorArg } from "../../../../app-bootstrap/init-orchestration.mjs";
|
|
17
|
-
import {
|
|
17
|
+
import { promptForAuthoritySecret } from "../../../../helpers.web.mjs";
|
|
18
18
|
import { isSsoProviderLinked, getKeystoneCapabilities, isDelegateExpired, isCustodialDelegate } from "@ibgib/core-gib/dist/keystone/delegate/delegate-helpers.mjs";
|
|
19
19
|
import { generatePoolChallenges } from "@ibgib/core-gib/dist/keystone/keystone-helpers.mjs";
|
|
20
20
|
import { POOL_ID_MANAGE, KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT, KEYSTONE_VERB_MANAGE } from "@ibgib/core-gib/dist/keystone/keystone-constants.mjs";
|
|
21
21
|
import { generateDefaultDeviceTag } from "../../../../util/device-helper.mjs";
|
|
22
22
|
import { getGlobalMetaspace_waitIfNeeded } from "../../../../helpers.mjs";
|
|
23
23
|
import { createSovereignDeviceSyncDelegate, createOAuth2DeviceSyncDelegate } from "../../../../identity/device-delegate-helper.mjs";
|
|
24
|
-
import { isPasskeyPrfSupported, registerPasskeyCredential, evaluatePasskeyPrf, bufferToHex } from "../../../../identity/passkey-helper.mjs";
|
|
24
|
+
import { isPasskeyPrfSupported, registerPasskeyCredential, evaluatePasskeyPrf, createPasskeyDeviceSyncDelegate, bufferToHex } from "../../../../identity/passkey-helper.mjs";
|
|
25
25
|
import { KeystoneChallengeType, KeystoneReplenishStrategy } from "@ibgib/core-gib/dist/keystone/keystone-types.mjs";
|
|
26
26
|
const logalot = false;
|
|
27
27
|
export const KEYSTONE_DETAILS_COMPONENT_NAME = 'ibgib-keystone-details';
|
|
@@ -183,6 +183,7 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
183
183
|
this.elements.passkeysCardEl = shadow.getElementById('passkeys-card');
|
|
184
184
|
this.elements.passkeysListEl = shadow.getElementById('passkeys-list');
|
|
185
185
|
this.elements.btnAddPasskeyEl = shadow.getElementById('btn-add-passkey');
|
|
186
|
+
this.elements.selectPasskeyAttachmentEl = shadow.getElementById('select-passkey-attachment');
|
|
186
187
|
this.elements.passkeyStatusMsgEl = shadow.getElementById('passkey-status-msg');
|
|
187
188
|
this.elements.containerEl = shadow.getElementById('container');
|
|
188
189
|
this.elements.historicalWarningBannerEl = shadow.getElementById('historical-warning-banner');
|
|
@@ -514,19 +515,17 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
514
515
|
updatePasswordCardView() {
|
|
515
516
|
const caps = !!this.ibGib ?
|
|
516
517
|
getKeystoneCapabilities({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails }) :
|
|
517
|
-
|
|
518
|
+
undefined;
|
|
518
519
|
const cardTitleEl = this.elements?.changePasswordCardEl?.querySelector('h3');
|
|
519
520
|
const cardDescEl = this.elements?.changePasswordCardEl?.querySelector('.description');
|
|
520
521
|
const currentPasswordGroup = this.elements?.inputCurrentPasswordEl?.closest('.form-group');
|
|
521
522
|
const btnChangePassword = this.elements?.btnChangePasswordEl;
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
false;
|
|
525
|
-
if (isCustodialOnly) {
|
|
523
|
+
const hasPassword = !!caps?.hasPassword;
|
|
524
|
+
if (!hasPassword) {
|
|
526
525
|
if (cardTitleEl)
|
|
527
526
|
cardTitleEl.textContent = "Add Master Password";
|
|
528
527
|
if (cardDescEl)
|
|
529
|
-
cardDescEl.textContent = "Establish
|
|
528
|
+
cardDescEl.textContent = "Establish a master password for this identity. This enables sovereign password authentication alongside your passkeys and linked providers.";
|
|
530
529
|
if (currentPasswordGroup)
|
|
531
530
|
currentPasswordGroup.style.display = 'none';
|
|
532
531
|
if (btnChangePassword)
|
|
@@ -1011,12 +1010,17 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1011
1010
|
const lc = `${this.lc}[${this.handleRevokeDelegate.name}]`;
|
|
1012
1011
|
try {
|
|
1013
1012
|
let masterSecret;
|
|
1013
|
+
let matchedPoolId;
|
|
1014
1014
|
try {
|
|
1015
|
-
|
|
1015
|
+
const authResult = await promptForAuthoritySecret({
|
|
1016
|
+
keystone: this.ibGib,
|
|
1017
|
+
aggregatedDetails: this.aggregatedDetails,
|
|
1018
|
+
title: 'Revoke Delegate',
|
|
1016
1019
|
msg: `Enter Master Password or touch security key to revoke delegate '${deviceName}':`,
|
|
1017
|
-
|
|
1018
|
-
passkeys: this.aggregatedDetails?.passkeys,
|
|
1020
|
+
verb: KEYSTONE_VERB_MANAGE
|
|
1019
1021
|
});
|
|
1022
|
+
masterSecret = authResult.secret;
|
|
1023
|
+
matchedPoolId = authResult.poolId;
|
|
1020
1024
|
}
|
|
1021
1025
|
catch (err) {
|
|
1022
1026
|
// User cancelled prompt
|
|
@@ -1034,24 +1038,6 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1034
1038
|
throw new Error("Local space not found");
|
|
1035
1039
|
}
|
|
1036
1040
|
const keystoneSvc = new KeystoneService_V1();
|
|
1037
|
-
// Resolve which manage pool matches the provided masterSecret
|
|
1038
|
-
let matchedPoolId;
|
|
1039
|
-
const candidatePools = this.ibGib.data?.challengePools.filter(p => p.id === 'manage' || p.id.startsWith('passkey-manage-') || (p.config.allowedVerbs && p.config.allowedVerbs.includes(KEYSTONE_VERB_MANAGE))) || [];
|
|
1040
|
-
for (const pool of candidatePools) {
|
|
1041
|
-
const ok = await keystoneSvc.verifySigningSecret({
|
|
1042
|
-
keystoneIbGib: this.ibGib,
|
|
1043
|
-
signingSecret: masterSecret,
|
|
1044
|
-
poolId: pool.id
|
|
1045
|
-
});
|
|
1046
|
-
if (ok) {
|
|
1047
|
-
matchedPoolId = pool.id;
|
|
1048
|
-
break;
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
if (!matchedPoolId) {
|
|
1052
|
-
this.setSyncStatus("Failed to authorize revocation (invalid secret/passkey).", "error");
|
|
1053
|
-
return;
|
|
1054
|
-
}
|
|
1055
1041
|
// Unregister delegate from parent keystone
|
|
1056
1042
|
const evolvedParent = await keystoneSvc.unregisterDelegate({
|
|
1057
1043
|
parentKeystone: this.ibGib,
|
|
@@ -1210,10 +1196,16 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1210
1196
|
const username = this.aggregatedDetails?.username || this.ibGib.data?.frameDetails?.username || 'user';
|
|
1211
1197
|
const rawSalt = crypto.getRandomValues(new Uint8Array(32));
|
|
1212
1198
|
const saltHex = bufferToHex(rawSalt.buffer);
|
|
1199
|
+
const attachmentVal = this.elements?.selectPasskeyAttachmentEl?.value;
|
|
1200
|
+
const authenticatorAttachment = (attachmentVal === 'cross-platform' || attachmentVal === 'platform')
|
|
1201
|
+
? attachmentVal
|
|
1202
|
+
: undefined;
|
|
1213
1203
|
const regResult = await registerPasskeyCredential({
|
|
1214
1204
|
username,
|
|
1215
1205
|
displayName: username,
|
|
1216
|
-
salt: saltHex
|
|
1206
|
+
salt: saltHex,
|
|
1207
|
+
userVerification: 'preferred',
|
|
1208
|
+
authenticatorAttachment,
|
|
1217
1209
|
});
|
|
1218
1210
|
const { credentialId, transports } = regResult;
|
|
1219
1211
|
let passkeySecret = regResult.passkeySecret;
|
|
@@ -1221,7 +1213,8 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1221
1213
|
const prfResult = await evaluatePasskeyPrf({
|
|
1222
1214
|
credentialId,
|
|
1223
1215
|
salt: saltHex,
|
|
1224
|
-
transports
|
|
1216
|
+
transports,
|
|
1217
|
+
userVerification: 'preferred'
|
|
1225
1218
|
});
|
|
1226
1219
|
passkeySecret = prfResult.passkeySecret;
|
|
1227
1220
|
}
|
|
@@ -1245,6 +1238,32 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1245
1238
|
throw new Error("Local space not found");
|
|
1246
1239
|
const keystoneService = new KeystoneService_V1();
|
|
1247
1240
|
let evolvedKeystone;
|
|
1241
|
+
const poolConfig = {
|
|
1242
|
+
id: poolId,
|
|
1243
|
+
type: KeystoneChallengeType.hash_reveal_v1,
|
|
1244
|
+
salt: poolId,
|
|
1245
|
+
algo: HashAlgorithm.sha_256,
|
|
1246
|
+
rounds: 1,
|
|
1247
|
+
allowedVerbs: [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT],
|
|
1248
|
+
behavior: {
|
|
1249
|
+
size: 10,
|
|
1250
|
+
replenish: KeystoneReplenishStrategy.topUp,
|
|
1251
|
+
selectSequentially: 1,
|
|
1252
|
+
selectRandomly: 0,
|
|
1253
|
+
targetBindingCount: 0,
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
const challenges = await generatePoolChallenges({
|
|
1257
|
+
config: poolConfig,
|
|
1258
|
+
masterSecret: passkeySecret
|
|
1259
|
+
});
|
|
1260
|
+
const newPool = {
|
|
1261
|
+
id: poolId,
|
|
1262
|
+
config: poolConfig,
|
|
1263
|
+
challenges
|
|
1264
|
+
};
|
|
1265
|
+
let authSecret;
|
|
1266
|
+
let signingPoolId;
|
|
1248
1267
|
if (isCustodialOnly) {
|
|
1249
1268
|
// Elevate custodial account with sovereign passkey
|
|
1250
1269
|
this.showBusy({
|
|
@@ -1253,34 +1272,12 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1253
1272
|
msg: 'Establishing sovereign passkey pool on identity...',
|
|
1254
1273
|
animationEmoji: '⚡'
|
|
1255
1274
|
});
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
id: poolId,
|
|
1259
|
-
type: KeystoneChallengeType.hash_reveal_v1,
|
|
1260
|
-
salt: poolId,
|
|
1261
|
-
algo: HashAlgorithm.sha_256,
|
|
1262
|
-
rounds: 1,
|
|
1263
|
-
allowedVerbs: [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT],
|
|
1264
|
-
behavior: {
|
|
1265
|
-
size: 10,
|
|
1266
|
-
replenish: KeystoneReplenishStrategy.topUp,
|
|
1267
|
-
selectSequentially: 1,
|
|
1268
|
-
selectRandomly: 0,
|
|
1269
|
-
targetBindingCount: 0,
|
|
1270
|
-
}
|
|
1271
|
-
};
|
|
1272
|
-
const challenges = await generatePoolChallenges({
|
|
1273
|
-
config: poolConfig,
|
|
1274
|
-
masterSecret: passkeySecret
|
|
1275
|
-
});
|
|
1276
|
-
const newPool = {
|
|
1277
|
-
id: poolId,
|
|
1278
|
-
config: poolConfig,
|
|
1279
|
-
challenges
|
|
1280
|
-
};
|
|
1275
|
+
authSecret = passkeySecret;
|
|
1276
|
+
signingPoolId = poolId;
|
|
1281
1277
|
evolvedKeystone = await keystoneService.addPools({
|
|
1282
1278
|
latestKeystone: this.ibGib,
|
|
1283
|
-
masterSecret:
|
|
1279
|
+
masterSecret: authSecret,
|
|
1280
|
+
signingPoolId,
|
|
1284
1281
|
newPools: [newPool],
|
|
1285
1282
|
frameDetails: {
|
|
1286
1283
|
passkeys: {
|
|
@@ -1296,37 +1293,19 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1296
1293
|
}
|
|
1297
1294
|
else {
|
|
1298
1295
|
// Secondary passkey registration on existing sovereign identity:
|
|
1299
|
-
// Prompt user to authorize with current master password
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
? 'Enter your current Master Secret (Password) or touch security key to authorize adding this passkey:'
|
|
1308
|
-
: `Incorrect password. Please try again (${maxTries - tries} tries remaining):`;
|
|
1309
|
-
authSecret = await promptForSecret({
|
|
1310
|
-
msg: promptMsg,
|
|
1311
|
-
confirm: false,
|
|
1312
|
-
passkeys: this.aggregatedDetails?.passkeys,
|
|
1313
|
-
});
|
|
1314
|
-
}
|
|
1315
|
-
catch (err) {
|
|
1316
|
-
authSecret = undefined;
|
|
1317
|
-
break;
|
|
1318
|
-
}
|
|
1319
|
-
if (!authSecret)
|
|
1320
|
-
break;
|
|
1321
|
-
// Verify secret against manage/passkey pools
|
|
1322
|
-
isCorrect = await keystoneService.verifySigningSecret({
|
|
1323
|
-
keystoneIbGib: this.ibGib,
|
|
1324
|
-
signingSecret: authSecret
|
|
1296
|
+
// Prompt user to authorize with current master password or passkey
|
|
1297
|
+
try {
|
|
1298
|
+
const authResult = await promptForAuthoritySecret({
|
|
1299
|
+
keystone: this.ibGib,
|
|
1300
|
+
aggregatedDetails: this.aggregatedDetails,
|
|
1301
|
+
title: 'Authorize New Passkey',
|
|
1302
|
+
msg: 'Enter Master Password or touch security key to authorize adding this passkey:',
|
|
1303
|
+
verb: KEYSTONE_VERB_MANAGE
|
|
1325
1304
|
});
|
|
1326
|
-
|
|
1327
|
-
|
|
1305
|
+
authSecret = authResult.secret;
|
|
1306
|
+
signingPoolId = authResult.poolId;
|
|
1328
1307
|
}
|
|
1329
|
-
|
|
1308
|
+
catch (err) {
|
|
1330
1309
|
this.setPasskeyStatus("Registration cancelled (authorization required).", "error");
|
|
1331
1310
|
return;
|
|
1332
1311
|
}
|
|
@@ -1336,33 +1315,10 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1336
1315
|
msg: 'Adding cryptographic challenge pool and evolving identity...',
|
|
1337
1316
|
animationEmoji: '🛡️'
|
|
1338
1317
|
});
|
|
1339
|
-
const poolConfig = {
|
|
1340
|
-
id: poolId,
|
|
1341
|
-
type: KeystoneChallengeType.hash_reveal_v1,
|
|
1342
|
-
salt: poolId,
|
|
1343
|
-
algo: HashAlgorithm.sha_256,
|
|
1344
|
-
rounds: 1,
|
|
1345
|
-
allowedVerbs: [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT],
|
|
1346
|
-
behavior: {
|
|
1347
|
-
size: 10,
|
|
1348
|
-
replenish: KeystoneReplenishStrategy.topUp,
|
|
1349
|
-
selectSequentially: 1,
|
|
1350
|
-
selectRandomly: 0,
|
|
1351
|
-
targetBindingCount: 0,
|
|
1352
|
-
}
|
|
1353
|
-
};
|
|
1354
|
-
const challenges = await generatePoolChallenges({
|
|
1355
|
-
config: poolConfig,
|
|
1356
|
-
masterSecret: passkeySecret
|
|
1357
|
-
});
|
|
1358
|
-
const newPool = {
|
|
1359
|
-
id: poolId,
|
|
1360
|
-
config: poolConfig,
|
|
1361
|
-
challenges
|
|
1362
|
-
};
|
|
1363
1318
|
evolvedKeystone = await keystoneService.addPools({
|
|
1364
1319
|
latestKeystone: this.ibGib,
|
|
1365
1320
|
masterSecret: authSecret,
|
|
1321
|
+
signingPoolId,
|
|
1366
1322
|
newPools: [newPool],
|
|
1367
1323
|
frameDetails: {
|
|
1368
1324
|
passkeys: {
|
|
@@ -1375,22 +1331,66 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1375
1331
|
metaspace,
|
|
1376
1332
|
space
|
|
1377
1333
|
});
|
|
1334
|
+
// Sync pool addition frame to server
|
|
1335
|
+
const primaryTjpAddr = getTjpAddr({ ibGib: evolvedKeystone, defaultIfNone: 'incomingAddr' });
|
|
1336
|
+
const resSyncPool = await getApiBridge().putEvolveKeystone({
|
|
1337
|
+
domainAddr: primaryTjpAddr,
|
|
1338
|
+
keystoneIbGib: evolvedKeystone,
|
|
1339
|
+
relatedKeystoneIbGibs: []
|
|
1340
|
+
});
|
|
1341
|
+
if (!resSyncPool.success) {
|
|
1342
|
+
throw new Error(`Server synchronization of passkey pool failed: ${resSyncPool.message}`);
|
|
1343
|
+
}
|
|
1378
1344
|
}
|
|
1379
|
-
//
|
|
1345
|
+
// Construct outward device sync delegate for this passkey
|
|
1346
|
+
this.showBusy({
|
|
1347
|
+
isBusy: true,
|
|
1348
|
+
title: 'Creating Sync Delegate...',
|
|
1349
|
+
msg: 'Constructing outward passkey device sync delegate...',
|
|
1350
|
+
animationEmoji: '📱'
|
|
1351
|
+
});
|
|
1352
|
+
const primaryTjpAddr = getTjpAddr({ ibGib: evolvedKeystone, defaultIfNone: 'incomingAddr' });
|
|
1353
|
+
const { delegateKeystone, deviceTag } = await createPasskeyDeviceSyncDelegate({
|
|
1354
|
+
username,
|
|
1355
|
+
primaryTjpAddr,
|
|
1356
|
+
passkeySecret,
|
|
1357
|
+
transports,
|
|
1358
|
+
metaspace,
|
|
1359
|
+
space
|
|
1360
|
+
});
|
|
1361
|
+
// Register outward sync delegate on primary keystone
|
|
1362
|
+
this.showBusy({
|
|
1363
|
+
isBusy: true,
|
|
1364
|
+
title: 'Registering Delegate...',
|
|
1365
|
+
msg: 'Registering outward passkey delegate on primary identity...',
|
|
1366
|
+
animationEmoji: '📝'
|
|
1367
|
+
});
|
|
1368
|
+
const delegateSigningPoolId = isCustodialOnly ? poolId : signingPoolId;
|
|
1369
|
+
const delegateAuthSecret = isCustodialOnly ? passkeySecret : authSecret;
|
|
1370
|
+
evolvedKeystone = await keystoneService.registerDelegate({
|
|
1371
|
+
parentKeystone: evolvedKeystone,
|
|
1372
|
+
delegateKeystone,
|
|
1373
|
+
masterSecret: delegateAuthSecret,
|
|
1374
|
+
signingPoolId: delegateSigningPoolId,
|
|
1375
|
+
metaspace,
|
|
1376
|
+
space,
|
|
1377
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT],
|
|
1378
|
+
deviceName: deviceTag
|
|
1379
|
+
});
|
|
1380
|
+
// Sync evolved keystone and delegate to server
|
|
1380
1381
|
this.showBusy({
|
|
1381
1382
|
isBusy: true,
|
|
1382
1383
|
title: 'Syncing to Server...',
|
|
1383
|
-
msg: 'Publishing evolved identity keystone to server...',
|
|
1384
|
+
msg: 'Publishing evolved identity keystone and delegate to server...',
|
|
1384
1385
|
animationEmoji: '⚡'
|
|
1385
1386
|
});
|
|
1386
|
-
const primaryTjpAddr = getTjpAddr({ ibGib: evolvedKeystone, defaultIfNone: 'incomingAddr' });
|
|
1387
1387
|
const resSync = await getApiBridge().putEvolveKeystone({
|
|
1388
1388
|
domainAddr: primaryTjpAddr,
|
|
1389
1389
|
keystoneIbGib: evolvedKeystone,
|
|
1390
|
-
relatedKeystoneIbGibs: []
|
|
1390
|
+
relatedKeystoneIbGibs: [delegateKeystone]
|
|
1391
1391
|
});
|
|
1392
1392
|
if (!resSync.success) {
|
|
1393
|
-
|
|
1393
|
+
throw new Error(`Server synchronization of delegate failed: ${resSync.message}`);
|
|
1394
1394
|
}
|
|
1395
1395
|
// Update Special Index
|
|
1396
1396
|
await updateSpecialIndex({
|
|
@@ -1398,7 +1398,7 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1398
1398
|
rel8nInfos: [
|
|
1399
1399
|
{
|
|
1400
1400
|
rel8nName: "keystone",
|
|
1401
|
-
ibGibs: [toDto({ ibGib: evolvedKeystone })]
|
|
1401
|
+
ibGibs: [toDto({ ibGib: evolvedKeystone }), toDto({ ibGib: delegateKeystone })]
|
|
1402
1402
|
}
|
|
1403
1403
|
],
|
|
1404
1404
|
metaspace,
|
|
@@ -1427,34 +1427,19 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1427
1427
|
try {
|
|
1428
1428
|
const keystoneService = new KeystoneService_V1();
|
|
1429
1429
|
let masterSecret;
|
|
1430
|
-
let
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
masterSecret = await promptForSecret({
|
|
1439
|
-
msg: promptMsg,
|
|
1440
|
-
confirm: false,
|
|
1441
|
-
passkeys: this.aggregatedDetails?.passkeys,
|
|
1442
|
-
});
|
|
1443
|
-
}
|
|
1444
|
-
catch (err) {
|
|
1445
|
-
masterSecret = undefined;
|
|
1446
|
-
break;
|
|
1447
|
-
}
|
|
1448
|
-
if (!masterSecret)
|
|
1449
|
-
break;
|
|
1450
|
-
isCorrect = await keystoneService.verifySigningSecret({
|
|
1451
|
-
keystoneIbGib: this.ibGib,
|
|
1452
|
-
signingSecret: masterSecret
|
|
1430
|
+
let signingPoolId;
|
|
1431
|
+
try {
|
|
1432
|
+
const authResult = await promptForAuthoritySecret({
|
|
1433
|
+
keystone: this.ibGib,
|
|
1434
|
+
aggregatedDetails: this.aggregatedDetails,
|
|
1435
|
+
title: 'Authorize Removal',
|
|
1436
|
+
msg: `Are you sure you want to remove "${passkeyName}"? Enter your Master Secret (Password) or touch security key to authorize:`,
|
|
1437
|
+
verb: KEYSTONE_VERB_MANAGE
|
|
1453
1438
|
});
|
|
1454
|
-
|
|
1455
|
-
|
|
1439
|
+
masterSecret = authResult.secret;
|
|
1440
|
+
signingPoolId = authResult.poolId;
|
|
1456
1441
|
}
|
|
1457
|
-
|
|
1442
|
+
catch (err) {
|
|
1458
1443
|
this.setPasskeyStatus("Removal cancelled or unauthorized.", "error");
|
|
1459
1444
|
return;
|
|
1460
1445
|
}
|
|
@@ -1477,7 +1462,8 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1477
1462
|
if (poolExists) {
|
|
1478
1463
|
evolvedKeystone = await keystoneService.removePools({
|
|
1479
1464
|
latestKeystone: this.ibGib,
|
|
1480
|
-
masterSecret,
|
|
1465
|
+
masterSecret: masterSecret,
|
|
1466
|
+
signingPoolId: signingPoolId,
|
|
1481
1467
|
poolIds: [poolIdToRemove],
|
|
1482
1468
|
frameDetails: {
|
|
1483
1469
|
passkeys: {
|
|
@@ -1492,11 +1478,12 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1492
1478
|
});
|
|
1493
1479
|
}
|
|
1494
1480
|
else {
|
|
1481
|
+
// If pool doesn't exist, just update frameDetails
|
|
1495
1482
|
evolvedKeystone = await keystoneService.sign({
|
|
1496
1483
|
latestKeystone: this.ibGib,
|
|
1497
|
-
masterSecret,
|
|
1484
|
+
masterSecret: masterSecret,
|
|
1485
|
+
poolId: signingPoolId,
|
|
1498
1486
|
claim: {
|
|
1499
|
-
verb: POOL_ID_MANAGE,
|
|
1500
1487
|
target: getIbGibAddr({ ibGib: this.ibGib })
|
|
1501
1488
|
},
|
|
1502
1489
|
frameDetails: {
|
|
@@ -1664,46 +1651,23 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1664
1651
|
throw new Error(`Failed to retrieve custodian challenge pool: ${linkData.message || 'unknown error'}`);
|
|
1665
1652
|
}
|
|
1666
1653
|
const custodianPool = linkData.custodianPool;
|
|
1667
|
-
// 2. Prompt user for
|
|
1668
|
-
this.showBusy({ isBusy: false });
|
|
1654
|
+
// 2. Prompt user for authorization secret
|
|
1669
1655
|
const keystoneService = new KeystoneService_V1();
|
|
1670
1656
|
let masterSecret;
|
|
1671
|
-
let
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
masterSecret = await promptForSecret({
|
|
1680
|
-
msg: promptMsg,
|
|
1681
|
-
confirm: false
|
|
1682
|
-
});
|
|
1683
|
-
}
|
|
1684
|
-
catch (error) {
|
|
1685
|
-
// cancelled
|
|
1686
|
-
if (logalot) {
|
|
1687
|
-
console.log(`${lc}[promptForSecret] user cancelled (I: 911ff1fb0234949228481968e9087626)`);
|
|
1688
|
-
}
|
|
1689
|
-
masterSecret = undefined;
|
|
1690
|
-
break;
|
|
1691
|
-
}
|
|
1692
|
-
if (!masterSecret) {
|
|
1693
|
-
break;
|
|
1694
|
-
}
|
|
1695
|
-
// Verify secret using verifySigningSecret (requires manage poolId)
|
|
1696
|
-
isCorrect = await keystoneService.verifySigningSecret({
|
|
1697
|
-
keystoneIbGib: this.ibGib,
|
|
1698
|
-
signingSecret: masterSecret,
|
|
1699
|
-
poolId: 'manage'
|
|
1657
|
+
let signingPoolId;
|
|
1658
|
+
try {
|
|
1659
|
+
const authResult = await promptForAuthoritySecret({
|
|
1660
|
+
keystone: this.ibGib,
|
|
1661
|
+
aggregatedDetails: this.aggregatedDetails,
|
|
1662
|
+
title: `Link ${providerName}`,
|
|
1663
|
+
msg: `Enter Master Password or touch security key to authorize linking ${providerName}:`,
|
|
1664
|
+
verb: KEYSTONE_VERB_MANAGE
|
|
1700
1665
|
});
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
}
|
|
1666
|
+
masterSecret = authResult.secret;
|
|
1667
|
+
signingPoolId = authResult.poolId;
|
|
1704
1668
|
}
|
|
1705
|
-
|
|
1706
|
-
this.setSSOStatus("Link cancelled or failed
|
|
1669
|
+
catch (error) {
|
|
1670
|
+
this.setSSOStatus("Link cancelled or failed.", "error");
|
|
1707
1671
|
return;
|
|
1708
1672
|
}
|
|
1709
1673
|
this.showBusy({
|
|
@@ -1724,6 +1688,7 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1724
1688
|
const evolvedParent = await keystoneService.addPools({
|
|
1725
1689
|
latestKeystone: this.ibGib,
|
|
1726
1690
|
masterSecret: masterSecret,
|
|
1691
|
+
signingPoolId: signingPoolId,
|
|
1727
1692
|
newPools: [custodianPool],
|
|
1728
1693
|
metaspace,
|
|
1729
1694
|
space
|
|
@@ -1751,7 +1716,7 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1751
1716
|
parentKeystone: evolvedParent,
|
|
1752
1717
|
delegateKeystone,
|
|
1753
1718
|
masterSecret: masterSecret,
|
|
1754
|
-
signingPoolId:
|
|
1719
|
+
signingPoolId: signingPoolId,
|
|
1755
1720
|
metaspace,
|
|
1756
1721
|
space,
|
|
1757
1722
|
allowedVerbs: [KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT],
|
|
@@ -1820,41 +1785,23 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1820
1785
|
const providerName = providerId === 'google' ? 'Google' : providerId === 'github' ? 'GitHub' : providerId;
|
|
1821
1786
|
this.setSSOStatus(`Unlinking ${providerName}...`, "info");
|
|
1822
1787
|
try {
|
|
1823
|
-
// 1. Prompt user for
|
|
1788
|
+
// 1. Prompt user for authorization secret
|
|
1824
1789
|
const keystoneService = new KeystoneService_V1();
|
|
1825
1790
|
let masterSecret;
|
|
1826
|
-
let
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
masterSecret = await promptForSecret({
|
|
1835
|
-
msg: promptMsg,
|
|
1836
|
-
confirm: false,
|
|
1837
|
-
passkeys: this.aggregatedDetails?.passkeys,
|
|
1838
|
-
});
|
|
1839
|
-
}
|
|
1840
|
-
catch (error) {
|
|
1841
|
-
masterSecret = undefined;
|
|
1842
|
-
break;
|
|
1843
|
-
}
|
|
1844
|
-
if (!masterSecret) {
|
|
1845
|
-
break;
|
|
1846
|
-
}
|
|
1847
|
-
// Verify secret using verifySigningSecret
|
|
1848
|
-
isCorrect = await keystoneService.verifySigningSecret({
|
|
1849
|
-
keystoneIbGib: this.ibGib,
|
|
1850
|
-
signingSecret: masterSecret
|
|
1791
|
+
let signingPoolId;
|
|
1792
|
+
try {
|
|
1793
|
+
const authResult = await promptForAuthoritySecret({
|
|
1794
|
+
keystone: this.ibGib,
|
|
1795
|
+
aggregatedDetails: this.aggregatedDetails,
|
|
1796
|
+
title: `Unlink ${providerName}`,
|
|
1797
|
+
msg: `Are you sure you want to unlink your ${providerName} account? If so, enter your Master Password or touch security key to authorize.`,
|
|
1798
|
+
verb: KEYSTONE_VERB_MANAGE
|
|
1851
1799
|
});
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
}
|
|
1800
|
+
masterSecret = authResult.secret;
|
|
1801
|
+
signingPoolId = authResult.poolId;
|
|
1855
1802
|
}
|
|
1856
|
-
|
|
1857
|
-
this.setSSOStatus("Unlink cancelled or failed
|
|
1803
|
+
catch (error) {
|
|
1804
|
+
this.setSSOStatus("Unlink cancelled or failed.", "error");
|
|
1858
1805
|
return;
|
|
1859
1806
|
}
|
|
1860
1807
|
this.showBusy({
|
|
@@ -1876,6 +1823,7 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1876
1823
|
let evolvedParent = await keystoneService.removePools({
|
|
1877
1824
|
latestKeystone: this.ibGib,
|
|
1878
1825
|
masterSecret: masterSecret,
|
|
1826
|
+
signingPoolId: signingPoolId,
|
|
1879
1827
|
poolIds: [targetPoolId],
|
|
1880
1828
|
metaspace,
|
|
1881
1829
|
space
|
|
@@ -2134,98 +2082,13 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
2134
2082
|
await metaspace.put({ ibGibs: [evolvedParent], space });
|
|
2135
2083
|
await metaspace.registerNewIbGib({ ibGib: evolvedParent, space });
|
|
2136
2084
|
}
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
title: 'Creating Sovereign Sync Delegate...',
|
|
2141
|
-
msg: 'Deriving local device sync delegate keys from Master Password...',
|
|
2142
|
-
animationEmoji: '🔑'
|
|
2143
|
-
});
|
|
2144
|
-
const rawUsername = this.aggregatedDetails?.username
|
|
2145
|
-
|| this.ibGib?.data?.username
|
|
2146
|
-
|| this.aggregatedDetails?.emails?.[0]?.split('@')[0]
|
|
2147
|
-
|| this.ibGib?.data?.emails?.[0]?.split('@')[0]
|
|
2148
|
-
|| 'user';
|
|
2149
|
-
const { delegateKeystone: userSyncDelegate, deviceTag } = await createSovereignDeviceSyncDelegate({
|
|
2150
|
-
username: rawUsername,
|
|
2085
|
+
await this.registerAndSyncSovereignPasswordDelegate({
|
|
2086
|
+
evolvedParent,
|
|
2087
|
+
newPassword,
|
|
2151
2088
|
primaryTjpAddr,
|
|
2152
|
-
masterSecret: newPassword,
|
|
2153
|
-
metaspace,
|
|
2154
|
-
space,
|
|
2155
|
-
});
|
|
2156
|
-
// 2. Register the new user sync delegate on the evolved parent keystone
|
|
2157
|
-
this.showBusy({
|
|
2158
|
-
isBusy: true,
|
|
2159
|
-
title: 'Registering Sovereign Delegate...',
|
|
2160
|
-
msg: 'Registering user sync delegate on primary keystone...',
|
|
2161
|
-
animationEmoji: '📝'
|
|
2162
|
-
});
|
|
2163
|
-
const keystoneService = new KeystoneService_V1();
|
|
2164
|
-
const parentWithUserDelegate = await keystoneService.registerDelegate({
|
|
2165
|
-
parentKeystone: evolvedParent,
|
|
2166
|
-
delegateKeystone: userSyncDelegate,
|
|
2167
|
-
masterSecret: newPassword,
|
|
2168
|
-
signingPoolId: 'manage',
|
|
2169
|
-
metaspace,
|
|
2170
|
-
space,
|
|
2171
|
-
allowedVerbs: [KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT],
|
|
2172
|
-
deviceName: deviceTag
|
|
2173
|
-
});
|
|
2174
|
-
// 3. Post evolved keystone with new delegate to server
|
|
2175
|
-
this.showBusy({
|
|
2176
|
-
isBusy: true,
|
|
2177
|
-
title: 'Syncing to Server...',
|
|
2178
|
-
msg: 'Publishing updated domain identity and sovereign delegate to server...',
|
|
2179
|
-
animationEmoji: '⚡'
|
|
2180
|
-
});
|
|
2181
|
-
const resSync = await getApiBridge().putEvolveKeystone({
|
|
2182
|
-
domainAddr: primaryTjpAddr,
|
|
2183
|
-
keystoneIbGib: parentWithUserDelegate,
|
|
2184
|
-
relatedKeystoneIbGibs: [userSyncDelegate]
|
|
2185
|
-
});
|
|
2186
|
-
if (!resSync.success) {
|
|
2187
|
-
console.warn(`${lc} Warning syncing user delegate to server: ${resSync.message}`);
|
|
2188
|
-
}
|
|
2189
|
-
// 4. Save parentWithUserDelegate and userSyncDelegate to local space & metaspace
|
|
2190
|
-
await metaspace.put({ ibGibs: [parentWithUserDelegate, userSyncDelegate], space });
|
|
2191
|
-
await metaspace.registerNewIbGib({ ibGib: parentWithUserDelegate, space });
|
|
2192
|
-
await metaspace.registerNewIbGib({ ibGib: userSyncDelegate, space });
|
|
2193
|
-
await updateSpecialIndex({
|
|
2194
|
-
type: "keystones",
|
|
2195
|
-
rel8nInfos: [
|
|
2196
|
-
{
|
|
2197
|
-
rel8nName: "keystone",
|
|
2198
|
-
ibGibs: [toDto({ ibGib: parentWithUserDelegate }), toDto({ ibGib: userSyncDelegate })]
|
|
2199
|
-
}
|
|
2200
|
-
],
|
|
2201
2089
|
metaspace,
|
|
2202
2090
|
space
|
|
2203
2091
|
});
|
|
2204
|
-
const evolvedAddr = getIbGibAddr({ ibGib: parentWithUserDelegate });
|
|
2205
|
-
// Dispatch EVENT_IBGIB_IDENTITY_CHANGED
|
|
2206
|
-
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_IDENTITY_CHANGED, {
|
|
2207
|
-
detail: { activeIdentityAddr: evolvedAddr },
|
|
2208
|
-
bubbles: true,
|
|
2209
|
-
composed: true
|
|
2210
|
-
}));
|
|
2211
|
-
this.setChangePasswordStatus("Successfully added Master Password! Identity is now sovereign.", "success");
|
|
2212
|
-
// Reset OTP section and button text
|
|
2213
|
-
if (this.elements?.sectionAddPasswordOtpEl) {
|
|
2214
|
-
this.elements.sectionAddPasswordOtpEl.classList.add('hidden');
|
|
2215
|
-
if (this.elements.inputAddPasswordOtpEl) {
|
|
2216
|
-
this.elements.inputAddPasswordOtpEl.value = '';
|
|
2217
|
-
}
|
|
2218
|
-
}
|
|
2219
|
-
if (this.elements?.btnChangePasswordEl) {
|
|
2220
|
-
this.elements.btnChangePasswordEl.textContent = "Update Password";
|
|
2221
|
-
}
|
|
2222
|
-
// Refresh the view
|
|
2223
|
-
this.ibGibAddr = evolvedAddr;
|
|
2224
|
-
this.staticIbGib = parentWithUserDelegate;
|
|
2225
|
-
await this.loadIbGib({ getLatest: true });
|
|
2226
|
-
// Clear inputs
|
|
2227
|
-
this.elements.inputNewPasswordEl.value = '';
|
|
2228
|
-
this.elements.inputConfirmPasswordEl.value = '';
|
|
2229
2092
|
}
|
|
2230
2093
|
catch (error) {
|
|
2231
2094
|
console.error(`${lc} Elevation failed: ${extractErrorMsg(error)}`);
|
|
@@ -2287,14 +2150,265 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
2287
2150
|
}
|
|
2288
2151
|
}
|
|
2289
2152
|
}
|
|
2153
|
+
/**
|
|
2154
|
+
* Common DRY post-elevation workflow: creates and registers the password-based
|
|
2155
|
+
* device sync delegate, synchronizes the evolved identity with the delegate to the server,
|
|
2156
|
+
* updates the local space and special index, and dispatches the identity change event.
|
|
2157
|
+
*/
|
|
2158
|
+
async registerAndSyncSovereignPasswordDelegate({ evolvedParent, newPassword, primaryTjpAddr, metaspace, space, }) {
|
|
2159
|
+
const lc = `${this.lc}[${this.registerAndSyncSovereignPasswordDelegate.name}]`;
|
|
2160
|
+
// 1. Create a password-based sync delegate for this device
|
|
2161
|
+
this.showBusy({
|
|
2162
|
+
isBusy: true,
|
|
2163
|
+
title: 'Creating Sovereign Sync Delegate...',
|
|
2164
|
+
msg: 'Deriving local device sync delegate keys from Master Password...',
|
|
2165
|
+
animationEmoji: '🔑'
|
|
2166
|
+
});
|
|
2167
|
+
await delay(50);
|
|
2168
|
+
const rawUsername = this.aggregatedDetails?.username
|
|
2169
|
+
|| this.ibGib?.data?.username
|
|
2170
|
+
|| this.aggregatedDetails?.emails?.[0]?.split('@')[0]
|
|
2171
|
+
|| this.ibGib?.data?.emails?.[0]?.split('@')[0]
|
|
2172
|
+
|| 'user';
|
|
2173
|
+
const { delegateKeystone: userSyncDelegate, deviceTag } = await createSovereignDeviceSyncDelegate({
|
|
2174
|
+
username: rawUsername,
|
|
2175
|
+
primaryTjpAddr,
|
|
2176
|
+
masterSecret: newPassword,
|
|
2177
|
+
metaspace,
|
|
2178
|
+
space,
|
|
2179
|
+
});
|
|
2180
|
+
// 2. Register the new user sync delegate on the evolved parent keystone
|
|
2181
|
+
this.showBusy({
|
|
2182
|
+
isBusy: true,
|
|
2183
|
+
title: 'Registering Sovereign Delegate...',
|
|
2184
|
+
msg: 'Registering user sync delegate on primary keystone...',
|
|
2185
|
+
animationEmoji: '📝'
|
|
2186
|
+
});
|
|
2187
|
+
await delay(50);
|
|
2188
|
+
const keystoneService = new KeystoneService_V1();
|
|
2189
|
+
const parentWithUserDelegate = await keystoneService.registerDelegate({
|
|
2190
|
+
parentKeystone: evolvedParent,
|
|
2191
|
+
delegateKeystone: userSyncDelegate,
|
|
2192
|
+
masterSecret: newPassword,
|
|
2193
|
+
signingPoolId: POOL_ID_MANAGE,
|
|
2194
|
+
metaspace,
|
|
2195
|
+
space,
|
|
2196
|
+
allowedVerbs: [KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT],
|
|
2197
|
+
deviceName: deviceTag
|
|
2198
|
+
});
|
|
2199
|
+
// 3. Post evolved keystone with new delegate to server
|
|
2200
|
+
this.showBusy({
|
|
2201
|
+
isBusy: true,
|
|
2202
|
+
title: 'Syncing to Server...',
|
|
2203
|
+
msg: 'Publishing updated domain identity and sovereign delegate to server...',
|
|
2204
|
+
animationEmoji: '⚡'
|
|
2205
|
+
});
|
|
2206
|
+
const resSync = await getApiBridge().putEvolveKeystone({
|
|
2207
|
+
domainAddr: primaryTjpAddr,
|
|
2208
|
+
keystoneIbGib: parentWithUserDelegate,
|
|
2209
|
+
relatedKeystoneIbGibs: [userSyncDelegate]
|
|
2210
|
+
});
|
|
2211
|
+
if (!resSync.success) {
|
|
2212
|
+
throw new Error(`Server sync of password delegate failed: ${resSync.message}`);
|
|
2213
|
+
}
|
|
2214
|
+
// 4. Save parentWithUserDelegate and userSyncDelegate to local space & metaspace
|
|
2215
|
+
await metaspace.put({ ibGibs: [parentWithUserDelegate, userSyncDelegate], space });
|
|
2216
|
+
await metaspace.registerNewIbGib({ ibGib: parentWithUserDelegate, space });
|
|
2217
|
+
await metaspace.registerNewIbGib({ ibGib: userSyncDelegate, space });
|
|
2218
|
+
await updateSpecialIndex({
|
|
2219
|
+
type: "keystones",
|
|
2220
|
+
rel8nInfos: [
|
|
2221
|
+
{
|
|
2222
|
+
rel8nName: "keystone",
|
|
2223
|
+
ibGibs: [toDto({ ibGib: parentWithUserDelegate }), toDto({ ibGib: userSyncDelegate })]
|
|
2224
|
+
}
|
|
2225
|
+
],
|
|
2226
|
+
metaspace,
|
|
2227
|
+
space
|
|
2228
|
+
});
|
|
2229
|
+
const evolvedAddr = getIbGibAddr({ ibGib: parentWithUserDelegate });
|
|
2230
|
+
// Dispatch EVENT_IBGIB_IDENTITY_CHANGED
|
|
2231
|
+
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_IDENTITY_CHANGED, {
|
|
2232
|
+
detail: { activeIdentityAddr: evolvedAddr },
|
|
2233
|
+
bubbles: true,
|
|
2234
|
+
composed: true
|
|
2235
|
+
}));
|
|
2236
|
+
this.setChangePasswordStatus("Successfully added Master Password! Identity is now sovereign.", "success");
|
|
2237
|
+
// Reset OTP section and button text
|
|
2238
|
+
if (this.elements?.sectionAddPasswordOtpEl) {
|
|
2239
|
+
this.elements.sectionAddPasswordOtpEl.classList.add('hidden');
|
|
2240
|
+
if (this.elements.inputAddPasswordOtpEl) {
|
|
2241
|
+
this.elements.inputAddPasswordOtpEl.value = '';
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
if (this.elements?.btnChangePasswordEl) {
|
|
2245
|
+
this.elements.btnChangePasswordEl.textContent = "Update Password";
|
|
2246
|
+
}
|
|
2247
|
+
// Refresh the view
|
|
2248
|
+
this.ibGibAddr = evolvedAddr;
|
|
2249
|
+
this.staticIbGib = parentWithUserDelegate;
|
|
2250
|
+
await this.loadIbGib({ getLatest: true });
|
|
2251
|
+
// Clear inputs
|
|
2252
|
+
this.elements.inputNewPasswordEl.value = '';
|
|
2253
|
+
this.elements.inputConfirmPasswordEl.value = '';
|
|
2254
|
+
return parentWithUserDelegate;
|
|
2255
|
+
}
|
|
2256
|
+
/**
|
|
2257
|
+
* Handles adding a master password to a sovereign identity that was created with passkeys only.
|
|
2258
|
+
* Authorizes via passkey biometric/hardware assertion and cryptographically adds the manage pool.
|
|
2259
|
+
*/
|
|
2260
|
+
async handleAddPasswordToPasskeyIdentity() {
|
|
2261
|
+
const lc = `${this.lc}[${this.handleAddPasswordToPasskeyIdentity.name}]`;
|
|
2262
|
+
this.setChangePasswordStatus("Validating entries...", "info");
|
|
2263
|
+
try {
|
|
2264
|
+
const newPassword = this.elements.inputNewPasswordEl.value;
|
|
2265
|
+
const confirmPassword = this.elements.inputConfirmPasswordEl.value;
|
|
2266
|
+
if (!newPassword) {
|
|
2267
|
+
this.setChangePasswordStatus("Master password is required.", "error");
|
|
2268
|
+
return;
|
|
2269
|
+
}
|
|
2270
|
+
if (newPassword !== confirmPassword) {
|
|
2271
|
+
this.setChangePasswordStatus("Master password and confirmation do not match.", "error");
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
const passkeysMap = this.aggregatedDetails?.passkeys
|
|
2275
|
+
|| this.ibGib?.data?.frameDetails?.passkeys
|
|
2276
|
+
|| this.ibGib?.data?.passkeys
|
|
2277
|
+
|| {};
|
|
2278
|
+
const passkeysList = Array.isArray(passkeysMap)
|
|
2279
|
+
? passkeysMap
|
|
2280
|
+
: Object.values(passkeysMap);
|
|
2281
|
+
const passkeyPool = this.ibGib?.data?.challengePools?.find(p => p.id.startsWith('passkey-manage-'));
|
|
2282
|
+
if (!passkeyPool) {
|
|
2283
|
+
this.setChangePasswordStatus("No active passkey manage pool found on this identity.", "error");
|
|
2284
|
+
return;
|
|
2285
|
+
}
|
|
2286
|
+
let passkeySecret;
|
|
2287
|
+
let signingPoolId;
|
|
2288
|
+
try {
|
|
2289
|
+
const authResult = await promptForAuthoritySecret({
|
|
2290
|
+
keystone: this.ibGib,
|
|
2291
|
+
aggregatedDetails: this.aggregatedDetails,
|
|
2292
|
+
title: 'Authorize Master Password',
|
|
2293
|
+
msg: 'Please touch your fingerprint sensor, Face ID, or security key to authorize adding a Master Password...',
|
|
2294
|
+
verb: KEYSTONE_VERB_MANAGE
|
|
2295
|
+
});
|
|
2296
|
+
passkeySecret = authResult.secret;
|
|
2297
|
+
signingPoolId = authResult.poolId;
|
|
2298
|
+
}
|
|
2299
|
+
catch (err) {
|
|
2300
|
+
this.setChangePasswordStatus("Authorization cancelled.", "error");
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
this.showBusy({
|
|
2304
|
+
isBusy: true,
|
|
2305
|
+
title: 'Adding Master Password...',
|
|
2306
|
+
msg: 'Compiling cryptographic master password pool and evolving identity...',
|
|
2307
|
+
animationEmoji: '🔑'
|
|
2308
|
+
});
|
|
2309
|
+
await delay(50);
|
|
2310
|
+
this.setChangePasswordStatus("Generating sovereign master password challenge pool...", "info");
|
|
2311
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
2312
|
+
if (!metaspace)
|
|
2313
|
+
throw new Error("Global metaspace not available");
|
|
2314
|
+
const space = await metaspace.getLocalUserSpace({ lock: false });
|
|
2315
|
+
if (!space)
|
|
2316
|
+
throw new Error("Local user space not available");
|
|
2317
|
+
const poolConfig = {
|
|
2318
|
+
id: POOL_ID_MANAGE,
|
|
2319
|
+
type: KeystoneChallengeType.hash_reveal_v1,
|
|
2320
|
+
salt: POOL_ID_MANAGE,
|
|
2321
|
+
algo: HashAlgorithm.sha_256,
|
|
2322
|
+
rounds: 1,
|
|
2323
|
+
allowedVerbs: [KEYSTONE_VERB_MANAGE, KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT],
|
|
2324
|
+
behavior: {
|
|
2325
|
+
size: 10,
|
|
2326
|
+
replenish: KeystoneReplenishStrategy.topUp,
|
|
2327
|
+
selectSequentially: 1,
|
|
2328
|
+
selectRandomly: 0,
|
|
2329
|
+
targetBindingCount: 0,
|
|
2330
|
+
}
|
|
2331
|
+
};
|
|
2332
|
+
const newChallenges = await generatePoolChallenges({
|
|
2333
|
+
config: poolConfig,
|
|
2334
|
+
masterSecret: newPassword,
|
|
2335
|
+
});
|
|
2336
|
+
const newManagePool = {
|
|
2337
|
+
id: POOL_ID_MANAGE,
|
|
2338
|
+
config: poolConfig,
|
|
2339
|
+
challenges: newChallenges,
|
|
2340
|
+
};
|
|
2341
|
+
const primaryTjpAddr = (this.ibGib ? getTjpAddr({ ibGib: this.ibGib }) : undefined)
|
|
2342
|
+
|| (this.ibGibAddr ? (() => {
|
|
2343
|
+
const { ib } = getIbAndGib({ ibGibAddr: this.ibGibAddr });
|
|
2344
|
+
const { tjpGib } = getGibInfo({ ibGibAddr: this.ibGibAddr });
|
|
2345
|
+
return tjpGib && ib ? `${ib}^${tjpGib}` : this.ibGibAddr;
|
|
2346
|
+
})() : undefined)
|
|
2347
|
+
|| getIbGibAddr({ ibGib: this.ibGib });
|
|
2348
|
+
const keystoneService = new KeystoneService_V1();
|
|
2349
|
+
const evolvedParent = await keystoneService.sign({
|
|
2350
|
+
latestKeystone: this.ibGib,
|
|
2351
|
+
claim: {
|
|
2352
|
+
target: primaryTjpAddr,
|
|
2353
|
+
verb: KEYSTONE_VERB_MANAGE
|
|
2354
|
+
},
|
|
2355
|
+
masterSecret: passkeySecret,
|
|
2356
|
+
poolId: signingPoolId,
|
|
2357
|
+
dataToPatch: {
|
|
2358
|
+
challengePools: [...(this.ibGib.data.challengePools || []), newManagePool]
|
|
2359
|
+
},
|
|
2360
|
+
metaspace,
|
|
2361
|
+
space
|
|
2362
|
+
});
|
|
2363
|
+
// Sync pool addition to server
|
|
2364
|
+
this.showBusy({
|
|
2365
|
+
isBusy: true,
|
|
2366
|
+
title: 'Syncing to Server...',
|
|
2367
|
+
msg: 'Publishing updated identity to server...',
|
|
2368
|
+
animationEmoji: '⚡'
|
|
2369
|
+
});
|
|
2370
|
+
const resSync = await getApiBridge().putEvolveKeystone({
|
|
2371
|
+
domainAddr: primaryTjpAddr,
|
|
2372
|
+
keystoneIbGib: evolvedParent,
|
|
2373
|
+
relatedKeystoneIbGibs: []
|
|
2374
|
+
});
|
|
2375
|
+
if (!resSync.success) {
|
|
2376
|
+
throw new Error(`Server sync failed: ${resSync.message}`);
|
|
2377
|
+
}
|
|
2378
|
+
// Save locally
|
|
2379
|
+
await metaspace.put({ ibGibs: [evolvedParent], space });
|
|
2380
|
+
await metaspace.registerNewIbGib({ ibGib: evolvedParent, space });
|
|
2381
|
+
// Delegate registration & final sync via DRY helper
|
|
2382
|
+
await this.registerAndSyncSovereignPasswordDelegate({
|
|
2383
|
+
evolvedParent,
|
|
2384
|
+
newPassword,
|
|
2385
|
+
primaryTjpAddr,
|
|
2386
|
+
metaspace,
|
|
2387
|
+
space
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
catch (error) {
|
|
2391
|
+
console.error(`${lc} Failed to add password to passkey identity: ${extractErrorMsg(error)}`, error);
|
|
2392
|
+
this.setChangePasswordStatus(`Failed to add master password: ${extractErrorMsg(error)}`, "error");
|
|
2393
|
+
}
|
|
2394
|
+
finally {
|
|
2395
|
+
this.showBusy({ isBusy: false });
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2290
2398
|
async handleChangePassword() {
|
|
2291
2399
|
const lc = `${this.lc}[${this.handleChangePassword.name}]`;
|
|
2292
2400
|
const caps = !!this.ibGib ?
|
|
2293
2401
|
getKeystoneCapabilities({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails }) :
|
|
2294
2402
|
undefined;
|
|
2295
|
-
const
|
|
2296
|
-
if (
|
|
2297
|
-
|
|
2403
|
+
const hasPassword = !!caps?.hasPassword;
|
|
2404
|
+
if (!hasPassword) {
|
|
2405
|
+
const isCustodialOnly = !!caps ? caps.isCustodial && !caps.isSovereign : false;
|
|
2406
|
+
if (isCustodialOnly) {
|
|
2407
|
+
await this.handleAddMasterPassword();
|
|
2408
|
+
}
|
|
2409
|
+
else {
|
|
2410
|
+
await this.handleAddPasswordToPasskeyIdentity();
|
|
2411
|
+
}
|
|
2298
2412
|
return;
|
|
2299
2413
|
}
|
|
2300
2414
|
this.setChangePasswordStatus("Validating entries...", "info");
|