@ibgib/web-gib 0.0.59 → 0.0.60
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/identity/custodian-delegate-helper.d.mts.map +1 -1
- package/dist/identity/custodian-delegate-helper.mjs +20 -3
- package/dist/identity/custodian-delegate-helper.mjs.map +1 -1
- package/dist/identity/identity-constants.mjs +2 -2
- package/dist/identity/identity-constants.mjs.map +1 -1
- package/dist/identity/identity-types.d.mts +4 -4
- package/dist/identity/identity-types.d.mts.map +1 -1
- package/dist/ui/component/identity/add-device/add-device.css +93 -17
- package/dist/ui/component/identity/add-device/add-device.d.mts +24 -7
- package/dist/ui/component/identity/add-device/add-device.d.mts.map +1 -1
- package/dist/ui/component/identity/add-device/add-device.html +97 -23
- package/dist/ui/component/identity/add-device/add-device.mjs +490 -186
- 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 +7 -4
- package/dist/ui/component/identity/keystone-creator/keystone-creator.mjs.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.css +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts +8 -8
- package/dist/ui/component/identity/keystone-details/keystone-details.d.mts.map +1 -1
- package/dist/ui/component/identity/keystone-details/keystone-details.html +29 -20
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs +51 -51
- package/dist/ui/component/identity/keystone-details/keystone-details.mjs.map +1 -1
- package/dist/ui/component/identity/remove-device/remove-device.d.mts +1 -1
- package/dist/ui/component/identity/remove-device/remove-device.d.mts.map +1 -1
- package/dist/ui/component/identity/remove-device/remove-device.html +3 -3
- package/dist/ui/component/identity/remove-device/remove-device.mjs +7 -7
- package/dist/ui/component/identity/remove-device/remove-device.mjs.map +1 -1
- package/dist/util/device-helper.d.mts +30 -0
- package/dist/util/device-helper.d.mts.map +1 -0
- package/dist/util/device-helper.mjs +90 -0
- package/dist/util/device-helper.mjs.map +1 -0
- package/dist/util/device-helper.respec.d.mts +2 -0
- package/dist/util/device-helper.respec.d.mts.map +1 -0
- package/dist/util/device-helper.respec.mjs +83 -0
- package/dist/util/device-helper.respec.mjs.map +1 -0
- package/package.json +3 -3
- package/src/AUTO-GENERATED-version.mts +1 -1
- package/src/identity/custodian-delegate-helper.mts +28 -3
- package/src/identity/identity-constants.mts +2 -2
- package/src/identity/identity-types.mts +4 -4
- package/src/ui/component/identity/add-device/add-device.css +93 -17
- package/src/ui/component/identity/add-device/add-device.html +97 -23
- package/src/ui/component/identity/add-device/add-device.mts +577 -194
- package/src/ui/component/identity/keystone-creator/keystone-creator.mts +7 -4
- package/src/ui/component/identity/keystone-details/keystone-details.css +1 -1
- package/src/ui/component/identity/keystone-details/keystone-details.html +29 -20
- package/src/ui/component/identity/keystone-details/keystone-details.mts +57 -57
- package/src/ui/component/identity/remove-device/remove-device.html +3 -3
- package/src/ui/component/identity/remove-device/remove-device.mts +8 -8
- package/src/util/device-helper.mts +104 -0
- package/src/util/device-helper.respec.mts +90 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module util/device-helper
|
|
3
|
+
*
|
|
4
|
+
* Pure utility functions for generating clean, friendly, auto-generated device names/tags.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Generates a friendly, URL-safe, kebab-cased default device tag.
|
|
8
|
+
* (e.g., `chrome-win-a1b2`, `mobile-safari-ios-3c4d`, `tablet-android-e5f6`).
|
|
9
|
+
*
|
|
10
|
+
* Pure function: Uses global browser objects (`navigator`, `window`) only as fallbacks
|
|
11
|
+
* when explicit options are omitted, enabling deterministic unit testing.
|
|
12
|
+
*
|
|
13
|
+
* @param opts Optional mock options for deterministic unit testing.
|
|
14
|
+
*/
|
|
15
|
+
export function generateDefaultDeviceTag(opts) {
|
|
16
|
+
const ua = (opts?.userAgent ?? (typeof navigator !== 'undefined' ? navigator.userAgent : '') ?? '').toLowerCase();
|
|
17
|
+
const uaData = opts?.userAgentData ?? (typeof navigator !== 'undefined' ? navigator.userAgentData : undefined);
|
|
18
|
+
const touchPoints = opts?.maxTouchPoints ?? (typeof navigator !== 'undefined' ? navigator.maxTouchPoints : 0) ?? 0;
|
|
19
|
+
const width = opts?.screenWidth ?? (typeof window !== 'undefined' ? window.innerWidth : 1024) ?? 1024;
|
|
20
|
+
// 1. Detect Form Factor (Mobile, Tablet, Desktop)
|
|
21
|
+
let formFactor = '';
|
|
22
|
+
const isMobileUaData = uaData?.mobile;
|
|
23
|
+
const isMobileUa = /mobile|iphone|ipod|android.*mobile|windows phone/i.test(ua);
|
|
24
|
+
const isTabletUa = /ipad|tablet|(android(?!.*mobile))/i.test(ua) || (touchPoints > 1 && /macintosh/i.test(ua) && width <= 1366);
|
|
25
|
+
if (isTabletUa) {
|
|
26
|
+
formFactor = 'tablet';
|
|
27
|
+
}
|
|
28
|
+
else if (isMobileUaData || isMobileUa) {
|
|
29
|
+
formFactor = 'mobile';
|
|
30
|
+
}
|
|
31
|
+
// 2. Detect OS / Platform
|
|
32
|
+
let platform = '';
|
|
33
|
+
const uaPlatform = (uaData?.platform || '').toLowerCase();
|
|
34
|
+
if (uaPlatform.includes('win') || ua.includes('windows')) {
|
|
35
|
+
platform = 'win';
|
|
36
|
+
}
|
|
37
|
+
else if (uaPlatform.includes('ios') || ua.includes('iphone') || ua.includes('ipad') || ua.includes('ipod')) {
|
|
38
|
+
platform = 'ios';
|
|
39
|
+
}
|
|
40
|
+
else if (uaPlatform.includes('mac') || ua.includes('macintosh') || ua.includes('mac os')) {
|
|
41
|
+
platform = 'mac';
|
|
42
|
+
}
|
|
43
|
+
else if (uaPlatform.includes('android') || ua.includes('android')) {
|
|
44
|
+
platform = 'android';
|
|
45
|
+
}
|
|
46
|
+
else if (uaPlatform.includes('linux') || ua.includes('linux')) {
|
|
47
|
+
platform = 'linux';
|
|
48
|
+
}
|
|
49
|
+
// 3. Detect Browser
|
|
50
|
+
let browser = '';
|
|
51
|
+
if (ua.includes('edg/') || ua.includes('edge/')) {
|
|
52
|
+
browser = 'edge';
|
|
53
|
+
}
|
|
54
|
+
else if (ua.includes('chrome/') || ua.includes('crios/')) {
|
|
55
|
+
browser = 'chrome';
|
|
56
|
+
}
|
|
57
|
+
else if (ua.includes('firefox/') || ua.includes('fxios/')) {
|
|
58
|
+
browser = 'firefox';
|
|
59
|
+
}
|
|
60
|
+
else if (ua.includes('safari/') && !ua.includes('chrome/') && !ua.includes('crios/')) {
|
|
61
|
+
browser = 'safari';
|
|
62
|
+
}
|
|
63
|
+
// 4. Generate or use provided 4-character random suffix
|
|
64
|
+
let suffix = opts?.randomSuffix;
|
|
65
|
+
if (!suffix) {
|
|
66
|
+
const chars = '0123456789abcdef';
|
|
67
|
+
suffix = '';
|
|
68
|
+
for (let i = 0; i < 4; i++) {
|
|
69
|
+
suffix += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
suffix = suffix.toLowerCase().replace(/[^a-z0-9]/g, '').slice(0, 8);
|
|
73
|
+
// 5. Assemble Tokens
|
|
74
|
+
const tokens = [];
|
|
75
|
+
if (formFactor) {
|
|
76
|
+
tokens.push(formFactor);
|
|
77
|
+
}
|
|
78
|
+
if (browser) {
|
|
79
|
+
tokens.push(browser);
|
|
80
|
+
}
|
|
81
|
+
if (platform) {
|
|
82
|
+
tokens.push(platform);
|
|
83
|
+
}
|
|
84
|
+
if (tokens.length === 0) {
|
|
85
|
+
tokens.push('device');
|
|
86
|
+
}
|
|
87
|
+
tokens.push(suffix);
|
|
88
|
+
return tokens.join('-');
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=device-helper.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-helper.mjs","sourceRoot":"","sources":["../../src/util/device-helper.mts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-helper.respec.d.mts","sourceRoot":"","sources":["../../src/util/device-helper.respec.mts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { respecfully, iReckon, ifWe // DO NOT REMOVE ifWeMight or ifWe even if no longer used. We constantly change these when focusing tests
|
|
2
|
+
} from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
|
|
3
|
+
const maam = `[${import.meta.url}]`, sir = maam;
|
|
4
|
+
import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
5
|
+
import { generateDefaultDeviceTag } from './device-helper.mjs';
|
|
6
|
+
await respecfully(sir, 'device-helper', async () => {
|
|
7
|
+
await ifWe(sir, 'generate clean tag for Desktop Windows Chrome', async () => {
|
|
8
|
+
const lc = `[generate clean tag for Desktop Windows Chrome]`;
|
|
9
|
+
try {
|
|
10
|
+
const tag = generateDefaultDeviceTag({
|
|
11
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
12
|
+
userAgentData: { platform: 'Windows', mobile: false },
|
|
13
|
+
randomSuffix: 'a1b2'
|
|
14
|
+
});
|
|
15
|
+
iReckon(sir, tag).asTo('tag value').isGonnaBe('chrome-win-a1b2');
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
await ifWe(sir, 'generate clean tag for Mobile iOS Safari (iPhone)', async () => {
|
|
23
|
+
const lc = `[generate clean tag for Mobile iOS Safari (iPhone)]`;
|
|
24
|
+
try {
|
|
25
|
+
const tag = generateDefaultDeviceTag({
|
|
26
|
+
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
|
|
27
|
+
userAgentData: { platform: 'iOS', mobile: true },
|
|
28
|
+
randomSuffix: '3c4d'
|
|
29
|
+
});
|
|
30
|
+
iReckon(sir, tag).asTo('tag value').isGonnaBe('mobile-safari-ios-3c4d');
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
await ifWe(sir, 'generate clean tag for Android Chrome Phone', async () => {
|
|
38
|
+
const lc = `[generate clean tag for Android Chrome Phone]`;
|
|
39
|
+
try {
|
|
40
|
+
const tag = generateDefaultDeviceTag({
|
|
41
|
+
userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36',
|
|
42
|
+
userAgentData: { platform: 'Android', mobile: true },
|
|
43
|
+
randomSuffix: '9e8f'
|
|
44
|
+
});
|
|
45
|
+
iReckon(sir, tag).asTo('tag value').isGonnaBe('mobile-chrome-android-9e8f');
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
await ifWe(sir, 'generate clean tag for iPad / Tablet', async () => {
|
|
53
|
+
const lc = `[generate clean tag for iPad / Tablet]`;
|
|
54
|
+
try {
|
|
55
|
+
const tag = generateDefaultDeviceTag({
|
|
56
|
+
userAgent: 'Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
|
|
57
|
+
maxTouchPoints: 5,
|
|
58
|
+
screenWidth: 1024,
|
|
59
|
+
randomSuffix: '7a8b'
|
|
60
|
+
});
|
|
61
|
+
iReckon(sir, tag).asTo('tag value').isGonnaBe('tablet-safari-ios-7a8b');
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
await ifWe(sir, 'generate fallback tag with random 4-char suffix when no hints are present', async () => {
|
|
69
|
+
const lc = `[generate fallback tag with random 4-char suffix]`;
|
|
70
|
+
try {
|
|
71
|
+
const tag = generateDefaultDeviceTag({
|
|
72
|
+
userAgent: '',
|
|
73
|
+
randomSuffix: '1234'
|
|
74
|
+
});
|
|
75
|
+
iReckon(sir, tag).asTo('tag value').isGonnaBe('device-1234');
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=device-helper.respec.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-helper.respec.mjs","sourceRoot":"","sources":["../../src/util/device-helper.respec.mts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ibgib/web-gib",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.60",
|
|
4
4
|
"description": "Framework for creating agentic ibGib web apps. Contains plumbing for ibgib components, agentic framework (currently only Gemini implemented), web-based IndexedDB storage substrate, and more.",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"license": "ISC",
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@google/genai": "^1.33.0",
|
|
32
|
-
"@ibgib/core-gib": "^0.1.
|
|
32
|
+
"@ibgib/core-gib": "^0.1.74",
|
|
33
33
|
"@ibgib/encrypt-gib": "^0.3.1",
|
|
34
34
|
"@ibgib/helper-gib": "^0.0.37",
|
|
35
|
-
"@ibgib/ts-gib": "^0.5.
|
|
35
|
+
"@ibgib/ts-gib": "^0.5.34"
|
|
36
36
|
},
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">=19.0.0"
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { getUUID, hash, HashAlgorithm, extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
9
|
+
import { storageGet, storagePut } from "../storage/storage-helpers.web.mjs";
|
|
10
|
+
import { getGlobalDbName, getGlobalStoreName } from "../helpers.web.mjs";
|
|
9
11
|
|
|
10
12
|
const LOCAL_STORAGE_PREFIX = 'ibgib_custodian_delegate_secret_';
|
|
11
13
|
|
|
@@ -27,20 +29,43 @@ export async function deriveLocalCustodianDelegateSecret({
|
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
const key = `${LOCAL_STORAGE_PREFIX}${primaryTjpAddr}`;
|
|
30
|
-
|
|
32
|
+
|
|
33
|
+
const dbName = getGlobalDbName();
|
|
34
|
+
const storeName = getGlobalStoreName();
|
|
35
|
+
|
|
36
|
+
// debugger; // what are the dbName and storeName?
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
const existingSecret = await storageGet({ dbName, storeName, key });
|
|
31
40
|
if (existingSecret) {
|
|
32
41
|
return existingSecret;
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
// Generate high-entropy 512-bit SHA hash secret from fresh UUIDs
|
|
36
|
-
const rawEntropy =
|
|
45
|
+
const rawEntropy = await getUUID(32);
|
|
37
46
|
const newSecret = await hash({
|
|
38
47
|
s: rawEntropy,
|
|
39
48
|
algorithm: HashAlgorithm.sha_512
|
|
40
49
|
});
|
|
50
|
+
await storagePut({ dbName, storeName, key, value: newSecret });
|
|
41
51
|
|
|
42
|
-
localStorage.setItem(key, newSecret);
|
|
43
52
|
return newSecret;
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
// const existingSecret = localStorage.getItem(key);
|
|
56
|
+
// if (existingSecret) {
|
|
57
|
+
// return existingSecret;
|
|
58
|
+
// }
|
|
59
|
+
|
|
60
|
+
// // Generate high-entropy 512-bit SHA hash secret from fresh UUIDs
|
|
61
|
+
// const rawEntropy = `${await getUUID()}-${await getUUID()}-${await getUUID()}-${Date.now()}`;
|
|
62
|
+
// const newSecret = await hash({
|
|
63
|
+
// s: rawEntropy,
|
|
64
|
+
// algorithm: HashAlgorithm.sha_512
|
|
65
|
+
// });
|
|
66
|
+
|
|
67
|
+
// localStorage.setItem(key, newSecret);
|
|
68
|
+
// return newSecret;
|
|
44
69
|
} catch (error) {
|
|
45
70
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
46
71
|
throw error;
|
|
@@ -65,8 +65,8 @@ export const IDENTITY_POOL_ID_APP_EPOCH = 'identity-app-epoch';
|
|
|
65
65
|
|
|
66
66
|
// ===========================================================================
|
|
67
67
|
// KDF SALTS
|
|
68
|
-
// Used to derive tier-specific MasterSecrets from a root
|
|
69
|
-
// Different salt per tier ensures independent secrets even with same
|
|
68
|
+
// Used to derive tier-specific MasterSecrets from a root password.
|
|
69
|
+
// Different salt per tier ensures independent secrets even with same password.
|
|
70
70
|
// ===========================================================================
|
|
71
71
|
|
|
72
72
|
export const IDENTITY_KDF_SALT_META = 'ibgib-identity-meta-v1';
|
|
@@ -221,17 +221,17 @@ export interface IIdentityService {
|
|
|
221
221
|
// ===========================================================================
|
|
222
222
|
|
|
223
223
|
/**
|
|
224
|
-
* Inputs for deriving a tier-specific MasterSecret from a root
|
|
224
|
+
* Inputs for deriving a tier-specific MasterSecret from a root password.
|
|
225
225
|
*
|
|
226
226
|
* Each tier uses a different salt to ensure independent MasterSecrets
|
|
227
|
-
* even when the same
|
|
227
|
+
* even when the same password is used for all tiers.
|
|
228
228
|
*
|
|
229
229
|
* @see docs/identity/KEYSTONES.md for per-tier salt conventions
|
|
230
230
|
* @see identity-helpers.mts for derivation functions
|
|
231
231
|
*/
|
|
232
232
|
export interface MasterSecretDerivationContext {
|
|
233
|
-
/** The root
|
|
234
|
-
|
|
233
|
+
/** The root password (sovereign path) or server secret (custodial path). */
|
|
234
|
+
password: string;
|
|
235
235
|
|
|
236
236
|
/**
|
|
237
237
|
* Tier-specific salt (e.g., `IDENTITY_KDF_SALT_META`, `IDENTITY_KDF_SALT_LOCAL`).
|
|
@@ -5,22 +5,75 @@
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
.component-container {
|
|
8
|
-
padding: 1.
|
|
8
|
+
padding: 1.75rem;
|
|
9
9
|
background: var(--ibgib-bg-surface, #1e1e2e);
|
|
10
10
|
color: var(--ibgib-text-primary, #cdd6f4);
|
|
11
11
|
border-radius: 12px;
|
|
12
12
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
.hidden {
|
|
16
|
+
display: none !important;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.sso-options {
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
gap: 0.75rem;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.sso-btn {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
gap: 0.75rem;
|
|
30
|
+
padding: 0.8rem 1.25rem;
|
|
31
|
+
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
32
|
+
border-radius: 8px;
|
|
33
|
+
background: #181825;
|
|
34
|
+
color: #cdd6f4;
|
|
35
|
+
font-size: 0.95rem;
|
|
36
|
+
font-weight: 600;
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
transition: transform 0.15s ease, background 0.15s ease, border-color 0.15s ease;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.sso-btn:hover {
|
|
42
|
+
transform: translateY(-1px);
|
|
43
|
+
background: #313244;
|
|
44
|
+
border-color: #89b4fa;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.divider {
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
text-align: center;
|
|
51
|
+
margin: 0.75rem 0;
|
|
52
|
+
color: #a6adc8;
|
|
53
|
+
font-size: 0.75rem;
|
|
54
|
+
font-weight: 700;
|
|
55
|
+
letter-spacing: 1px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.divider::before, .divider::after {
|
|
59
|
+
content: '';
|
|
60
|
+
flex: 1;
|
|
61
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.divider span {
|
|
65
|
+
padding: 0 0.75rem;
|
|
66
|
+
}
|
|
67
|
+
|
|
15
68
|
.input-group {
|
|
16
|
-
margin-bottom:
|
|
69
|
+
margin-bottom: 1rem;
|
|
17
70
|
display: flex;
|
|
18
71
|
flex-direction: column;
|
|
19
72
|
gap: 0.4rem;
|
|
20
73
|
}
|
|
21
74
|
|
|
22
75
|
.input-group label {
|
|
23
|
-
font-size: 0.
|
|
76
|
+
font-size: 0.92rem;
|
|
24
77
|
font-weight: 500;
|
|
25
78
|
color: var(--ibgib-text-secondary, #a6adc8);
|
|
26
79
|
}
|
|
@@ -50,7 +103,7 @@
|
|
|
50
103
|
}
|
|
51
104
|
|
|
52
105
|
.action-row {
|
|
53
|
-
margin-top:
|
|
106
|
+
margin-top: 1rem;
|
|
54
107
|
}
|
|
55
108
|
|
|
56
109
|
.action-btn {
|
|
@@ -58,7 +111,7 @@
|
|
|
58
111
|
padding: 0.85rem 1.5rem;
|
|
59
112
|
border: none;
|
|
60
113
|
border-radius: 8px;
|
|
61
|
-
background: linear-gradient(135deg, #
|
|
114
|
+
background: linear-gradient(135deg, #89b4fa, #74c7ec);
|
|
62
115
|
color: #11111b;
|
|
63
116
|
font-size: 1rem;
|
|
64
117
|
font-weight: 600;
|
|
@@ -75,6 +128,12 @@
|
|
|
75
128
|
transform: translateY(0);
|
|
76
129
|
}
|
|
77
130
|
|
|
131
|
+
.action-btn:disabled {
|
|
132
|
+
opacity: 0.6;
|
|
133
|
+
cursor: not-allowed;
|
|
134
|
+
transform: none;
|
|
135
|
+
}
|
|
136
|
+
|
|
78
137
|
.status-area {
|
|
79
138
|
margin-top: 1.2rem;
|
|
80
139
|
padding: 1rem;
|
|
@@ -100,34 +159,51 @@
|
|
|
100
159
|
color: #a6e3a1;
|
|
101
160
|
}
|
|
102
161
|
|
|
103
|
-
.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
.collapsed {
|
|
108
|
-
display: none !important;
|
|
162
|
+
.status-msg {
|
|
163
|
+
margin: 0;
|
|
164
|
+
line-height: 1.4;
|
|
165
|
+
word-break: break-word;
|
|
109
166
|
}
|
|
110
167
|
|
|
111
168
|
.result-area {
|
|
112
169
|
margin-top: 1.5rem;
|
|
113
|
-
padding: 1.
|
|
114
|
-
|
|
115
|
-
background: rgba(166, 227, 161, 0.1);
|
|
170
|
+
padding: 1.25rem;
|
|
171
|
+
background: rgba(166, 227, 161, 0.08);
|
|
116
172
|
border: 1px solid #a6e3a1;
|
|
173
|
+
border-radius: 8px;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.result-area.collapsed {
|
|
177
|
+
display: none;
|
|
117
178
|
}
|
|
118
179
|
|
|
119
180
|
.result-area h3 {
|
|
120
181
|
margin-top: 0;
|
|
182
|
+
margin-bottom: 0.5rem;
|
|
121
183
|
color: #a6e3a1;
|
|
184
|
+
font-size: 1.15rem;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.address-label {
|
|
188
|
+
margin-bottom: 0.25rem;
|
|
189
|
+
font-size: 0.85rem;
|
|
190
|
+
color: var(--ibgib-text-secondary, #a6adc8);
|
|
122
191
|
}
|
|
123
192
|
|
|
124
193
|
.keystone-addr {
|
|
125
194
|
display: block;
|
|
126
|
-
|
|
127
|
-
padding: 0.6rem;
|
|
195
|
+
padding: 0.5rem 0.75rem;
|
|
128
196
|
background: #11111b;
|
|
129
197
|
border-radius: 6px;
|
|
130
198
|
font-family: monospace;
|
|
131
199
|
font-size: 0.85rem;
|
|
132
|
-
color: #
|
|
200
|
+
color: #f9e2af;
|
|
201
|
+
word-break: break-all;
|
|
202
|
+
margin-bottom: 0.75rem;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.warning {
|
|
206
|
+
margin: 0;
|
|
207
|
+
font-size: 0.85rem;
|
|
208
|
+
color: #a6adc8;
|
|
133
209
|
}
|
|
@@ -1,37 +1,111 @@
|
|
|
1
1
|
<div id="container" class="component-container">
|
|
2
|
-
<h2 id="title">Sign In
|
|
2
|
+
<h2 id="title">Sign In to Device</h2>
|
|
3
3
|
<div class="content">
|
|
4
|
-
<p
|
|
4
|
+
<p id="subtitle" style="margin-bottom: 1.25rem; font-size: 0.95rem; color: #a6adc8;">
|
|
5
|
+
Sign in on this device to link and synchronize your identity.
|
|
6
|
+
</p>
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
<!-- ----------------------------------------------------------------- -->
|
|
9
|
+
<!-- Surface 1: Main Sign In Entry Surface -->
|
|
10
|
+
<!-- ----------------------------------------------------------------- -->
|
|
11
|
+
<div id="surface-entry">
|
|
12
|
+
<div class="sso-options"
|
|
13
|
+
style="display: flex; flex-direction: column; gap: 0.6rem; margin-bottom: 1.25rem;">
|
|
14
|
+
<button id="btn-google-sso" class="sso-btn google-btn">
|
|
15
|
+
<svg width="18" height="18" viewBox="0 0 24 24">
|
|
16
|
+
<path fill="#4285F4"
|
|
17
|
+
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" />
|
|
18
|
+
<path fill="#34A853"
|
|
19
|
+
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" />
|
|
20
|
+
<path fill="#FBBC05"
|
|
21
|
+
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.06H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.94l2.85-2.22.81-.63z" />
|
|
22
|
+
<path fill="#EA4335"
|
|
23
|
+
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.06l3.66 2.84c.87-2.6 3.3-4.52 6.16-4.52z" />
|
|
24
|
+
</svg>
|
|
25
|
+
Continue with Google
|
|
26
|
+
</button>
|
|
27
|
+
<button id="btn-github-sso" class="sso-btn github-btn">
|
|
28
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
|
29
|
+
<path
|
|
30
|
+
d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
|
|
31
|
+
</svg>
|
|
32
|
+
Continue with GitHub
|
|
33
|
+
</button>
|
|
34
|
+
</div>
|
|
10
35
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class="text-input">
|
|
15
|
-
</div>
|
|
36
|
+
<div class="divider">
|
|
37
|
+
<span>OR SIGN IN WITH EMAIL</span>
|
|
38
|
+
</div>
|
|
16
39
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
40
|
+
<div class="input-group">
|
|
41
|
+
<label for="input-identifier">Email</label>
|
|
42
|
+
<input type="email" id="input-identifier" placeholder="Enter your registered email..."
|
|
43
|
+
class="text-input">
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div class="action-row" style="margin-top: 0.75rem;">
|
|
47
|
+
<button id="btn-continue-email" class="action-btn">Continue with Email</button>
|
|
48
|
+
</div>
|
|
21
49
|
</div>
|
|
22
50
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
51
|
+
<!-- ----------------------------------------------------------------- -->
|
|
52
|
+
<!-- Surface 2 (Conditional): 6-Digit Email OTP Verification -->
|
|
53
|
+
<!-- ----------------------------------------------------------------- -->
|
|
54
|
+
<div id="section-code-verification" class="input-group hidden"
|
|
55
|
+
style="margin-top: 1.25rem; padding: 1rem; border: 1px dashed #89b4fa; border-radius: 8px; background: rgba(137, 180, 250, 0.05);">
|
|
56
|
+
<label for="input-code" style="font-weight: bold; color: #89b4fa;">✉️ Enter 6-Digit Email Verification
|
|
57
|
+
Code</label>
|
|
58
|
+
<p style="font-size: 0.82rem; color: #a6adc8; margin-top: 0.25rem; margin-bottom: 0.75rem;">Check your email
|
|
59
|
+
for your 6-digit verification code:</p>
|
|
60
|
+
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;">
|
|
61
|
+
<input type="text" id="input-code" maxlength="6" placeholder="123456" class="text-input"
|
|
62
|
+
style="font-family: monospace; font-size: 1.2rem; letter-spacing: 4px; text-align: center; max-width: 160px;">
|
|
63
|
+
<button id="btn-verify-code" class="action-btn" style="white-space: nowrap; width: auto;">Verify
|
|
64
|
+
Code</button>
|
|
65
|
+
</div>
|
|
28
66
|
</div>
|
|
29
67
|
|
|
30
|
-
|
|
31
|
-
|
|
68
|
+
<!-- ----------------------------------------------------------------- -->
|
|
69
|
+
<!-- Surface 3 (Conditional): Sovereign Primary Password Entry -->
|
|
70
|
+
<!-- ----------------------------------------------------------------- -->
|
|
71
|
+
<div id="section-sovereign-password" class="input-group hidden"
|
|
72
|
+
style="margin-top: 1.25rem; padding: 1rem; border: 1px dashed #f59e0b; border-radius: 8px; background: rgba(245, 158, 11, 0.05);">
|
|
73
|
+
<label for="input-password" style="font-weight: bold; color: #f59e0b;">🔐 Primary Password
|
|
74
|
+
Required</label>
|
|
75
|
+
<p style="font-size: 0.82rem; color: #a6adc8; margin-top: 0.25rem; margin-bottom: 0.75rem;">This sovereign
|
|
76
|
+
identity requires your master password to authorize this device:</p>
|
|
77
|
+
<div class="input-group">
|
|
78
|
+
<input type="password" id="input-password" placeholder="Enter your master password..."
|
|
79
|
+
class="secret-input text-input">
|
|
80
|
+
</div>
|
|
81
|
+
<div class="action-row" style="margin-top: 0.5rem;">
|
|
82
|
+
<button id="btn-authorize-password" class="action-btn">Authorize & Pair Device</button>
|
|
83
|
+
</div>
|
|
32
84
|
</div>
|
|
33
85
|
|
|
34
|
-
|
|
86
|
+
<!-- ----------------------------------------------------------------- -->
|
|
87
|
+
<!-- Advanced Settings (Collapsible Device Tag & Active Toggle) -->
|
|
88
|
+
<!-- ----------------------------------------------------------------- -->
|
|
89
|
+
<details id="details-device-options"
|
|
90
|
+
style="margin-top: 1.25rem; font-size: 0.85rem; color: #a6adc8; cursor: pointer;">
|
|
91
|
+
<summary style="user-select: none; font-weight: 500;">⚙️ Advanced Device Options</summary>
|
|
92
|
+
<div style="padding-top: 0.75rem;">
|
|
93
|
+
<div class="input-group" style="margin-bottom: 0.75rem;">
|
|
94
|
+
<label for="input-device-tag" style="font-size: 0.82rem;">Device Name / Tag</label>
|
|
95
|
+
<input type="text" id="input-device-tag" placeholder="e.g. laptop, phone, living-room..."
|
|
96
|
+
class="text-input" style="font-size: 0.88rem; padding: 0.55rem 0.8rem;">
|
|
97
|
+
</div>
|
|
98
|
+
<div class="checkbox-group" style="display: flex; align-items: center; gap: 0.5rem;">
|
|
99
|
+
<input type="checkbox" id="checkbox-make-active" checked
|
|
100
|
+
style="cursor: pointer; width: 0.95rem; height: 0.95rem;">
|
|
101
|
+
<label for="checkbox-make-active"
|
|
102
|
+
style="cursor: pointer; font-size: 0.82rem; user-select: none;">Set as active identity on this
|
|
103
|
+
browser</label>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</details>
|
|
107
|
+
|
|
108
|
+
<div class="switch-workflow" style="margin-top: 1.5rem; text-align: center; font-size: 0.9rem; color: #a6adc8;">
|
|
35
109
|
Need a new identity? <button id="btn-switch-create-account" class="link-btn"
|
|
36
110
|
style="background: none; border: none; color: #89b4fa; font-weight: 600; cursor: pointer; text-decoration: underline; padding: 0; font-size: 0.9rem;">Create
|
|
37
111
|
Account</button>
|