@ibgib/web-gib 0.0.60 → 0.0.61
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/helpers.d.mts.map +1 -1
- package/dist/helpers.mjs +1 -0
- package/dist/helpers.mjs.map +1 -1
- package/dist/identity/device-delegate-helper.d.mts +43 -0
- package/dist/identity/device-delegate-helper.d.mts.map +1 -0
- package/dist/identity/device-delegate-helper.mjs +106 -0
- package/dist/identity/device-delegate-helper.mjs.map +1 -0
- package/dist/identity/device-delegate.respec.d.mts +2 -0
- package/dist/identity/device-delegate.respec.d.mts.map +1 -0
- package/dist/identity/device-delegate.respec.mjs +106 -0
- package/dist/identity/device-delegate.respec.mjs.map +1 -0
- package/dist/identity/keystone-sync-service.d.mts +18 -0
- package/dist/identity/keystone-sync-service.d.mts.map +1 -1
- package/dist/identity/keystone-sync-service.mjs +77 -1
- package/dist/identity/keystone-sync-service.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -2
- package/dist/index.mjs.map +1 -1
- package/dist/storage/storage-helpers.web.d.mts +9 -0
- package/dist/storage/storage-helpers.web.d.mts.map +1 -1
- package/dist/storage/storage-helpers.web.mjs +42 -0
- package/dist/storage/storage-helpers.web.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.mjs +30 -43
- package/dist/ui/component/identity/add-device/add-device.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-creator/keystone-creator.d.mts.map +1 -1
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs +41 -57
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts +3 -0
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs +307 -46
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs.map +1 -1
- package/dist/ui/ui-constants.d.mts +1 -0
- package/dist/ui/ui-constants.d.mts.map +1 -1
- package/dist/ui/ui-constants.mjs +1 -0
- package/dist/ui/ui-constants.mjs.map +1 -1
- package/dist/ui/ui-types.d.mts +5 -0
- package/dist/ui/ui-types.d.mts.map +1 -1
- package/package.json +2 -2
- package/src/AUTO-GENERATED-version.mts +1 -1
- package/src/helpers.mts +1 -0
- package/src/identity/custodian-delegate-helper.mts.bak +7 -0
- package/src/identity/device-delegate-helper.mts +135 -0
- package/src/identity/device-delegate.respec.mts +125 -0
- package/src/identity/keystone-sync-service.mts +95 -2
- package/src/index.mts +1 -2
- package/src/storage/storage-helpers.web.mts +47 -0
- package/src/ui/component/identity/add-device/add-device.mts +36 -48
- package/src/ui/component/identity/keystone-creator/keystone-creator.mts +48 -65
- package/src/ui/component/identity/keystone-details/keystone-details.mts +317 -49
- package/src/ui/ui-constants.mts +1 -0
- package/src/ui/ui-types.mts +6 -0
- package/dist/identity/custodian-delegate-helper.d.mts +0 -22
- package/dist/identity/custodian-delegate-helper.d.mts.map +0 -1
- package/dist/identity/custodian-delegate-helper.mjs +0 -67
- package/dist/identity/custodian-delegate-helper.mjs.map +0 -1
- package/src/identity/custodian-delegate-helper.mts +0 -86
|
@@ -15,7 +15,9 @@ import { parseSpaceIb } from "@ibgib/core-gib/dist/witness/space/space-helper.mj
|
|
|
15
15
|
import { executeSsoOAuthPopup } from "../../../../identity/sso-popup-helper.mjs";
|
|
16
16
|
import { getComponentCtorArg } from "../../../../app-bootstrap/init-orchestration.mjs";
|
|
17
17
|
import { promptForSecret } from "../../../../helpers.web.mjs";
|
|
18
|
-
import { isSsoProviderLinked } from "
|
|
18
|
+
import { isSsoProviderLinked, getKeystoneCapabilities, isDelegateExpired } from "@ibgib/core-gib/dist/keystone/delegate/delegate-helpers.mjs";
|
|
19
|
+
import { generatePoolChallenges } from "@ibgib/core-gib/dist/keystone/keystone-helpers.mjs";
|
|
20
|
+
import { POOL_ID_MANAGE } from "@ibgib/core-gib/dist/keystone/keystone-constants.mjs";
|
|
19
21
|
import { getGlobalMetaspace_waitIfNeeded } from "../../../../helpers.mjs";
|
|
20
22
|
const logalot = false;
|
|
21
23
|
export const KEYSTONE_DETAILS_COMPONENT_NAME = 'ibgib-keystone-details';
|
|
@@ -51,6 +53,7 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
51
53
|
isHistoricalFrame = false;
|
|
52
54
|
_onIdentityChanged = null;
|
|
53
55
|
keystoneCache = new Map();
|
|
56
|
+
aggregatedDetails;
|
|
54
57
|
constructor() {
|
|
55
58
|
super();
|
|
56
59
|
}
|
|
@@ -394,21 +397,36 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
394
397
|
this.elements.btnSetActiveEl.disabled = isTimelineActive;
|
|
395
398
|
this.elements.btnLinkGoogleEl.classList.remove('disabled');
|
|
396
399
|
this.elements.btnLinkGithubEl.classList.remove('disabled');
|
|
397
|
-
// Check if
|
|
398
|
-
const
|
|
399
|
-
const
|
|
400
|
+
// Check if origin provider or linked
|
|
401
|
+
const originProvider = this.aggregatedDetails?.custodianProvider || this.ibGib?.data?.frameDetails?.custodianProvider;
|
|
402
|
+
const isGoogleLinked = isSsoProviderLinked({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails, providerId: 'google' });
|
|
403
|
+
const isGithubLinked = isSsoProviderLinked({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails, providerId: 'github' });
|
|
404
|
+
const caps = getKeystoneCapabilities({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails });
|
|
400
405
|
// Google Card States
|
|
401
406
|
const googleCard = this.elements.btnLinkGoogleEl;
|
|
402
407
|
const googleLabel = googleCard.querySelector('.toggle-state-text');
|
|
403
408
|
if (isGoogleLinked) {
|
|
404
409
|
googleCard.classList.remove('unlinked');
|
|
405
410
|
googleCard.classList.add('linked');
|
|
406
|
-
|
|
411
|
+
const isOrigin = originProvider === 'google';
|
|
412
|
+
if (isOrigin && !caps.isSovereign) {
|
|
413
|
+
googleLabel.textContent = "Google (Primary Account) 🔒";
|
|
414
|
+
googleCard.title = "Cannot unlink primary account without first adding a Master Password below.";
|
|
415
|
+
}
|
|
416
|
+
else if (isOrigin) {
|
|
417
|
+
googleLabel.textContent = "Google (Primary Account) ✅";
|
|
418
|
+
googleCard.title = "Click to unlink Google provider (requires Master Password).";
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
googleLabel.textContent = "Google Linked ✅";
|
|
422
|
+
googleCard.title = "Click to unlink Google provider.";
|
|
423
|
+
}
|
|
407
424
|
}
|
|
408
425
|
else {
|
|
409
426
|
googleCard.classList.remove('linked');
|
|
410
427
|
googleCard.classList.add('unlinked');
|
|
411
428
|
googleLabel.textContent = "Unlinked";
|
|
429
|
+
googleCard.title = "Click to link Google account.";
|
|
412
430
|
}
|
|
413
431
|
// GitHub Card States
|
|
414
432
|
const githubCard = this.elements.btnLinkGithubEl;
|
|
@@ -416,15 +434,56 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
416
434
|
if (isGithubLinked) {
|
|
417
435
|
githubCard.classList.remove('unlinked');
|
|
418
436
|
githubCard.classList.add('linked');
|
|
419
|
-
|
|
437
|
+
const isOrigin = originProvider === 'github';
|
|
438
|
+
if (isOrigin && !caps.isSovereign) {
|
|
439
|
+
githubLabel.textContent = "GitHub (Primary Account) 🔒";
|
|
440
|
+
githubCard.title = "Cannot unlink primary account without first adding a Master Password below.";
|
|
441
|
+
}
|
|
442
|
+
else if (isOrigin) {
|
|
443
|
+
githubLabel.textContent = "GitHub (Primary Account) ✅";
|
|
444
|
+
githubCard.title = "Click to unlink GitHub provider (requires Master Password).";
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
githubLabel.textContent = "GitHub Linked ✅";
|
|
448
|
+
githubCard.title = "Click to unlink GitHub provider.";
|
|
449
|
+
}
|
|
420
450
|
}
|
|
421
451
|
else {
|
|
422
452
|
githubCard.classList.remove('linked');
|
|
423
453
|
githubCard.classList.add('unlinked');
|
|
424
454
|
githubLabel.textContent = "Unlinked";
|
|
455
|
+
githubCard.title = "Click to link GitHub account.";
|
|
425
456
|
}
|
|
426
457
|
}
|
|
427
458
|
}
|
|
459
|
+
updatePasswordCardView() {
|
|
460
|
+
const caps = getKeystoneCapabilities({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails });
|
|
461
|
+
const cardTitleEl = this.elements?.changePasswordCardEl?.querySelector('h3');
|
|
462
|
+
const cardDescEl = this.elements?.changePasswordCardEl?.querySelector('.description');
|
|
463
|
+
const currentPasswordGroup = this.elements?.inputCurrentPasswordEl?.closest('.form-group');
|
|
464
|
+
const btnChangePassword = this.elements?.btnChangePasswordEl;
|
|
465
|
+
const isCustodialOnly = caps.isCustodial && !caps.isSovereign;
|
|
466
|
+
if (isCustodialOnly) {
|
|
467
|
+
if (cardTitleEl)
|
|
468
|
+
cardTitleEl.textContent = "Add Master Password";
|
|
469
|
+
if (cardDescEl)
|
|
470
|
+
cardDescEl.textContent = "Establish sovereign ownership by adding a master password. This enables direct, independent cryptographic management without relying solely on custodian authentication.";
|
|
471
|
+
if (currentPasswordGroup)
|
|
472
|
+
currentPasswordGroup.style.display = 'none';
|
|
473
|
+
if (btnChangePassword)
|
|
474
|
+
btnChangePassword.textContent = "Add Master Password";
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
if (cardTitleEl)
|
|
478
|
+
cardTitleEl.textContent = "Change Password";
|
|
479
|
+
if (cardDescEl)
|
|
480
|
+
cardDescEl.textContent = "Rotate your local password that drives this identity. This will cryptographically evolve your primary identity and all registered user-owned delegate keystones to the new password.";
|
|
481
|
+
if (currentPasswordGroup)
|
|
482
|
+
currentPasswordGroup.style.display = '';
|
|
483
|
+
if (btnChangePassword)
|
|
484
|
+
btnChangePassword.textContent = "Update Password";
|
|
485
|
+
}
|
|
486
|
+
}
|
|
428
487
|
async updateFrameDetailsView() {
|
|
429
488
|
const lc = `${this.lc}[${this.updateFrameDetailsView.name}]`;
|
|
430
489
|
try {
|
|
@@ -484,6 +543,7 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
484
543
|
metaspace,
|
|
485
544
|
space
|
|
486
545
|
});
|
|
546
|
+
this.aggregatedDetails = aggregated;
|
|
487
547
|
this.elements.identityAggrDetailsEl.textContent = aggregated ? pretty(aggregated) : '{}';
|
|
488
548
|
// Update Username and Description fields
|
|
489
549
|
this.elements.identityNameEl.textContent = aggregated?.username || aggregated?.profile?.name || aggregated?.name || '-';
|
|
@@ -491,18 +551,21 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
491
551
|
}
|
|
492
552
|
catch (err) {
|
|
493
553
|
console.warn(`${lc} Error aggregating details: ${extractErrorMsg(err)}`);
|
|
554
|
+
this.aggregatedDetails = undefined;
|
|
494
555
|
this.elements.identityAggrDetailsEl.textContent = '{}';
|
|
495
556
|
this.elements.identityNameEl.textContent = '-';
|
|
496
557
|
this.elements.identityDescriptionEl.textContent = '-';
|
|
497
558
|
}
|
|
498
559
|
}
|
|
499
560
|
else {
|
|
561
|
+
this.aggregatedDetails = undefined;
|
|
500
562
|
this.elements.identityAggrDetailsEl.textContent = '{}';
|
|
501
563
|
this.elements.identityNameEl.textContent = '-';
|
|
502
564
|
this.elements.identityDescriptionEl.textContent = '-';
|
|
503
565
|
}
|
|
504
566
|
}
|
|
505
567
|
else {
|
|
568
|
+
this.aggregatedDetails = undefined;
|
|
506
569
|
this.elements.identityAggrDetailsEl.textContent = '{}';
|
|
507
570
|
this.elements.identityNameEl.textContent = '-';
|
|
508
571
|
this.elements.identityDescriptionEl.textContent = '-';
|
|
@@ -532,6 +595,7 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
532
595
|
await this.renderDelegates(keystone.data?.delegates);
|
|
533
596
|
this.elements.ssoLinkCardEl.classList.remove('hidden');
|
|
534
597
|
this.elements.changePasswordCardEl.classList.remove('hidden');
|
|
598
|
+
this.updatePasswordCardView();
|
|
535
599
|
}
|
|
536
600
|
else {
|
|
537
601
|
this.elements.delegatesCardEl.classList.add('hidden');
|
|
@@ -554,6 +618,7 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
554
618
|
if (!this.elements) {
|
|
555
619
|
return;
|
|
556
620
|
}
|
|
621
|
+
this.aggregatedDetails = undefined;
|
|
557
622
|
this.isHistoricalFrame = false;
|
|
558
623
|
if (this.elements.historicalWarningBannerEl) {
|
|
559
624
|
this.elements.historicalWarningBannerEl.classList.add('hidden');
|
|
@@ -730,39 +795,66 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
730
795
|
return;
|
|
731
796
|
}
|
|
732
797
|
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
798
|
+
if (!metaspace) {
|
|
799
|
+
throw new Error(`(UNEXPECTED) metaspace falsy? (E: 8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3e)`);
|
|
800
|
+
}
|
|
801
|
+
const space = await metaspace.getLocalUserSpace({ lock: false });
|
|
802
|
+
if (!space) {
|
|
803
|
+
throw new Error(`(UNEXPECTED) space falsy? (E: 9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4f)`);
|
|
739
804
|
}
|
|
805
|
+
const localSpaceId = space.ib ? parseSpaceIb({ spaceIb: space.ib }).spaceId : undefined;
|
|
740
806
|
for (const [delegateTjpAddr, info] of Object.entries(delegates)) {
|
|
741
807
|
const tr = document.createElement('tr');
|
|
742
808
|
const deviceName = info.deviceName || info.details?.deviceTag || info.details?.name || 'Device Delegate';
|
|
743
809
|
const tdName = document.createElement('td');
|
|
744
810
|
const delegateSpaceId = info.spaceId || info.details?.spaceId;
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
badgeEl.textContent = 'This Device';
|
|
755
|
-
badgeEl.className = 'badge badge-this-device';
|
|
756
|
-
badgeEl.style.backgroundColor = '#1b5e20';
|
|
757
|
-
badgeEl.style.color = '#a5d6a7';
|
|
811
|
+
// Check if delegate is present in local space (via spaceId match or direct presence check)
|
|
812
|
+
let isThisDevice = false;
|
|
813
|
+
if (localSpaceId && delegateSpaceId === localSpaceId) {
|
|
814
|
+
isThisDevice = true;
|
|
815
|
+
}
|
|
816
|
+
else {
|
|
817
|
+
try {
|
|
818
|
+
const resGet = await metaspace.get({ addrs: [delegateTjpAddr], space });
|
|
819
|
+
isThisDevice = !!(resGet.success && resGet.ibGibs && resGet.ibGibs.length > 0);
|
|
758
820
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
badgeEl.className = 'badge badge-remote-device';
|
|
762
|
-
badgeEl.style.backgroundColor = '#0d47a1';
|
|
763
|
-
badgeEl.style.color = '#90caf9';
|
|
821
|
+
catch {
|
|
822
|
+
isThisDevice = false;
|
|
764
823
|
}
|
|
765
824
|
}
|
|
825
|
+
const badgeEl = document.createElement('span');
|
|
826
|
+
badgeEl.style.marginLeft = '0.5rem';
|
|
827
|
+
badgeEl.style.padding = '0.15rem 0.4rem';
|
|
828
|
+
badgeEl.style.borderRadius = '4px';
|
|
829
|
+
badgeEl.style.fontSize = '0.75rem';
|
|
830
|
+
badgeEl.style.fontWeight = 'bold';
|
|
831
|
+
if (isThisDevice) {
|
|
832
|
+
badgeEl.textContent = 'This Device';
|
|
833
|
+
badgeEl.className = 'badge badge-this-device';
|
|
834
|
+
badgeEl.style.backgroundColor = '#1b5e20';
|
|
835
|
+
badgeEl.style.color = '#a5d6a7';
|
|
836
|
+
}
|
|
837
|
+
else {
|
|
838
|
+
badgeEl.textContent = 'Remote Device';
|
|
839
|
+
badgeEl.className = 'badge badge-remote-device';
|
|
840
|
+
badgeEl.style.backgroundColor = '#0d47a1';
|
|
841
|
+
badgeEl.style.color = '#90caf9';
|
|
842
|
+
}
|
|
843
|
+
// Check expiration
|
|
844
|
+
const isExpired = isDelegateExpired({ delegateInfo: info });
|
|
845
|
+
let expiredBadgeEl = null;
|
|
846
|
+
if (isExpired) {
|
|
847
|
+
expiredBadgeEl = document.createElement('span');
|
|
848
|
+
expiredBadgeEl.textContent = 'Expired';
|
|
849
|
+
expiredBadgeEl.className = 'badge badge-expired';
|
|
850
|
+
expiredBadgeEl.style.marginLeft = '0.3rem';
|
|
851
|
+
expiredBadgeEl.style.padding = '0.15rem 0.4rem';
|
|
852
|
+
expiredBadgeEl.style.borderRadius = '4px';
|
|
853
|
+
expiredBadgeEl.style.fontSize = '0.75rem';
|
|
854
|
+
expiredBadgeEl.style.fontWeight = 'bold';
|
|
855
|
+
expiredBadgeEl.style.backgroundColor = '#b71c1c';
|
|
856
|
+
expiredBadgeEl.style.color = '#ef9a9a';
|
|
857
|
+
}
|
|
766
858
|
const applyNameAndBadge = (displayName, hoverText) => {
|
|
767
859
|
tdName.innerHTML = '';
|
|
768
860
|
const textSpan = document.createElement('span');
|
|
@@ -771,6 +863,9 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
771
863
|
if (badgeEl) {
|
|
772
864
|
tdName.appendChild(badgeEl);
|
|
773
865
|
}
|
|
866
|
+
if (expiredBadgeEl) {
|
|
867
|
+
tdName.appendChild(expiredBadgeEl);
|
|
868
|
+
}
|
|
774
869
|
tdName.title = hoverText || displayName;
|
|
775
870
|
};
|
|
776
871
|
const displayName = `💻 ${deviceName}`;
|
|
@@ -781,7 +876,7 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
781
876
|
const tdVerbs = document.createElement('td');
|
|
782
877
|
tdVerbs.textContent = info.allowedVerbs ? info.allowedVerbs.join(', ') : '-';
|
|
783
878
|
tdVerbs.title = tdVerbs.textContent;
|
|
784
|
-
const isRemote =
|
|
879
|
+
const isRemote = !isThisDevice;
|
|
785
880
|
const tdActions = document.createElement('td');
|
|
786
881
|
const btnView = document.createElement('button');
|
|
787
882
|
btnView.className = 'action-btn secondary small';
|
|
@@ -792,22 +887,22 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
792
887
|
this.showRemoteDelegateModal(info);
|
|
793
888
|
}
|
|
794
889
|
else {
|
|
795
|
-
if (info.delegateAddr) {
|
|
890
|
+
if (info.delegateAddr || delegateTjpAddr) {
|
|
796
891
|
let addrToLoad = info.delegateAddr;
|
|
797
|
-
|
|
798
|
-
const
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
}
|
|
806
|
-
catch (err) {
|
|
807
|
-
console.warn(`${lc} Error resolving latest delegate address: ${extractErrorMsg(err)}`);
|
|
808
|
-
}
|
|
892
|
+
try {
|
|
893
|
+
const keystoneService = new KeystoneService_V1();
|
|
894
|
+
const latestKeystone = await keystoneService.getLatestKeystone({
|
|
895
|
+
addr: delegateTjpAddr,
|
|
896
|
+
metaspace,
|
|
897
|
+
space
|
|
898
|
+
});
|
|
899
|
+
if (latestKeystone) {
|
|
900
|
+
addrToLoad = getIbGibAddr({ ibGib: latestKeystone });
|
|
809
901
|
}
|
|
810
902
|
}
|
|
903
|
+
catch (err) {
|
|
904
|
+
console.warn(`${lc} Error resolving latest delegate keystone: ${extractErrorMsg(err)}`);
|
|
905
|
+
}
|
|
811
906
|
this.dispatchEvent(new CustomEvent('ibgib-view-keystone-details', {
|
|
812
907
|
detail: { addr: addrToLoad },
|
|
813
908
|
bubbles: true,
|
|
@@ -1090,8 +1185,13 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1090
1185
|
}
|
|
1091
1186
|
}
|
|
1092
1187
|
async handleSSOClick(providerId) {
|
|
1093
|
-
const isLinked = isSsoProviderLinked({ keystone: this.ibGib, providerId });
|
|
1188
|
+
const isLinked = isSsoProviderLinked({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails, providerId });
|
|
1094
1189
|
if (isLinked) {
|
|
1190
|
+
const caps = getKeystoneCapabilities({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails });
|
|
1191
|
+
if (!caps.isSovereign) {
|
|
1192
|
+
this.setSSOStatus(`Cannot unlink ${providerId}: This is your only identity recovery and management method. Please add a Master Password first in the Password card below to enable sovereign ownership before unlinking.`, "error");
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1095
1195
|
await this.handleSSOUnlink(providerId);
|
|
1096
1196
|
}
|
|
1097
1197
|
else {
|
|
@@ -1149,13 +1249,28 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1149
1249
|
}
|
|
1150
1250
|
// 2. Evolve parent keystone to remove the custodian pool
|
|
1151
1251
|
const targetPoolId = `custodian-manage-${providerId}`;
|
|
1152
|
-
|
|
1252
|
+
let evolvedParent = await keystoneService.removePools({
|
|
1153
1253
|
latestKeystone: this.ibGib,
|
|
1154
1254
|
masterSecret: masterSecret,
|
|
1155
1255
|
poolIds: [targetPoolId],
|
|
1156
1256
|
metaspace,
|
|
1157
1257
|
space
|
|
1158
1258
|
});
|
|
1259
|
+
// Unregister any corresponding outward delegate for this provider if present
|
|
1260
|
+
const delegatesMap = this.ibGib?.data?.delegates;
|
|
1261
|
+
if (delegatesMap) {
|
|
1262
|
+
for (const [delegateTjpAddr, info] of Object.entries(delegatesMap)) {
|
|
1263
|
+
if (info.deviceName?.includes(providerId)) {
|
|
1264
|
+
evolvedParent = await keystoneService.unregisterDelegate({
|
|
1265
|
+
parentKeystone: evolvedParent,
|
|
1266
|
+
delegateTjpAddr,
|
|
1267
|
+
masterSecret: masterSecret,
|
|
1268
|
+
metaspace,
|
|
1269
|
+
space,
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1159
1274
|
// 3. Sync evolved parent keystone back to server
|
|
1160
1275
|
this.setSSOStatus("Publishing evolved identity keystone to server...", "info");
|
|
1161
1276
|
const syncRes = await getApiBridge().putEvolveKeystone({
|
|
@@ -1192,8 +1307,154 @@ export class KeystoneDetailsComponentInstance extends IbGibDynamicComponentInsta
|
|
|
1192
1307
|
el.className = `status-msg ${type}`;
|
|
1193
1308
|
el.classList.remove('hidden');
|
|
1194
1309
|
}
|
|
1310
|
+
async handleAddMasterPassword() {
|
|
1311
|
+
const lc = `${this.lc}[${this.handleAddMasterPassword.name}]`;
|
|
1312
|
+
this.setChangePasswordStatus("Validating entries...", "info");
|
|
1313
|
+
try {
|
|
1314
|
+
const newPassword = this.elements.inputNewPasswordEl.value;
|
|
1315
|
+
const confirmPassword = this.elements.inputConfirmPasswordEl.value;
|
|
1316
|
+
if (!newPassword) {
|
|
1317
|
+
this.setChangePasswordStatus("Master password is required.", "error");
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
if (newPassword !== confirmPassword) {
|
|
1321
|
+
this.setChangePasswordStatus("Master password and confirmation do not match.", "error");
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
const initialManagePool = this.ibGib?.data?.challengePools?.find(p => p.id === POOL_ID_MANAGE);
|
|
1325
|
+
if (!initialManagePool) {
|
|
1326
|
+
this.setChangePasswordStatus("Primary keystone missing active manage challenge pool.", "error");
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
this.showBusy({
|
|
1330
|
+
isBusy: true,
|
|
1331
|
+
title: 'Elevating Identity...',
|
|
1332
|
+
msg: 'Generating cryptographic sovereign challenge pool...',
|
|
1333
|
+
animationEmoji: '🔑'
|
|
1334
|
+
});
|
|
1335
|
+
this.setChangePasswordStatus("Generating sovereign challenge pool...", "info");
|
|
1336
|
+
const newChallenges = await generatePoolChallenges({
|
|
1337
|
+
config: initialManagePool.config,
|
|
1338
|
+
masterSecret: newPassword,
|
|
1339
|
+
});
|
|
1340
|
+
const newManagePool = {
|
|
1341
|
+
id: POOL_ID_MANAGE,
|
|
1342
|
+
config: initialManagePool.config,
|
|
1343
|
+
challenges: newChallenges,
|
|
1344
|
+
};
|
|
1345
|
+
const primaryTjpAddr = getTjpAddr({ ibGib: this.ibGib }) || getIbGibAddr({ ibGib: this.ibGib });
|
|
1346
|
+
const custodianProvider = this.aggregatedDetails?.custodianProvider || 'google';
|
|
1347
|
+
let requestBody = {
|
|
1348
|
+
primaryTjpAddr,
|
|
1349
|
+
newManagePool,
|
|
1350
|
+
};
|
|
1351
|
+
if (custodianProvider === 'google' || custodianProvider === 'github') {
|
|
1352
|
+
this.showBusy({
|
|
1353
|
+
isBusy: true,
|
|
1354
|
+
title: 'Re-authenticating with Provider...',
|
|
1355
|
+
msg: `Please complete authentication in the ${custodianProvider} popup window...`,
|
|
1356
|
+
animationEmoji: '🔒'
|
|
1357
|
+
});
|
|
1358
|
+
this.setChangePasswordStatus(`Please authenticate in the popup with ${custodianProvider} to verify ownership...`, "info");
|
|
1359
|
+
const resPopup = await executeSsoOAuthPopup({
|
|
1360
|
+
providerId: custodianProvider,
|
|
1361
|
+
parentTjpAddr: primaryTjpAddr,
|
|
1362
|
+
});
|
|
1363
|
+
if (!resPopup.success || !resPopup.code) {
|
|
1364
|
+
this.showBusy({ isBusy: false });
|
|
1365
|
+
this.setChangePasswordStatus(resPopup.message || "Re-authentication cancelled.", "error");
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
requestBody.providerId = custodianProvider;
|
|
1369
|
+
requestBody.code = resPopup.code;
|
|
1370
|
+
requestBody.redirectUri = resPopup.redirectUri;
|
|
1371
|
+
}
|
|
1372
|
+
else {
|
|
1373
|
+
this.showBusy({ isBusy: false });
|
|
1374
|
+
// Email OTP fallback
|
|
1375
|
+
const email = this.aggregatedDetails?.emails?.[0] || this.ibGib?.data?.emails?.[0];
|
|
1376
|
+
const otpCode = await promptForSecret({
|
|
1377
|
+
msg: `Enter 6-digit verification code sent to ${email}:`,
|
|
1378
|
+
confirm: false,
|
|
1379
|
+
});
|
|
1380
|
+
if (!otpCode) {
|
|
1381
|
+
this.setChangePasswordStatus("Verification code required.", "error");
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
requestBody.email = email;
|
|
1385
|
+
requestBody.code = otpCode;
|
|
1386
|
+
}
|
|
1387
|
+
this.showBusy({
|
|
1388
|
+
isBusy: true,
|
|
1389
|
+
title: 'Updating Keystone Timeline...',
|
|
1390
|
+
msg: 'Publishing sovereign master password and evolving identity DAG...',
|
|
1391
|
+
animationEmoji: '⏳'
|
|
1392
|
+
});
|
|
1393
|
+
this.setChangePasswordStatus("Elevating identity to sovereign master password on server...", "info");
|
|
1394
|
+
const response = await fetch('/api/identity/custodian/add-master-password', {
|
|
1395
|
+
method: 'POST',
|
|
1396
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1397
|
+
body: JSON.stringify(requestBody),
|
|
1398
|
+
});
|
|
1399
|
+
const resJson = await response.json();
|
|
1400
|
+
if (!response.ok || !resJson.success) {
|
|
1401
|
+
throw new Error(resJson.error || resJson.message || `Server error (${response.status})`);
|
|
1402
|
+
}
|
|
1403
|
+
const evolvedParent = resJson.primaryKeystone;
|
|
1404
|
+
// Save to local user space & metaspace
|
|
1405
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
1406
|
+
if (!metaspace) {
|
|
1407
|
+
throw new Error("Global metaspace not available");
|
|
1408
|
+
}
|
|
1409
|
+
const space = await metaspace.getLocalUserSpace({ lock: false });
|
|
1410
|
+
if (!space) {
|
|
1411
|
+
throw new Error("Local user space not available");
|
|
1412
|
+
}
|
|
1413
|
+
await metaspace.put({ ibGibs: [evolvedParent], space });
|
|
1414
|
+
await metaspace.registerNewIbGib({ ibGib: evolvedParent, space });
|
|
1415
|
+
await updateSpecialIndex({
|
|
1416
|
+
type: "keystones",
|
|
1417
|
+
rel8nInfos: [
|
|
1418
|
+
{
|
|
1419
|
+
rel8nName: "keystone",
|
|
1420
|
+
ibGibs: [toDto({ ibGib: evolvedParent })]
|
|
1421
|
+
}
|
|
1422
|
+
],
|
|
1423
|
+
metaspace,
|
|
1424
|
+
space
|
|
1425
|
+
});
|
|
1426
|
+
const evolvedAddr = getIbGibAddr({ ibGib: evolvedParent });
|
|
1427
|
+
// Dispatch EVENT_IBGIB_IDENTITY_CHANGED
|
|
1428
|
+
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_IDENTITY_CHANGED, {
|
|
1429
|
+
detail: { activeIdentityAddr: evolvedAddr },
|
|
1430
|
+
bubbles: true,
|
|
1431
|
+
composed: true
|
|
1432
|
+
}));
|
|
1433
|
+
this.setChangePasswordStatus("Successfully added Master Password! Identity is now sovereign.", "success");
|
|
1434
|
+
// Refresh the view
|
|
1435
|
+
this.ibGibAddr = evolvedAddr;
|
|
1436
|
+
this.staticIbGib = evolvedParent;
|
|
1437
|
+
await this.loadIbGib({ getLatest: true });
|
|
1438
|
+
// Clear inputs
|
|
1439
|
+
this.elements.inputNewPasswordEl.value = '';
|
|
1440
|
+
this.elements.inputConfirmPasswordEl.value = '';
|
|
1441
|
+
}
|
|
1442
|
+
catch (error) {
|
|
1443
|
+
console.error(`${lc} Elevation failed: ${extractErrorMsg(error)}`);
|
|
1444
|
+
this.setChangePasswordStatus(`Failed to add master password: ${extractErrorMsg(error)}`, "error");
|
|
1445
|
+
}
|
|
1446
|
+
finally {
|
|
1447
|
+
this.showBusy({ isBusy: false });
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1195
1450
|
async handleChangePassword() {
|
|
1196
1451
|
const lc = `${this.lc}[${this.handleChangePassword.name}]`;
|
|
1452
|
+
const caps = getKeystoneCapabilities({ keystone: this.ibGib, aggregatedDetails: this.aggregatedDetails });
|
|
1453
|
+
const isCustodialOnly = caps.isCustodial && !caps.isSovereign;
|
|
1454
|
+
if (isCustodialOnly) {
|
|
1455
|
+
await this.handleAddMasterPassword();
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1197
1458
|
this.setChangePasswordStatus("Validating entries...", "info");
|
|
1198
1459
|
try {
|
|
1199
1460
|
const currentPassword = this.elements.inputCurrentPasswordEl.value;
|