@ibgib/web-gib 0.0.56 → 0.0.58
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/AUTO-GENERATED-version.d.mts +1 -1
- package/dist/AUTO-GENERATED-version.mjs +1 -1
- package/dist/api/api-index.mjs +1 -1
- package/dist/api/api-index.mjs.map +1 -1
- package/dist/api/ibgib-api-bridge.d.mts +97 -0
- package/dist/api/ibgib-api-bridge.d.mts.map +1 -1
- package/dist/api/ibgib-api-bridge.mjs +205 -3
- package/dist/api/ibgib-api-bridge.mjs.map +1 -1
- package/dist/identity/custodian-delegate-helper.d.mts +22 -0
- package/dist/identity/custodian-delegate-helper.d.mts.map +1 -0
- package/dist/identity/custodian-delegate-helper.mjs +50 -0
- package/dist/identity/custodian-delegate-helper.mjs.map +1 -0
- package/dist/identity/sso-popup-helper.d.mts +33 -0
- package/dist/identity/sso-popup-helper.d.mts.map +1 -0
- package/dist/identity/sso-popup-helper.mjs +124 -0
- package/dist/identity/sso-popup-helper.mjs.map +1 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -1
- package/dist/ui/component/identity/add-device/add-device.d.mts.map +1 -1
- package/dist/ui/component/identity/add-device/add-device.html +6 -7
- package/dist/ui/component/identity/add-device/add-device.mjs +13 -10
- package/dist/ui/component/identity/add-device/add-device.mjs.map +1 -1
- package/dist/ui/component/identity/identity-header/identity-header.d.mts.map +1 -1
- package/dist/ui/component/identity/identity-header/identity-header.mjs +2 -1
- package/dist/ui/component/identity/identity-header/identity-header.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-creator/keystone-creator.css +114 -9
- package/dist/ui/component/identity/keystone-creator/keystone-creator.d.mts +23 -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 +122 -50
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs +529 -83
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs +19 -61
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs.map +1 -1
- package/package.json +4 -4
- package/src/AUTO-GENERATED-version.mts +1 -1
- package/src/api/api-index.mts +1 -1
- package/src/api/ibgib-api-bridge.mts +288 -4
- package/src/identity/custodian-delegate-helper.mts +61 -0
- package/src/identity/sso/{sso-custodian-service.mts → sso-custodian-service.mts.bak} +65 -38
- package/src/identity/sso/{sso-custodian-service.respec.mts → sso-custodian-service.respec.mts.bak} +15 -14
- package/src/identity/sso-popup-helper.mts +155 -0
- package/src/index.mts +3 -0
- package/src/ui/component/identity/add-device/add-device.html +6 -7
- package/src/ui/component/identity/add-device/add-device.mts +17 -16
- package/src/ui/component/identity/identity-header/identity-header.mts +2 -1
- package/src/ui/component/identity/keystone-creator/keystone-creator.css +114 -9
- package/src/ui/component/identity/keystone-creator/keystone-creator.html +122 -50
- package/src/ui/component/identity/keystone-creator/keystone-creator.mts +599 -100
- package/src/ui/component/identity/keystone-details/keystone-details.mts +26 -77
- package/dist/identity/sso/sso-config-helper.d.mts +0 -7
- package/dist/identity/sso/sso-config-helper.d.mts.map +0 -1
- package/dist/identity/sso/sso-config-helper.mjs +0 -42
- package/dist/identity/sso/sso-config-helper.mjs.map +0 -1
- package/dist/identity/sso/sso-custodian-service.d.mts +0 -139
- package/dist/identity/sso/sso-custodian-service.d.mts.map +0 -1
- package/dist/identity/sso/sso-custodian-service.mjs +0 -443
- package/dist/identity/sso/sso-custodian-service.mjs.map +0 -1
- package/dist/identity/sso/sso-custodian-service.respec.d.mts +0 -2
- package/dist/identity/sso/sso-custodian-service.respec.d.mts.map +0 -1
- package/dist/identity/sso/sso-custodian-service.respec.mjs +0 -254
- package/dist/identity/sso/sso-custodian-service.respec.mjs.map +0 -1
- /package/src/identity/sso/{sso-config-helper.mts → sso-config-helper.mts.bak} +0 -0
|
@@ -2,39 +2,32 @@ import thisCss from "./keystone-creator.css";
|
|
|
2
2
|
import thisHtml from "./keystone-creator.html";
|
|
3
3
|
import commonComponentStylesCss from "../../common-component-styles.css";
|
|
4
4
|
|
|
5
|
-
import { delay, extractErrorMsg,
|
|
6
|
-
import { HashAlgorithm } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
5
|
+
import { delay, extractErrorMsg, } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
7
6
|
import { IbGibAddr } from "@ibgib/ts-gib/dist/types.mjs";
|
|
8
7
|
import { IbGib_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs";
|
|
9
8
|
import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
|
|
9
|
+
import { getGibInfo } from "@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs";
|
|
10
10
|
import { KeystoneService_V1 } from "@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs";
|
|
11
|
-
import {
|
|
12
|
-
KeystonePoolConfig, KeystoneChallengeType, KeystoneReplenishStrategy
|
|
13
|
-
} from "@ibgib/core-gib/dist/keystone/keystone-types.mjs";
|
|
14
11
|
import { KeystoneProfileBuilder } from "@ibgib/core-gib/dist/keystone/policy/keystone-profile-builder.mjs";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
} from "
|
|
12
|
+
import { updateSpecialIndex, } from "@ibgib/core-gib/dist/timeline/timeline-api.mjs";
|
|
13
|
+
import { toDto, getTjpAddr } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
|
|
14
|
+
import { FlatIbGibGraph } from "@ibgib/core-gib/dist/common/other/graph-types.mjs";
|
|
15
|
+
import { validateEmail } from "@ibgib/core-gib/dist/keystone/keystone-helpers.mjs";
|
|
16
|
+
import { KEYSTONE_USERNAME_REGEXP, KEYSTONE_DESCRIPTION_REGEXP, KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT } from "@ibgib/core-gib/dist/keystone/keystone-constants.mjs";
|
|
17
|
+
import { deriveDelegateSecret } from "@ibgib/core-gib/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mjs";
|
|
18
|
+
|
|
19
|
+
import { getGlobalMetaspace_waitIfNeeded, } from "../../../../helpers.mjs";
|
|
20
|
+
import { IbGibDynamicComponentMetaBase, IbGibFormInstanceBase, } from "../../ibgib-dynamic-component-bases.mjs";
|
|
19
21
|
import {
|
|
20
22
|
ElementsBase, IbGibDynamicComponentInstance,
|
|
21
23
|
IbGibDynamicComponentInstanceInitOpts,
|
|
22
24
|
} from "../../component-types.mjs";
|
|
23
|
-
|
|
24
25
|
import { getComponentCtorArg } from "../../../../app-bootstrap/init-orchestration.mjs";
|
|
25
26
|
import { getApiBridge } from "../../../../api/ibgib-api-bridge.mjs";
|
|
26
|
-
|
|
27
|
-
import {
|
|
28
|
-
createSettings, getSectionName, getSettingsScope
|
|
29
|
-
} from "../../../../common/settings/settings-helpers.mjs";
|
|
30
|
-
import { SettingsType } from "../../../../common/settings/settings-constants.mjs";
|
|
31
|
-
import { SettingsIbGib_V1 } from "../../../../common/settings/settings-types.mjs";
|
|
32
|
-
import { updateSpecialIndex, mut8Timeline } from "@ibgib/core-gib/dist/timeline/timeline-api.mjs";
|
|
33
|
-
import { toDto } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
|
|
34
|
-
import { validateEmail } from "@ibgib/core-gib/dist/keystone/keystone-helpers.mjs";
|
|
35
|
-
import { KEYSTONE_USERNAME_REGEXP, KEYSTONE_DESCRIPTION_REGEXP, KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT } from "@ibgib/core-gib/dist/keystone/keystone-constants.mjs";
|
|
27
|
+
import { deriveLocalCustodianDelegateSecret } from "../../../../identity/custodian-delegate-helper.mjs";
|
|
28
|
+
import { executeSsoOAuthPopup } from "../../../../identity/sso-popup-helper.mjs";
|
|
36
29
|
import { EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, EVENT_IBGIB_UI_MESSAGE } from "../../../ui-constants.mjs";
|
|
37
|
-
import {
|
|
30
|
+
import { alertUser, shadowRoot_getElementById } from "../../../../helpers.web.mjs";
|
|
38
31
|
|
|
39
32
|
export const KEYSTONE_CREATOR_COMPONENT_NAME = 'ibgib-keystone-creator';
|
|
40
33
|
|
|
@@ -174,6 +167,59 @@ export class KeystoneCreatorComponentInstance
|
|
|
174
167
|
}
|
|
175
168
|
|
|
176
169
|
private initHandlers() {
|
|
170
|
+
// Mode Tab Switching (Quick vs Password-based)
|
|
171
|
+
const tabQuick = this.shadowRoot?.getElementById('tab-quick') as HTMLButtonElement | null;
|
|
172
|
+
const tabPassword = this.shadowRoot?.getElementById('tab-password') as HTMLButtonElement | null;
|
|
173
|
+
const panelQuick = this.shadowRoot?.getElementById('panel-quick') as HTMLDivElement | null;
|
|
174
|
+
const panelPassword = this.shadowRoot?.getElementById('panel-password') as HTMLDivElement | null;
|
|
175
|
+
const subtitleQuick = this.shadowRoot?.getElementById('subtitle-quick') as HTMLParagraphElement | null;
|
|
176
|
+
const subtitlePassword = this.shadowRoot?.getElementById('subtitle-password') as HTMLParagraphElement | null;
|
|
177
|
+
|
|
178
|
+
tabQuick?.addEventListener('click', () => {
|
|
179
|
+
tabQuick.classList.add('active');
|
|
180
|
+
tabPassword?.classList.remove('active');
|
|
181
|
+
panelQuick?.classList.remove('hidden');
|
|
182
|
+
panelPassword?.classList.add('hidden');
|
|
183
|
+
subtitleQuick?.classList.remove('hidden');
|
|
184
|
+
subtitlePassword?.classList.add('hidden');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
tabPassword?.addEventListener('click', () => {
|
|
188
|
+
tabPassword.classList.add('active');
|
|
189
|
+
tabQuick?.classList.remove('active');
|
|
190
|
+
panelPassword?.classList.remove('hidden');
|
|
191
|
+
panelQuick?.classList.add('hidden');
|
|
192
|
+
subtitlePassword?.classList.remove('hidden');
|
|
193
|
+
subtitleQuick?.classList.add('hidden');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const btnGoogleSso = this.shadowRoot?.getElementById('btn-google-sso') as HTMLButtonElement | null;
|
|
197
|
+
btnGoogleSso?.addEventListener('click', () => this.handleSsoClick('google'));
|
|
198
|
+
|
|
199
|
+
const btnGithubSso = this.shadowRoot?.getElementById('btn-github-sso') as HTMLButtonElement | null;
|
|
200
|
+
btnGithubSso?.addEventListener('click', () => this.handleSsoClick('github'));
|
|
201
|
+
|
|
202
|
+
// Email Sign-In Info Button
|
|
203
|
+
const btnEmailInfo = this.shadowRoot?.getElementById('btn-email-info') as HTMLButtonElement | null;
|
|
204
|
+
btnEmailInfo?.addEventListener('click', (e) => {
|
|
205
|
+
e.preventDefault();
|
|
206
|
+
alertUser({
|
|
207
|
+
title: 'Custom Email Sign-In',
|
|
208
|
+
msg: 'When you enter your email address, we dispatch a 6-digit one-time verification code to your inbox.\n\nEntering the code creates your cryptographic identity delegate on your device and signs you in seamlessly without needing a password!'
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Quick Email Verification Code Handlers
|
|
213
|
+
const btnQuickSendCode = this.shadowRoot?.getElementById('btn-quick-send-code') as HTMLButtonElement | null;
|
|
214
|
+
const inputQuickEmail = this.shadowRoot?.getElementById('input-quick-email') as HTMLInputElement | null;
|
|
215
|
+
const secQuickOtp = this.shadowRoot?.getElementById('section-quick-otp') as HTMLDivElement | null;
|
|
216
|
+
const inputQuickOtp = this.shadowRoot?.getElementById('input-quick-otp') as HTMLInputElement | null;
|
|
217
|
+
const btnQuickVerifyCode = this.shadowRoot?.getElementById('btn-quick-verify-code') as HTMLButtonElement | null;
|
|
218
|
+
|
|
219
|
+
btnQuickSendCode?.addEventListener('click', () => this.handleQuickSendCode());
|
|
220
|
+
|
|
221
|
+
btnQuickVerifyCode?.addEventListener('click', () => this.handleQuickVerifyCode());
|
|
222
|
+
|
|
177
223
|
this.elements!.btnGenerateEl.addEventListener('click', () => this.handleGenerate());
|
|
178
224
|
|
|
179
225
|
this.shadowRoot!.getElementById('btn-switch-sign-in')?.addEventListener('click', (e) => {
|
|
@@ -186,16 +232,16 @@ export class KeystoneCreatorComponentInstance
|
|
|
186
232
|
});
|
|
187
233
|
|
|
188
234
|
// Clear invalid styling when user inputs text
|
|
189
|
-
this.elements!.inputUsername
|
|
235
|
+
this.elements!.inputUsername?.addEventListener('input', () => {
|
|
190
236
|
this.elements!.inputUsername.classList.remove('invalid');
|
|
191
237
|
});
|
|
192
|
-
this.elements!.inputDescription
|
|
238
|
+
this.elements!.inputDescription?.addEventListener('input', () => {
|
|
193
239
|
this.elements!.inputDescription.classList.remove('invalid');
|
|
194
240
|
});
|
|
195
|
-
this.elements!.inputEmail
|
|
241
|
+
this.elements!.inputEmail?.addEventListener('input', () => {
|
|
196
242
|
this.elements!.inputEmail.classList.remove('invalid');
|
|
197
243
|
});
|
|
198
|
-
this.elements!.inputSecret
|
|
244
|
+
this.elements!.inputSecret?.addEventListener('input', () => {
|
|
199
245
|
this.elements!.inputSecret.classList.remove('invalid');
|
|
200
246
|
});
|
|
201
247
|
}
|
|
@@ -208,6 +254,245 @@ export class KeystoneCreatorComponentInstance
|
|
|
208
254
|
}
|
|
209
255
|
}
|
|
210
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Triggered by Google / GitHub SSO buttons.
|
|
259
|
+
* Opens OAuth popup, exchanges authorization code, builds local delegate keystone,
|
|
260
|
+
* registers delegate on server, and finalizes onboarding.
|
|
261
|
+
*/
|
|
262
|
+
protected async handleSsoClick(providerId: 'google' | 'github'): Promise<void> {
|
|
263
|
+
const lc = `${this.lc}[${this.handleSsoClick.name}]`;
|
|
264
|
+
this.setStatus(`Opening ${providerId.toUpperCase()} SSO authorization window...`, 'info');
|
|
265
|
+
const resPopup = await executeSsoOAuthPopup({ providerId });
|
|
266
|
+
if (!resPopup.success || !resPopup.code) {
|
|
267
|
+
this.setStatus(resPopup.message || `${providerId.toUpperCase()} sign-in cancelled.`, 'error');
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
this.setStatus(`Exchanging ${providerId.toUpperCase()} authorization code...`, 'info');
|
|
272
|
+
const apiBridge = getApiBridge();
|
|
273
|
+
const resLogin = await apiBridge.postSsoLogin({
|
|
274
|
+
code: resPopup.code,
|
|
275
|
+
providerId,
|
|
276
|
+
redirectUri: resPopup.redirectUri!
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
if (!resLogin.success || !resLogin.primaryTjpAddr || !resLogin.custodianReqs) {
|
|
280
|
+
this.setStatus(resLogin.message || `${providerId.toUpperCase()} login failed.`, 'error');
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const { primaryTjpAddr, custodianReqs } = resLogin;
|
|
285
|
+
this.setStatus('Deriving local device delegate keys...', 'info');
|
|
286
|
+
const delegateSecret = await deriveLocalCustodianDelegateSecret({ primaryTjpAddr });
|
|
287
|
+
const email = resLogin.userInfo?.email || '';
|
|
288
|
+
const username = email ? email.split('@')[0] : 'user';
|
|
289
|
+
|
|
290
|
+
const delegateBuilder = KeystoneProfileBuilder.buildKeystone('sync')
|
|
291
|
+
.withUsername(`${username}-sync`)
|
|
292
|
+
.withDescription(`Sync Delegate for ${username}`)
|
|
293
|
+
.withDetails({
|
|
294
|
+
client: 'space-gib-web',
|
|
295
|
+
role: 'sync-delegate',
|
|
296
|
+
primaryTjpAddr,
|
|
297
|
+
custodianReqs,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
const configs = await delegateBuilder.compileConfigs();
|
|
301
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
302
|
+
const space = await metaspace.getLocalUserSpace({ lock: false });
|
|
303
|
+
if (!space) {
|
|
304
|
+
this.setStatus('Could not access local user space.', 'error');
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const keystoneService = new KeystoneService_V1();
|
|
309
|
+
const delegateKeystone = await keystoneService.genesis({
|
|
310
|
+
masterSecret: delegateSecret,
|
|
311
|
+
configs,
|
|
312
|
+
metaspace,
|
|
313
|
+
space,
|
|
314
|
+
frameDetails: delegateBuilder.getFrameDetails(),
|
|
315
|
+
isPrimary: false
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
this.setStatus('Registering device delegate with custodian server...', 'info');
|
|
319
|
+
const resRegister = await apiBridge.postCustodianRegisterDelegate({ delegateKeystone });
|
|
320
|
+
|
|
321
|
+
if (!resRegister.success) {
|
|
322
|
+
this.setStatus(resRegister.message || 'Custodian delegate registration failed.', 'error');
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Finalize onboarding: update keystones index, activate identity, and navigate to identity manager
|
|
327
|
+
await this.finalizeIdentityOnboarding({
|
|
328
|
+
primaryKeystone: resRegister.evolvedPrimaryKeystone || resLogin.primaryKeystone,
|
|
329
|
+
primaryGraph: resRegister.primaryGraph || resLogin.primaryGraph,
|
|
330
|
+
delegateKeystone,
|
|
331
|
+
primaryTjpAddr,
|
|
332
|
+
delegateSecret,
|
|
333
|
+
makeActive: true
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Triggered by the "Send 6-Digit Verification Code" button.
|
|
339
|
+
* Validates quick email input and dispatches a 6-digit OTP code to the user's inbox.
|
|
340
|
+
*/
|
|
341
|
+
protected async handleQuickSendCode(): Promise<void> {
|
|
342
|
+
const lc = `${this.lc}[${this.handleQuickSendCode.name}]`;
|
|
343
|
+
const btnQuickSendCode = this.shadowRoot?.getElementById('btn-quick-send-code') as HTMLButtonElement | null;
|
|
344
|
+
const inputQuickEmail = this.shadowRoot?.getElementById('input-quick-email') as HTMLInputElement | null;
|
|
345
|
+
const secQuickOtp = this.shadowRoot?.getElementById('section-quick-otp') as HTMLDivElement | null;
|
|
346
|
+
|
|
347
|
+
const email = (inputQuickEmail?.value || '').trim();
|
|
348
|
+
if (!email || !validateEmail(email)) {
|
|
349
|
+
this.setStatus("Please enter a valid email address.", "error");
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (btnQuickSendCode) {
|
|
354
|
+
btnQuickSendCode.disabled = true;
|
|
355
|
+
btnQuickSendCode.textContent = "Sending Verification Code...";
|
|
356
|
+
}
|
|
357
|
+
this.setStatus("Requesting verification code...", "info");
|
|
358
|
+
|
|
359
|
+
try {
|
|
360
|
+
const resCode = await getApiBridge().postVerificationCode({ email });
|
|
361
|
+
if (resCode.success) {
|
|
362
|
+
secQuickOtp?.classList.remove('hidden');
|
|
363
|
+
this.setStatus("✉️ 6-digit verification code sent to your email!", "success");
|
|
364
|
+
} else {
|
|
365
|
+
this.setStatus(resCode.message || "Failed to dispatch verification code.", "error");
|
|
366
|
+
}
|
|
367
|
+
} catch (error) {
|
|
368
|
+
const emsg = extractErrorMsg(error);
|
|
369
|
+
console.error(`${lc} ${emsg}`);
|
|
370
|
+
this.setStatus(`Failed to dispatch verification code: ${emsg}`, "error");
|
|
371
|
+
} finally {
|
|
372
|
+
if (btnQuickSendCode) {
|
|
373
|
+
btnQuickSendCode.disabled = false;
|
|
374
|
+
btnQuickSendCode.textContent = "Send 6-Digit Verification Code";
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Triggered by the "Verify Code" button.
|
|
381
|
+
* Submits 6-digit OTP code to custodian server, builds local sync delegate keystone,
|
|
382
|
+
* registers delegate on server, and finalizes onboarding.
|
|
383
|
+
*/
|
|
384
|
+
protected async handleQuickVerifyCode(): Promise<void> {
|
|
385
|
+
const lc = `${this.lc}[${this.handleQuickVerifyCode.name}]`;
|
|
386
|
+
const inputQuickEmail = this.shadowRoot?.getElementById('input-quick-email') as HTMLInputElement | null;
|
|
387
|
+
const inputQuickOtp = this.shadowRoot?.getElementById('input-quick-otp') as HTMLInputElement | null;
|
|
388
|
+
const btnQuickVerifyCode = this.shadowRoot?.getElementById('btn-quick-verify-code') as HTMLButtonElement | null;
|
|
389
|
+
|
|
390
|
+
const email = (inputQuickEmail?.value || '').trim();
|
|
391
|
+
const code = (inputQuickOtp?.value || '').trim();
|
|
392
|
+
if (!code || code.length !== 6 || !/^\d{6}$/.test(code)) {
|
|
393
|
+
this.setStatus("Please enter a valid 6-digit verification code.", "error");
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (btnQuickVerifyCode) {
|
|
398
|
+
btnQuickVerifyCode.disabled = true;
|
|
399
|
+
btnQuickVerifyCode.textContent = "Verifying...";
|
|
400
|
+
}
|
|
401
|
+
this.setStatus("Verifying code & completing onboarding...", "info");
|
|
402
|
+
this.showBusy({
|
|
403
|
+
isBusy: true,
|
|
404
|
+
title: 'Verifying Code...',
|
|
405
|
+
msg: 'Great! Juust a moment...\n(Validating code, promoting server domain space, other security stuff...)',
|
|
406
|
+
animationEmoji: '🔐'
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
try {
|
|
410
|
+
const resGenesis = await getApiBridge().postCustodianGenesis({ email, code });
|
|
411
|
+
if (!resGenesis.success || !resGenesis.primaryTjpAddr || !resGenesis.custodianReqs) {
|
|
412
|
+
this.setStatus(resGenesis.message || "Custodian verification failed.", "error");
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const primaryTjpAddr = resGenesis.primaryTjpAddr;
|
|
417
|
+
const custodianReqs = resGenesis.custodianReqs;
|
|
418
|
+
|
|
419
|
+
// Construct local delegate keystone
|
|
420
|
+
this.setStatus("Constructing local sync delegate...", "info");
|
|
421
|
+
this.showBusy({
|
|
422
|
+
isBusy: true,
|
|
423
|
+
title: 'Creating Sync Delegate...',
|
|
424
|
+
msg: 'Generating cryptographic sync delegate keys...',
|
|
425
|
+
animationEmoji: '🔑'
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
const delegateSecret = await deriveLocalCustodianDelegateSecret({ primaryTjpAddr });
|
|
429
|
+
const username = email.split('@')[0];
|
|
430
|
+
const delegateBuilder = KeystoneProfileBuilder.buildKeystone('sync')
|
|
431
|
+
.withUsername(`${username}-sync`)
|
|
432
|
+
.withDescription(`Sync Delegate for ${username}`)
|
|
433
|
+
.withDetails({
|
|
434
|
+
client: 'space-gib-web',
|
|
435
|
+
role: 'sync-delegate',
|
|
436
|
+
primaryTjpAddr,
|
|
437
|
+
custodianReqs,
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const configs = await delegateBuilder.compileConfigs();
|
|
441
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
442
|
+
const space = await metaspace.getLocalUserSpace({ lock: false });
|
|
443
|
+
if (!space) {
|
|
444
|
+
this.setStatus("Failed to access local user space.", "error");
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const keystoneService = new KeystoneService_V1();
|
|
449
|
+
const delegateKeystone = await keystoneService.genesis({
|
|
450
|
+
masterSecret: delegateSecret,
|
|
451
|
+
configs,
|
|
452
|
+
metaspace,
|
|
453
|
+
space,
|
|
454
|
+
frameDetails: delegateBuilder.getFrameDetails(),
|
|
455
|
+
isPrimary: false
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// Register delegate with server
|
|
459
|
+
this.setStatus("Registering device delegate with custodian server...", "info");
|
|
460
|
+
this.showBusy({
|
|
461
|
+
isBusy: true,
|
|
462
|
+
title: 'Registering Delegate...',
|
|
463
|
+
msg: 'Registering local delegate with custodian server...',
|
|
464
|
+
animationEmoji: '📝'
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
const resRegister = await getApiBridge().postCustodianRegisterDelegate({ delegateKeystone });
|
|
468
|
+
if (!resRegister.success) {
|
|
469
|
+
this.setStatus(resRegister.message || "Failed to register delegate with custodian server.", "error");
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Finalize onboarding: update keystones index, activate identity, and navigate to identity manager
|
|
474
|
+
await this.finalizeIdentityOnboarding({
|
|
475
|
+
primaryKeystone: resRegister.evolvedPrimaryKeystone || resGenesis.primaryKeystone,
|
|
476
|
+
primaryGraph: resRegister.primaryGraph || resGenesis.primaryGraph,
|
|
477
|
+
delegateKeystone,
|
|
478
|
+
primaryTjpAddr,
|
|
479
|
+
delegateSecret,
|
|
480
|
+
makeActive: true
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
} catch (error) {
|
|
484
|
+
const emsg = extractErrorMsg(error);
|
|
485
|
+
console.error(`${lc} ${emsg}`, error);
|
|
486
|
+
this.setStatus(`Error: ${emsg}`, "error");
|
|
487
|
+
} finally {
|
|
488
|
+
this.showBusy({ isBusy: false });
|
|
489
|
+
if (btnQuickVerifyCode) {
|
|
490
|
+
btnQuickVerifyCode.disabled = false;
|
|
491
|
+
btnQuickVerifyCode.textContent = "Verify Code";
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
211
496
|
/**
|
|
212
497
|
* Triggered by the "Generate" button.
|
|
213
498
|
* Orchestrates: Secret -> Local Genesis -> Graph Collection -> Server Sync.
|
|
@@ -293,7 +578,12 @@ export class KeystoneCreatorComponentInstance
|
|
|
293
578
|
});
|
|
294
579
|
|
|
295
580
|
// 1. Prepare Primary/Domain Keystone Configs
|
|
296
|
-
const
|
|
581
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
582
|
+
const requestedProfile = (process.env.KEYSTONE_PROFILE || 'domain').toLowerCase();
|
|
583
|
+
const profileName = (isProd && requestedProfile === 'test') ? 'domain' : requestedProfile;
|
|
584
|
+
console.log(`${lc} Using keystone policy profile '${profileName}' (I: 90123456789abcdef0123456789abcde)`);
|
|
585
|
+
|
|
586
|
+
const builder = KeystoneProfileBuilder.buildKeystone(profileName as any)
|
|
297
587
|
.withUsername(username)
|
|
298
588
|
.withDescription(description);
|
|
299
589
|
if (email) {
|
|
@@ -328,11 +618,11 @@ export class KeystoneCreatorComponentInstance
|
|
|
328
618
|
isPrimary: true
|
|
329
619
|
});
|
|
330
620
|
|
|
331
|
-
const
|
|
332
|
-
console.log(`${lc} local genesis complete: ${
|
|
621
|
+
const primaryTjpAddr = getTjpAddr({ ibGib: keystoneIbGib, defaultIfNone: 'incomingAddr' })!;
|
|
622
|
+
console.log(`${lc} local genesis complete: ${primaryTjpAddr}`);
|
|
333
623
|
await delay(humanEyeDelay); // for human eyes, not needed for functionality
|
|
334
624
|
this.setStatus("Local genesis complete. Creating sync delegate...", "info");
|
|
335
|
-
console.log(`✓ Local genesis complete: ${
|
|
625
|
+
console.log(`✓ Local genesis complete: ${primaryTjpAddr}`);
|
|
336
626
|
|
|
337
627
|
// Automatically generate Sync Delegate Keystone
|
|
338
628
|
this.showBusy({
|
|
@@ -343,12 +633,13 @@ export class KeystoneCreatorComponentInstance
|
|
|
343
633
|
});
|
|
344
634
|
|
|
345
635
|
const delegateSecret = await deriveDelegateSecret({ masterSecret });
|
|
346
|
-
const delegateBuilder = KeystoneProfileBuilder.buildKeystone(
|
|
636
|
+
const delegateBuilder = KeystoneProfileBuilder.buildKeystone(profileName as any)
|
|
347
637
|
.withUsername(`${username}-sync`)
|
|
348
638
|
.withDescription(`Sync Delegate for ${username}`)
|
|
349
639
|
.withDetails({
|
|
350
640
|
client: 'space-gib-web',
|
|
351
|
-
role: 'sync-delegate'
|
|
641
|
+
role: 'sync-delegate',
|
|
642
|
+
primaryTjpAddr: primaryTjpAddr,
|
|
352
643
|
});
|
|
353
644
|
const delegateConfigs = await delegateBuilder.compileConfigs();
|
|
354
645
|
|
|
@@ -385,15 +676,11 @@ export class KeystoneCreatorComponentInstance
|
|
|
385
676
|
await delay(humanEyeDelay);
|
|
386
677
|
|
|
387
678
|
// 4. Post to Server
|
|
388
|
-
|
|
389
|
-
this.setStatus("Syncing genesis to server...", "info");
|
|
679
|
+
this.setStatus("Syncing genesis keystone to server...", "info");
|
|
390
680
|
this.showBusy({
|
|
391
681
|
isBusy: true,
|
|
392
682
|
title: 'Syncing to Server...',
|
|
393
|
-
msg:
|
|
394
|
-
'Posting genesis keystone to server sync node...',
|
|
395
|
-
'(may take awhile, depending on strength of keystone...)',
|
|
396
|
-
].join('\n'),
|
|
683
|
+
msg: 'Staging pending genesis keystone on server...',
|
|
397
684
|
animationEmoji: '🔄'
|
|
398
685
|
});
|
|
399
686
|
const resSync = await getApiBridge().postGenesisKeystone(keystoneIbGib);
|
|
@@ -406,74 +693,128 @@ export class KeystoneCreatorComponentInstance
|
|
|
406
693
|
return;
|
|
407
694
|
}
|
|
408
695
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
title: 'Syncing Delegate...',
|
|
414
|
-
msg: 'Posting evolved domain identity and sync delegate to server...',
|
|
415
|
-
animationEmoji: '🔄'
|
|
416
|
-
});
|
|
417
|
-
const resEvolve = await getApiBridge().putEvolveKeystone({
|
|
418
|
-
domainAddr: addr,
|
|
419
|
-
keystoneIbGib: evolvedParentKeystoneIbGib,
|
|
420
|
-
relatedKeystoneIbGibs: [delegateKeystoneIbGib]
|
|
421
|
-
});
|
|
422
|
-
if (!resEvolve.success) {
|
|
423
|
-
const msg = `Delegate registration sync failed: ${resEvolve.message}`;
|
|
424
|
-
console.error(`${lc} ${msg}`);
|
|
425
|
-
this.setStatus(msg, "error");
|
|
696
|
+
const isPending = (resSync as any).pending || (resSync as any).statusCode === 202;
|
|
697
|
+
|
|
698
|
+
if (isPending) {
|
|
699
|
+
console.log(`${lc} Genesis keystone quarantined in pending staging area. Prompting for 6-digit verification code.`);
|
|
426
700
|
this.showBusy({ isBusy: false });
|
|
427
|
-
this.
|
|
428
|
-
|
|
701
|
+
this.setFormDisabled(true);
|
|
702
|
+
|
|
703
|
+
if (!this.shadowRoot) { throw new Error(`(UNEXPECTED) this.shadowRoot falsy? (E: 6d316f5a2bf8e6a0286306468d35c826)`); }
|
|
704
|
+
const secVerify = shadowRoot_getElementById<HTMLDivElement>(this.shadowRoot, 'section-code-verification');
|
|
705
|
+
const inputVerifyCode =
|
|
706
|
+
shadowRoot_getElementById<HTMLInputElement>(this.shadowRoot, 'input-verify-code');
|
|
707
|
+
const btnSubmitCode =
|
|
708
|
+
shadowRoot_getElementById<HTMLButtonElement>(this.shadowRoot, 'btn-submit-code');
|
|
709
|
+
const btnResendCode =
|
|
710
|
+
shadowRoot_getElementById<HTMLButtonElement>(this.shadowRoot, 'btn-resend-code');
|
|
711
|
+
|
|
712
|
+
secVerify.classList.remove('hidden');
|
|
713
|
+
secVerify.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
714
|
+
this.setStatus("✉️ Verification code sent to your email. Enter 6-digit code below:", "info");
|
|
715
|
+
|
|
716
|
+
const onResendCode = async () => {
|
|
717
|
+
btnResendCode.disabled = true;
|
|
718
|
+
btnResendCode.textContent = "Sending...";
|
|
719
|
+
this.setStatus("Resending verification code to your email...", "info");
|
|
720
|
+
const resResend = await getApiBridge().postVerificationCode({ email, primaryTjpAddr: primaryTjpAddr });
|
|
721
|
+
if (resResend.success) {
|
|
722
|
+
this.setStatus("✉️ New verification code sent to your email!", "success");
|
|
723
|
+
} else {
|
|
724
|
+
this.setStatus(resResend.message || "Failed to resend verification code.", "error");
|
|
725
|
+
}
|
|
726
|
+
btnResendCode.disabled = false;
|
|
727
|
+
btnResendCode.textContent = "Resend Code";
|
|
728
|
+
};
|
|
729
|
+
btnResendCode.addEventListener('click', onResendCode);
|
|
730
|
+
|
|
731
|
+
await new Promise<void>((resolve, reject) => {
|
|
732
|
+
const onSubmitCode = async () => {
|
|
733
|
+
const code = inputVerifyCode.value.trim();
|
|
734
|
+
if (!code || code.length !== 6 || !/^\d{6}$/.test(code)) {
|
|
735
|
+
this.setStatus("Please enter a valid 6-digit numeric verification code (e.g. 482901).", "error");
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
btnSubmitCode.disabled = true;
|
|
740
|
+
btnSubmitCode.textContent = "Verifying...";
|
|
741
|
+
this.showBusy({
|
|
742
|
+
isBusy: true,
|
|
743
|
+
title: 'Verifying & Activating...',
|
|
744
|
+
msg: 'Great! Juust a moment...\n(Validating code, promoting server domain space, other security stuff...)',
|
|
745
|
+
animationEmoji: '🔐'
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
const resVerify = await getApiBridge().postVerifyCode({ email, primaryTjpAddr: primaryTjpAddr, code });
|
|
749
|
+
if (!resVerify.success) {
|
|
750
|
+
this.showBusy({ isBusy: false });
|
|
751
|
+
this.setStatus(resVerify.message || "Incorrect verification code.", "error");
|
|
752
|
+
btnSubmitCode.disabled = false;
|
|
753
|
+
btnSubmitCode.textContent = "Verify Code";
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
console.log(`${lc} Code verified & server domain promoted! Syncing delegate...`);
|
|
758
|
+
this.showBusy({
|
|
759
|
+
isBusy: true,
|
|
760
|
+
title: 'Syncing Delegate...',
|
|
761
|
+
msg: 'Posting evolved domain identity and sync delegate to server...',
|
|
762
|
+
animationEmoji: '⚡'
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
btnSubmitCode.removeEventListener('click', onSubmitCode);
|
|
766
|
+
secVerify.classList.add('hidden');
|
|
767
|
+
this.setFormDisabled(false);
|
|
768
|
+
|
|
769
|
+
// Now that server domain space is promoted, post evolved keystone + delegate
|
|
770
|
+
const resEvolve = await getApiBridge().putEvolveKeystone({
|
|
771
|
+
domainAddr: primaryTjpAddr,
|
|
772
|
+
keystoneIbGib: evolvedParentKeystoneIbGib,
|
|
773
|
+
relatedKeystoneIbGibs: [delegateKeystoneIbGib]
|
|
774
|
+
});
|
|
775
|
+
if (!resEvolve.success) {
|
|
776
|
+
console.warn(`${lc} Delegate sync warning after code verification: ${resEvolve.message}`);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
resolve();
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
btnSubmitCode.addEventListener('click', onSubmitCode);
|
|
783
|
+
});
|
|
784
|
+
} else {
|
|
785
|
+
// Post evolved keystone + delegate keystone immediately (no email or pre-verified)
|
|
786
|
+
this.setStatus("Syncing delegate registration to server...", "info");
|
|
787
|
+
this.showBusy({
|
|
788
|
+
isBusy: true,
|
|
789
|
+
title: 'Syncing Delegate...',
|
|
790
|
+
msg: 'Posting evolved domain identity and sync delegate to server...',
|
|
791
|
+
animationEmoji: '🔄'
|
|
792
|
+
});
|
|
793
|
+
const resEvolve = await getApiBridge().putEvolveKeystone({
|
|
794
|
+
domainAddr: primaryTjpAddr,
|
|
795
|
+
keystoneIbGib: evolvedParentKeystoneIbGib,
|
|
796
|
+
relatedKeystoneIbGibs: [delegateKeystoneIbGib]
|
|
797
|
+
});
|
|
798
|
+
if (!resEvolve.success) {
|
|
799
|
+
const msg = `Delegate registration sync failed: ${resEvolve.message}`;
|
|
800
|
+
console.error(`${lc} ${msg}`);
|
|
801
|
+
this.setStatus(msg, "error");
|
|
802
|
+
this.showBusy({ isBusy: false });
|
|
803
|
+
this.setButtonLoading(false);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
429
806
|
}
|
|
430
807
|
|
|
431
|
-
// 5.
|
|
432
|
-
this.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
await updateSpecialIndex({
|
|
440
|
-
type: "keystones",
|
|
441
|
-
rel8nInfos: [
|
|
442
|
-
{
|
|
443
|
-
rel8nName: "keystone",
|
|
444
|
-
ibGibs: [
|
|
445
|
-
toDto({ ibGib: evolvedParentKeystoneIbGib }),
|
|
446
|
-
toDto({ ibGib: delegateKeystoneIbGib })
|
|
447
|
-
],
|
|
448
|
-
},
|
|
449
|
-
],
|
|
450
|
-
metaspace,
|
|
451
|
-
space,
|
|
808
|
+
// 5. Finalize onboarding: update keystones index, activate identity, and navigate to identity manager
|
|
809
|
+
await this.finalizeIdentityOnboarding({
|
|
810
|
+
primaryKeystone: evolvedParentKeystoneIbGib,
|
|
811
|
+
delegateKeystone: delegateKeystoneIbGib,
|
|
812
|
+
primaryTjpAddr: evolvedParentAddr,
|
|
813
|
+
masterSecret,
|
|
814
|
+
delegateSecret,
|
|
815
|
+
makeActive: this.elements!.checkboxMakeActive?.checked ?? true
|
|
452
816
|
});
|
|
453
817
|
|
|
454
|
-
// Expose for dev tools
|
|
455
|
-
(window as any).dev_domainI = evolvedParentKeystoneIbGib;
|
|
456
|
-
(window as any).dev_domainIMasterSecret = masterSecret;
|
|
457
|
-
(window as any).dev_syncDelegate = delegateKeystoneIbGib;
|
|
458
|
-
(window as any).dev_syncDelegateSecret = delegateSecret;
|
|
459
|
-
|
|
460
|
-
// 6. Request to make the new identity active if the checkbox is checked
|
|
461
|
-
const makeActive = this.elements!.checkboxMakeActive?.checked;
|
|
462
|
-
if (makeActive) {
|
|
463
|
-
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, {
|
|
464
|
-
detail: { activeIdentityAddr: evolvedParentAddr },
|
|
465
|
-
bubbles: true,
|
|
466
|
-
composed: true
|
|
467
|
-
}));
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// 7. Success
|
|
471
|
-
this.elements!.keystoneAddrEl.textContent = evolvedParentAddr;
|
|
472
|
-
this.elements!.statusArea.classList.remove('hidden');
|
|
473
|
-
this.setStatus("Keystone successfully created and synced!", "success");
|
|
474
|
-
await delay(humanEyeDelay); // for human eyes, not needed for functionality
|
|
475
|
-
console.log(`✓ Keystone successfully created and synced!`);
|
|
476
|
-
|
|
477
818
|
} catch (error) {
|
|
478
819
|
const emsg = extractErrorMsg(error);
|
|
479
820
|
console.error(`${lc} ${emsg}`, error);
|
|
@@ -487,6 +828,164 @@ export class KeystoneCreatorComponentInstance
|
|
|
487
828
|
}
|
|
488
829
|
}
|
|
489
830
|
|
|
831
|
+
/**
|
|
832
|
+
* DRY helper to register keystones in the local keystones special index,
|
|
833
|
+
* set the active identity, and navigate to the Identity Manager component.
|
|
834
|
+
*/
|
|
835
|
+
private async finalizeIdentityOnboarding({
|
|
836
|
+
primaryKeystone,
|
|
837
|
+
primaryGraph,
|
|
838
|
+
delegateKeystone,
|
|
839
|
+
primaryTjpAddr,
|
|
840
|
+
masterSecret,
|
|
841
|
+
delegateSecret,
|
|
842
|
+
makeActive = true,
|
|
843
|
+
}: {
|
|
844
|
+
primaryKeystone?: IbGib_V1;
|
|
845
|
+
primaryGraph?: FlatIbGibGraph;
|
|
846
|
+
delegateKeystone: IbGib_V1;
|
|
847
|
+
primaryTjpAddr: string;
|
|
848
|
+
masterSecret?: string;
|
|
849
|
+
delegateSecret?: string;
|
|
850
|
+
makeActive?: boolean;
|
|
851
|
+
}): Promise<void> {
|
|
852
|
+
const lc = `${this.lc}[${this.finalizeIdentityOnboarding.name}]`;
|
|
853
|
+
const humanEyeDelay = 500;
|
|
854
|
+
|
|
855
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
856
|
+
if (!metaspace) {
|
|
857
|
+
throw new Error(`(UNEXPECTED) metaspace falsy? Expected active global metaspace instance. (E: 2e37a8b6fe987449c5d8693a7d4a2827)`);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
861
|
+
if (!space) {
|
|
862
|
+
throw new Error(`(UNEXPECTED) space falsy? Expected valid default local user space from metaspace. (E: 1e37a8b6fe987449c5d8693a7d4a2826)`);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// 0. Persist all frames of primary identity graph into local browser space
|
|
866
|
+
if (primaryGraph) {
|
|
867
|
+
const graphIbGibs = Object.values(primaryGraph) as IbGib_V1[];
|
|
868
|
+
for (const frame of graphIbGibs) {
|
|
869
|
+
await metaspace.put({ ibGibs: [frame], space });
|
|
870
|
+
await metaspace.registerNewIbGib({ ibGib: frame, space });
|
|
871
|
+
}
|
|
872
|
+
} else if (primaryKeystone) {
|
|
873
|
+
await metaspace.put({ ibGibs: [primaryKeystone], space });
|
|
874
|
+
await metaspace.registerNewIbGib({ ibGib: primaryKeystone, space });
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// 0.5 Verify primary keystone exists in local browser space via getLatestKeystone
|
|
878
|
+
const keystoneService = new KeystoneService_V1();
|
|
879
|
+
try {
|
|
880
|
+
await keystoneService.getLatestKeystone({
|
|
881
|
+
addr: primaryTjpAddr,
|
|
882
|
+
metaspace,
|
|
883
|
+
space
|
|
884
|
+
});
|
|
885
|
+
} catch (error) {
|
|
886
|
+
const emsg = `Primary identity keystone ${primaryTjpAddr} not found in local browser space! Error: ${extractErrorMsg(error)}`;
|
|
887
|
+
console.error(`${lc} ${emsg} (E: a123456789abcdef0123456789c0001)`);
|
|
888
|
+
throw new Error(emsg);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// 1. Register with the local keystones special index
|
|
892
|
+
this.showBusy({
|
|
893
|
+
isBusy: true,
|
|
894
|
+
title: 'Registering Identity...',
|
|
895
|
+
msg: 'Updating local identity indexes and scope settings...',
|
|
896
|
+
animationEmoji: '💾'
|
|
897
|
+
});
|
|
898
|
+
const ibGibsToRegister: IbGib_V1[] = [toDto({ ibGib: delegateKeystone })];
|
|
899
|
+
|
|
900
|
+
if (!primaryKeystone && !primaryGraph) { throw new Error(`(UNEXPECTED) primaryKeystone and primaryGraph both falsy? We should have gotten this back from the server. (E: e34cd882ac66633c75c53318a3b28826)`); }
|
|
901
|
+
if (!delegateKeystone) { throw new Error(`(UNEXPECTED) delegateKeystone falsy? We should have made this locally and even registered it with the server. (E: 56b1c396269e15f008b7e298c71bc826)`); }
|
|
902
|
+
|
|
903
|
+
if (primaryKeystone) {
|
|
904
|
+
ibGibsToRegister.unshift(toDto({ ibGib: primaryKeystone }));
|
|
905
|
+
}
|
|
906
|
+
await updateSpecialIndex({
|
|
907
|
+
type: "keystones",
|
|
908
|
+
rel8nInfos: [
|
|
909
|
+
{
|
|
910
|
+
rel8nName: "keystone",
|
|
911
|
+
ibGibs: ibGibsToRegister,
|
|
912
|
+
},
|
|
913
|
+
],
|
|
914
|
+
metaspace,
|
|
915
|
+
space,
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
// Expose for dev tools debugging if available
|
|
919
|
+
if (masterSecret) {
|
|
920
|
+
(window as any).dev_domainI = primaryKeystone;
|
|
921
|
+
(window as any).dev_domainIMasterSecret = masterSecret;
|
|
922
|
+
(window as any).dev_syncDelegate = delegateKeystone;
|
|
923
|
+
(window as any).dev_syncDelegateSecret = delegateSecret;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// 2. Request to make the new identity active
|
|
927
|
+
if (makeActive) {
|
|
928
|
+
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, {
|
|
929
|
+
detail: { activeIdentityAddr: primaryTjpAddr },
|
|
930
|
+
bubbles: true,
|
|
931
|
+
composed: true
|
|
932
|
+
}));
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// 3. Display success status
|
|
936
|
+
const successMsg = "Identity successfully created and activated!";
|
|
937
|
+
this.showBusy({
|
|
938
|
+
isBusy: true,
|
|
939
|
+
title: 'Success!',
|
|
940
|
+
msg: successMsg,
|
|
941
|
+
animationEmoji: '✅'
|
|
942
|
+
});
|
|
943
|
+
await delay(humanEyeDelay);
|
|
944
|
+
if (this.elements?.keystoneAddrEl) {
|
|
945
|
+
this.elements.keystoneAddrEl.textContent = primaryTjpAddr;
|
|
946
|
+
}
|
|
947
|
+
if (this.elements?.statusArea) {
|
|
948
|
+
this.elements.statusArea.classList.remove('hidden');
|
|
949
|
+
}
|
|
950
|
+
this.setStatus(successMsg, "success");
|
|
951
|
+
console.log(`${lc} ✓ ${successMsg}`);
|
|
952
|
+
|
|
953
|
+
// 4. Dispatch UI navigation message to close creator and open identity manager
|
|
954
|
+
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_UI_MESSAGE, {
|
|
955
|
+
detail: { action: 'show-identity-manager', addr: primaryTjpAddr },
|
|
956
|
+
bubbles: true,
|
|
957
|
+
composed: true
|
|
958
|
+
}));
|
|
959
|
+
|
|
960
|
+
this.showBusy({ isBusy: false });
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
private setFormDisabled(disabled: boolean): void {
|
|
964
|
+
const root = this.shadowRoot!;
|
|
965
|
+
const inputIds = [
|
|
966
|
+
'input-username',
|
|
967
|
+
'input-description',
|
|
968
|
+
'input-email',
|
|
969
|
+
'input-secret',
|
|
970
|
+
'checkbox-make-active',
|
|
971
|
+
'btn-generate',
|
|
972
|
+
'btn-switch-sign-in',
|
|
973
|
+
];
|
|
974
|
+
for (const id of inputIds) {
|
|
975
|
+
const el = root.getElementById(id) as HTMLInputElement | HTMLButtonElement | null;
|
|
976
|
+
if (el) {
|
|
977
|
+
el.disabled = disabled;
|
|
978
|
+
if (disabled) {
|
|
979
|
+
el.style.opacity = '0.5';
|
|
980
|
+
el.style.pointerEvents = 'none';
|
|
981
|
+
} else {
|
|
982
|
+
el.style.opacity = '1';
|
|
983
|
+
el.style.pointerEvents = 'auto';
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
490
989
|
protected override async renderUI(): Promise<void> {
|
|
491
990
|
// No complex rendering needed for this simple creator component yet
|
|
492
991
|
}
|