decane-connect-kit 2.30.0 → 2.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +106 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -16
- package/dist/index.d.ts +17 -16
- package/dist/index.js +106 -117
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -94,11 +94,8 @@ interface DecaneConnectConfig {
|
|
|
94
94
|
chains: Chain[];
|
|
95
95
|
authMethods: Array<'google' | 'email' | 'phone' | 'kingschat' | 'x'>;
|
|
96
96
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* enough to establish identity. Widen only if the app actually calls
|
|
100
|
-
* KingsChat APIs on the user's behalf — the consent screen lists every scope,
|
|
101
|
-
* so asking for more than you use costs conversions.
|
|
97
|
+
* @deprecated Ignored since 2.31.0. KingsChat scopes are now set on the
|
|
98
|
+
* KingsChat project in its developer portal, not per login request.
|
|
102
99
|
*/
|
|
103
100
|
kingsChatScopes?: string[];
|
|
104
101
|
apiBaseUrl?: string;
|
|
@@ -765,9 +762,14 @@ declare class ApiClient {
|
|
|
765
762
|
authorizationUrl: string;
|
|
766
763
|
}>;
|
|
767
764
|
/**
|
|
768
|
-
* Exchange a KingsChat
|
|
769
|
-
*
|
|
770
|
-
*
|
|
765
|
+
* Exchange a KingsChat authorization code for a Decane session. The backend
|
|
766
|
+
* trades it with KingsChat for an access token, verifies that token (it must
|
|
767
|
+
* have been issued to this project's KingsChat app), and never stores it.
|
|
768
|
+
*/
|
|
769
|
+
authKingsChatCode(code: string): Promise<AuthResult>;
|
|
770
|
+
/**
|
|
771
|
+
* Exchange a KingsChat access token the app already holds for a Decane
|
|
772
|
+
* session. Verified the same way as a code's token, and never stored.
|
|
771
773
|
*/
|
|
772
774
|
authKingsChat(accessToken: string): Promise<AuthResult>;
|
|
773
775
|
authEmailStart(email: string): Promise<void>;
|
|
@@ -989,8 +991,8 @@ interface SocialConfig {
|
|
|
989
991
|
apiKey: string;
|
|
990
992
|
authMethods?: ("google" | "email" | "phone" | "kingschat" | "x")[];
|
|
991
993
|
/**
|
|
992
|
-
*
|
|
993
|
-
*
|
|
994
|
+
* @deprecated Ignored since 2.31.0. KingsChat scopes are now set on the
|
|
995
|
+
* KingsChat project in its developer portal, not per login request.
|
|
994
996
|
*/
|
|
995
997
|
kingsChatScopes?: string[];
|
|
996
998
|
promptPin?: () => Promise<string>;
|
|
@@ -1194,19 +1196,18 @@ declare function startEmailAuth(api: ApiClient, email: string): Promise<void>;
|
|
|
1194
1196
|
*/
|
|
1195
1197
|
declare function verifyEmailCode(api: ApiClient, email: string, code: string): Promise<AuthResult>;
|
|
1196
1198
|
|
|
1199
|
+
/** @deprecated KingsChat no longer issues tokens to a popup. */
|
|
1197
1200
|
interface KingsChatTokens {
|
|
1198
1201
|
accessToken: string;
|
|
1199
1202
|
refreshToken?: string;
|
|
1200
1203
|
expiresInMillis?: number;
|
|
1201
1204
|
}
|
|
1202
1205
|
/**
|
|
1203
|
-
*
|
|
1204
|
-
*
|
|
1205
|
-
*
|
|
1206
|
-
* blockers reject a window.open that happens after an await, which is why the
|
|
1207
|
-
* clientId is fetched by the caller *before* this runs.
|
|
1206
|
+
* @deprecated Since 2.31.0. KingsChat retired the popup login; use
|
|
1207
|
+
* `connectWithKingsChat()`, which redirects to KingsChat and finishes on the
|
|
1208
|
+
* page registered as the KingsChat application's Redirect URL.
|
|
1208
1209
|
*/
|
|
1209
|
-
declare function loginWithKingsChatPopup(
|
|
1210
|
+
declare function loginWithKingsChatPopup(_opts?: {
|
|
1210
1211
|
clientId: string;
|
|
1211
1212
|
scopes?: string[];
|
|
1212
1213
|
timeoutMs?: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -94,11 +94,8 @@ interface DecaneConnectConfig {
|
|
|
94
94
|
chains: Chain[];
|
|
95
95
|
authMethods: Array<'google' | 'email' | 'phone' | 'kingschat' | 'x'>;
|
|
96
96
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* enough to establish identity. Widen only if the app actually calls
|
|
100
|
-
* KingsChat APIs on the user's behalf — the consent screen lists every scope,
|
|
101
|
-
* so asking for more than you use costs conversions.
|
|
97
|
+
* @deprecated Ignored since 2.31.0. KingsChat scopes are now set on the
|
|
98
|
+
* KingsChat project in its developer portal, not per login request.
|
|
102
99
|
*/
|
|
103
100
|
kingsChatScopes?: string[];
|
|
104
101
|
apiBaseUrl?: string;
|
|
@@ -765,9 +762,14 @@ declare class ApiClient {
|
|
|
765
762
|
authorizationUrl: string;
|
|
766
763
|
}>;
|
|
767
764
|
/**
|
|
768
|
-
* Exchange a KingsChat
|
|
769
|
-
*
|
|
770
|
-
*
|
|
765
|
+
* Exchange a KingsChat authorization code for a Decane session. The backend
|
|
766
|
+
* trades it with KingsChat for an access token, verifies that token (it must
|
|
767
|
+
* have been issued to this project's KingsChat app), and never stores it.
|
|
768
|
+
*/
|
|
769
|
+
authKingsChatCode(code: string): Promise<AuthResult>;
|
|
770
|
+
/**
|
|
771
|
+
* Exchange a KingsChat access token the app already holds for a Decane
|
|
772
|
+
* session. Verified the same way as a code's token, and never stored.
|
|
771
773
|
*/
|
|
772
774
|
authKingsChat(accessToken: string): Promise<AuthResult>;
|
|
773
775
|
authEmailStart(email: string): Promise<void>;
|
|
@@ -989,8 +991,8 @@ interface SocialConfig {
|
|
|
989
991
|
apiKey: string;
|
|
990
992
|
authMethods?: ("google" | "email" | "phone" | "kingschat" | "x")[];
|
|
991
993
|
/**
|
|
992
|
-
*
|
|
993
|
-
*
|
|
994
|
+
* @deprecated Ignored since 2.31.0. KingsChat scopes are now set on the
|
|
995
|
+
* KingsChat project in its developer portal, not per login request.
|
|
994
996
|
*/
|
|
995
997
|
kingsChatScopes?: string[];
|
|
996
998
|
promptPin?: () => Promise<string>;
|
|
@@ -1194,19 +1196,18 @@ declare function startEmailAuth(api: ApiClient, email: string): Promise<void>;
|
|
|
1194
1196
|
*/
|
|
1195
1197
|
declare function verifyEmailCode(api: ApiClient, email: string, code: string): Promise<AuthResult>;
|
|
1196
1198
|
|
|
1199
|
+
/** @deprecated KingsChat no longer issues tokens to a popup. */
|
|
1197
1200
|
interface KingsChatTokens {
|
|
1198
1201
|
accessToken: string;
|
|
1199
1202
|
refreshToken?: string;
|
|
1200
1203
|
expiresInMillis?: number;
|
|
1201
1204
|
}
|
|
1202
1205
|
/**
|
|
1203
|
-
*
|
|
1204
|
-
*
|
|
1205
|
-
*
|
|
1206
|
-
* blockers reject a window.open that happens after an await, which is why the
|
|
1207
|
-
* clientId is fetched by the caller *before* this runs.
|
|
1206
|
+
* @deprecated Since 2.31.0. KingsChat retired the popup login; use
|
|
1207
|
+
* `connectWithKingsChat()`, which redirects to KingsChat and finishes on the
|
|
1208
|
+
* page registered as the KingsChat application's Redirect URL.
|
|
1208
1209
|
*/
|
|
1209
|
-
declare function loginWithKingsChatPopup(
|
|
1210
|
+
declare function loginWithKingsChatPopup(_opts?: {
|
|
1210
1211
|
clientId: string;
|
|
1211
1212
|
scopes?: string[];
|
|
1212
1213
|
timeoutMs?: number;
|
package/dist/index.js
CHANGED
|
@@ -2106,9 +2106,19 @@ var ApiClient = class {
|
|
|
2106
2106
|
return this._request("/auth/kingschat/config", { method: "GET" });
|
|
2107
2107
|
}
|
|
2108
2108
|
/**
|
|
2109
|
-
* Exchange a KingsChat
|
|
2110
|
-
*
|
|
2111
|
-
*
|
|
2109
|
+
* Exchange a KingsChat authorization code for a Decane session. The backend
|
|
2110
|
+
* trades it with KingsChat for an access token, verifies that token (it must
|
|
2111
|
+
* have been issued to this project's KingsChat app), and never stores it.
|
|
2112
|
+
*/
|
|
2113
|
+
async authKingsChatCode(code) {
|
|
2114
|
+
return this._request("/auth/kingschat/code", {
|
|
2115
|
+
method: "POST",
|
|
2116
|
+
body: JSON.stringify({ code })
|
|
2117
|
+
});
|
|
2118
|
+
}
|
|
2119
|
+
/**
|
|
2120
|
+
* Exchange a KingsChat access token the app already holds for a Decane
|
|
2121
|
+
* session. Verified the same way as a code's token, and never stored.
|
|
2112
2122
|
*/
|
|
2113
2123
|
async authKingsChat(accessToken) {
|
|
2114
2124
|
return this._request("/auth/kingschat", {
|
|
@@ -2463,15 +2473,16 @@ init_errors();
|
|
|
2463
2473
|
|
|
2464
2474
|
// ../infra/tee-measurement.json
|
|
2465
2475
|
var tee_measurement_default = {
|
|
2466
|
-
signerVersion: "0.2.
|
|
2476
|
+
signerVersion: "0.2.13",
|
|
2467
2477
|
acceptedMrtd: [
|
|
2478
|
+
'["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","5538de3ba0930ae65c7c2b99346742a67dff7891545d91e8d74006d215fff256db91998eaff1c3dcca679a89df566700"]',
|
|
2468
2479
|
'["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","a76a81147b7f733cb400914c8791ec149bb3a31f3727b1c1e12ec22e9c1a183b6e617e78636dca37075f5a695afd46c2"]',
|
|
2469
2480
|
'["68102e7b524af310f7b7d426ce75481e36c40f5d513a9009c046e9d37e31551f0134d954b496a3357fd61d03f07ffe96","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","a76a81147b7f733cb400914c8791ec149bb3a31f3727b1c1e12ec22e9c1a183b6e617e78636dca37075f5a695afd46c2"]'
|
|
2470
2481
|
],
|
|
2471
|
-
primaryMrtd: '["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","
|
|
2472
|
-
capturedAt: "2026-09-
|
|
2482
|
+
primaryMrtd: '["fb14dc139f33d6fcf474bc8332cac001259fb31cfbcb6b34d4ceeb552a2c4466884a0cbde45ad98a05c5c060c23ad65a","07e6f51aa763abfe75c3ddfbf4f425fe3f0ceff66d807a75e049303dce9addf68e7218729bd419638af63a370f65878c","a2a58c9a959a4fa44bd6da0c97a2270c051faf12084cfe91ae900e4fdff6cdd4f69a82005e04ee920f231497894d677f","5538de3ba0930ae65c7c2b99346742a67dff7891545d91e8d74006d215fff256db91998eaff1c3dcca679a89df566700"]',
|
|
2483
|
+
capturedAt: "2026-09-30T18:36:41Z",
|
|
2473
2484
|
tcbStatus: "UpToDate",
|
|
2474
|
-
_note: "Captured from the live CVM
|
|
2485
|
+
_note: "Captured from the live CVM app_4405742df08d after the 2026-09-30 restart that shipped signer 0.2.13 (Path A, compose byte-identical). RTMR3 changed; RTMR0-2 did not. The app-id and compose-hash events in RTMR3 are unchanged, so the move is in the platform-recorded events (instance, KMS or OS image) underneath the CVM, not in what we deploy. The quote verified against Intel (TCB UpToDate) and bound a fresh nonce before this was accepted, with the operator's approval. Sealing verified intact (/signer/sealing-status ready). Both previous measurements are retained so a rollback still verifies. measurement is the JSON.stringify(replayRtmrs()) output, compared as an opaque string."
|
|
2475
2486
|
};
|
|
2476
2487
|
|
|
2477
2488
|
// src/social/attestation.ts
|
|
@@ -3287,75 +3298,47 @@ async function verifyPhoneCode(api, phone, code) {
|
|
|
3287
3298
|
|
|
3288
3299
|
// src/social/auth/kingschat.ts
|
|
3289
3300
|
init_errors();
|
|
3290
|
-
var
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
const
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
}
|
|
3301
|
+
var KINGSCHAT_LOGIN_URL = "https://accounts.kingschat.online/log-in";
|
|
3302
|
+
var STATE_KEY = "decane:kingschat_state";
|
|
3303
|
+
function randomState() {
|
|
3304
|
+
const bytes = new Uint8Array(16);
|
|
3305
|
+
crypto.getRandomValues(bytes);
|
|
3306
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
3307
|
+
}
|
|
3308
|
+
function storage2() {
|
|
3309
|
+
try {
|
|
3310
|
+
return window.sessionStorage ?? null;
|
|
3311
|
+
} catch {
|
|
3312
|
+
return null;
|
|
3313
|
+
}
|
|
3303
3314
|
}
|
|
3304
|
-
function
|
|
3305
|
-
const
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
url
|
|
3315
|
+
function beginKingsChatLogin(clientId, opts = {}) {
|
|
3316
|
+
const store = storage2();
|
|
3317
|
+
if (!store) throw new Error("KingsChat sign-in needs sessionStorage, which this browser is blocking");
|
|
3318
|
+
const state = randomState();
|
|
3319
|
+
store.setItem(STATE_KEY, state);
|
|
3320
|
+
const url = new URL(KINGSCHAT_LOGIN_URL);
|
|
3321
|
+
url.searchParams.set("clientId", clientId);
|
|
3322
|
+
url.searchParams.set("origin", state);
|
|
3323
|
+
if (opts.forceLogin) url.searchParams.set("forceLogin", "true");
|
|
3310
3324
|
return url.toString();
|
|
3311
3325
|
}
|
|
3312
|
-
function
|
|
3313
|
-
const
|
|
3314
|
-
const
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3326
|
+
function readKingsChatReturn(search) {
|
|
3327
|
+
const params = new URLSearchParams(search);
|
|
3328
|
+
const code = params.get("code");
|
|
3329
|
+
const origin = params.get("origin");
|
|
3330
|
+
if (!code || !origin) return null;
|
|
3331
|
+
const expected = storage2()?.getItem(STATE_KEY);
|
|
3332
|
+
if (!expected || expected !== origin) return null;
|
|
3333
|
+
return { code };
|
|
3334
|
+
}
|
|
3335
|
+
function clearKingsChatState() {
|
|
3336
|
+
storage2()?.removeItem(STATE_KEY);
|
|
3337
|
+
}
|
|
3338
|
+
async function loginWithKingsChatPopup(_opts) {
|
|
3339
|
+
throw new CallSequenceError(
|
|
3340
|
+
"loginWithKingsChatPopup was removed in 2.31.0: KingsChat retired its popup login. Use connectWithKingsChat(), which redirects to KingsChat and finishes on your registered Redirect URL."
|
|
3318
3341
|
);
|
|
3319
|
-
if (!popup) {
|
|
3320
|
-
throw new CallSequenceError(
|
|
3321
|
-
"The KingsChat sign-in popup was blocked. Call connectWithKingsChat() directly from a click handler."
|
|
3322
|
-
);
|
|
3323
|
-
}
|
|
3324
|
-
return new Promise((resolve, reject) => {
|
|
3325
|
-
let settled = false;
|
|
3326
|
-
const finish = (fn) => {
|
|
3327
|
-
if (settled) return;
|
|
3328
|
-
settled = true;
|
|
3329
|
-
window.removeEventListener("message", onMessage);
|
|
3330
|
-
clearInterval(closedPoll);
|
|
3331
|
-
clearTimeout(timeout);
|
|
3332
|
-
try {
|
|
3333
|
-
popup.close();
|
|
3334
|
-
} catch {
|
|
3335
|
-
}
|
|
3336
|
-
fn();
|
|
3337
|
-
};
|
|
3338
|
-
const onMessage = (event) => {
|
|
3339
|
-
if (event.origin !== KINGSCHAT_AUTH_ORIGIN) return;
|
|
3340
|
-
const data = event.data;
|
|
3341
|
-
if (data && typeof data === "object" && data.error) {
|
|
3342
|
-
finish(() => reject(new CallSequenceError(`KingsChat sign-in failed: ${String(data.error)}`)));
|
|
3343
|
-
return;
|
|
3344
|
-
}
|
|
3345
|
-
const tokens2 = readTokens(event.data);
|
|
3346
|
-
if (tokens2) finish(() => resolve(tokens2));
|
|
3347
|
-
};
|
|
3348
|
-
window.addEventListener("message", onMessage);
|
|
3349
|
-
const closedPoll = setInterval(() => {
|
|
3350
|
-
if (popup.closed) finish(() => reject(new UserCancelledError()));
|
|
3351
|
-
}, 400);
|
|
3352
|
-
const timeout = setTimeout(
|
|
3353
|
-
() => finish(
|
|
3354
|
-
() => reject(new CallSequenceError("KingsChat sign-in timed out before returning a token."))
|
|
3355
|
-
),
|
|
3356
|
-
opts.timeoutMs ?? 5 * 6e4
|
|
3357
|
-
);
|
|
3358
|
-
});
|
|
3359
3342
|
}
|
|
3360
3343
|
|
|
3361
3344
|
// src/social/signers/evm.ts
|
|
@@ -3960,7 +3943,7 @@ async function createDecaneConnect(config) {
|
|
|
3960
3943
|
const teeUrl = config.teeUrl ?? DEFAULT_TEE_URL;
|
|
3961
3944
|
const events = new EventEmitter();
|
|
3962
3945
|
const api = new ApiClient(baseUrl, config.apiKey, config.appId);
|
|
3963
|
-
const
|
|
3946
|
+
const storage3 = new Storage();
|
|
3964
3947
|
const session = new SessionManager(
|
|
3965
3948
|
events,
|
|
3966
3949
|
durationMs,
|
|
@@ -4150,7 +4133,7 @@ async function createDecaneConnect(config) {
|
|
|
4150
4133
|
}
|
|
4151
4134
|
async function attachUnlockToken(userId, jwt, tokenKey) {
|
|
4152
4135
|
try {
|
|
4153
|
-
const stored = await
|
|
4136
|
+
const stored = await storage3.getShare(userId);
|
|
4154
4137
|
if (!stored) return;
|
|
4155
4138
|
const { token } = await api.issueUnlockToken(jwt);
|
|
4156
4139
|
const unlockTokenIv = generateRandomBytes(12);
|
|
@@ -4159,13 +4142,13 @@ async function createDecaneConnect(config) {
|
|
|
4159
4142
|
new TextEncoder().encode(token),
|
|
4160
4143
|
unlockTokenIv
|
|
4161
4144
|
);
|
|
4162
|
-
await
|
|
4145
|
+
await storage3.putShare({ ...stored, wrappedUnlockToken, unlockTokenIv });
|
|
4163
4146
|
} catch (e) {
|
|
4164
4147
|
console.warn("decane-connect-kit: could not store an unlock token for this device.", e);
|
|
4165
4148
|
}
|
|
4166
4149
|
}
|
|
4167
4150
|
async function persistRotatedDeviceShare(userId, deviceShare) {
|
|
4168
|
-
const stored = await
|
|
4151
|
+
const stored = await storage3.getShare(userId);
|
|
4169
4152
|
if (!stored) {
|
|
4170
4153
|
throw new CallSequenceError(
|
|
4171
4154
|
"Share set was rotated but this device has no stored share to update. Recover the wallet on this device before rotating."
|
|
@@ -4174,21 +4157,21 @@ async function createDecaneConnect(config) {
|
|
|
4174
4157
|
const wrappingKey = await getWrappingKey(stored);
|
|
4175
4158
|
const iv = generateRandomBytes(12);
|
|
4176
4159
|
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
4177
|
-
await
|
|
4160
|
+
await storage3.putShare({ ...stored, wrappedDeviceShare, iv });
|
|
4178
4161
|
}
|
|
4179
4162
|
function hasPasswordUnlock(stored) {
|
|
4180
4163
|
return !!stored && stored.unlockMethod === "password" && !!stored.passwordSalt && !!stored.wrappedUnlockToken && !!stored.unlockTokenIv;
|
|
4181
4164
|
}
|
|
4182
4165
|
async function passwordDeviceCandidate() {
|
|
4183
4166
|
if (persisted?.userId) {
|
|
4184
|
-
const stored = await
|
|
4167
|
+
const stored = await storage3.getShare(persisted.userId);
|
|
4185
4168
|
return hasPasswordUnlock(stored) ? stored : null;
|
|
4186
4169
|
}
|
|
4187
4170
|
if (lastUserId) {
|
|
4188
|
-
const stored = await
|
|
4171
|
+
const stored = await storage3.getShare(lastUserId);
|
|
4189
4172
|
return hasPasswordUnlock(stored) ? stored : null;
|
|
4190
4173
|
}
|
|
4191
|
-
const candidates = (await
|
|
4174
|
+
const candidates = (await storage3.listShares()).filter(hasPasswordUnlock);
|
|
4192
4175
|
return candidates.length === 1 ? candidates[0] : null;
|
|
4193
4176
|
}
|
|
4194
4177
|
async function getWrappingKey(stored) {
|
|
@@ -4216,7 +4199,7 @@ async function createDecaneConnect(config) {
|
|
|
4216
4199
|
let credentialId;
|
|
4217
4200
|
let prfOutput;
|
|
4218
4201
|
try {
|
|
4219
|
-
const label = (await
|
|
4202
|
+
const label = (await storage3.getShare(userId))?.profile?.email;
|
|
4220
4203
|
({ credentialId, prfOutput } = await registerWebAuthnCredential({
|
|
4221
4204
|
api,
|
|
4222
4205
|
jwt,
|
|
@@ -4248,8 +4231,8 @@ async function createDecaneConnect(config) {
|
|
|
4248
4231
|
const wrappingKey = await importAesKey(prfOutput);
|
|
4249
4232
|
const iv = generateRandomBytes(12);
|
|
4250
4233
|
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
4251
|
-
const existing = await
|
|
4252
|
-
await
|
|
4234
|
+
const existing = await storage3.getShare(userId);
|
|
4235
|
+
await storage3.putShare({
|
|
4253
4236
|
...existing,
|
|
4254
4237
|
userId,
|
|
4255
4238
|
wrappedDeviceShare,
|
|
@@ -4333,7 +4316,7 @@ async function createDecaneConnect(config) {
|
|
|
4333
4316
|
const { wrappingKey, tokenKey, meta } = await passwordSetup();
|
|
4334
4317
|
const iv = generateRandomBytes(12);
|
|
4335
4318
|
const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
|
|
4336
|
-
await
|
|
4319
|
+
await storage3.putShare({
|
|
4337
4320
|
userId,
|
|
4338
4321
|
wrappedDeviceShare,
|
|
4339
4322
|
iv,
|
|
@@ -4407,7 +4390,7 @@ async function createDecaneConnect(config) {
|
|
|
4407
4390
|
shareSet.device,
|
|
4408
4391
|
iv
|
|
4409
4392
|
);
|
|
4410
|
-
await
|
|
4393
|
+
await storage3.putShare({
|
|
4411
4394
|
userId,
|
|
4412
4395
|
wrappedDeviceShare,
|
|
4413
4396
|
iv,
|
|
@@ -4447,7 +4430,7 @@ async function createDecaneConnect(config) {
|
|
|
4447
4430
|
zeroize(shareSet.server);
|
|
4448
4431
|
zeroize(shareSet.device);
|
|
4449
4432
|
zeroize(shareSet.recovery);
|
|
4450
|
-
if (wrappingKey) await
|
|
4433
|
+
if (wrappingKey) await storage3.deleteShare(userId).catch(() => {
|
|
4451
4434
|
});
|
|
4452
4435
|
events.emit("wallet-unlocking");
|
|
4453
4436
|
return returningUserUnlock(userId, jwt);
|
|
@@ -4460,7 +4443,7 @@ async function createDecaneConnect(config) {
|
|
|
4460
4443
|
} catch (e) {
|
|
4461
4444
|
zeroize(shareSet.device);
|
|
4462
4445
|
zeroize(shareSet.recovery);
|
|
4463
|
-
if (wrappingKey) await
|
|
4446
|
+
if (wrappingKey) await storage3.deleteShare(userId).catch(() => {
|
|
4464
4447
|
});
|
|
4465
4448
|
throw e;
|
|
4466
4449
|
}
|
|
@@ -4536,7 +4519,7 @@ async function createDecaneConnect(config) {
|
|
|
4536
4519
|
async function returningUserUnlock(userId, jwt) {
|
|
4537
4520
|
const rawAddresses = await api.getAddresses(jwt);
|
|
4538
4521
|
const recordedAddresses = rawAddresses.evm && rawAddresses.solana ? { evm: rawAddresses.evm, solana: rawAddresses.solana, tron: rawAddresses.tron ?? evmToTronAddress2(rawAddresses.evm) } : null;
|
|
4539
|
-
const stored = await
|
|
4522
|
+
const stored = await storage3.getShare(userId);
|
|
4540
4523
|
let deviceShare;
|
|
4541
4524
|
let joinedViaPasskeySync = false;
|
|
4542
4525
|
let migrating = false;
|
|
@@ -4588,7 +4571,7 @@ async function createDecaneConnect(config) {
|
|
|
4588
4571
|
rpId,
|
|
4589
4572
|
teeUrl,
|
|
4590
4573
|
api,
|
|
4591
|
-
storage:
|
|
4574
|
+
storage: storage3,
|
|
4592
4575
|
onProgress: config.onRecoveryProgress,
|
|
4593
4576
|
promptForRecoveryFile: config.promptForRecoveryFile,
|
|
4594
4577
|
onNewRecoveryShare: async (recoveryShare, shareSetVersion) => {
|
|
@@ -4602,7 +4585,7 @@ async function createDecaneConnect(config) {
|
|
|
4602
4585
|
});
|
|
4603
4586
|
deviceShare = recovered.deviceShare;
|
|
4604
4587
|
joinedViaPasskeySync = recovered.flow === "passkey-sync";
|
|
4605
|
-
if (!identityTier && !await
|
|
4588
|
+
if (!identityTier && !await storage3.getShare(userId) && isPasskeySupported()) {
|
|
4606
4589
|
await registerPasskeyAndBackup(userId, jwt, deviceShare);
|
|
4607
4590
|
}
|
|
4608
4591
|
} else {
|
|
@@ -4631,7 +4614,7 @@ async function createDecaneConnect(config) {
|
|
|
4631
4614
|
} catch (e) {
|
|
4632
4615
|
if (e instanceof StaleDeviceShareError) {
|
|
4633
4616
|
zeroize(deviceShare);
|
|
4634
|
-
await
|
|
4617
|
+
await storage3.deleteShare(userId);
|
|
4635
4618
|
if (stored) return returningUserUnlock(userId, jwt);
|
|
4636
4619
|
} else {
|
|
4637
4620
|
zeroize(deviceShare);
|
|
@@ -4659,7 +4642,7 @@ async function createDecaneConnect(config) {
|
|
|
4659
4642
|
const result = await tee.rotateShares(jwt);
|
|
4660
4643
|
try {
|
|
4661
4644
|
await persistRotatedDeviceShare(userId, result.deviceShare);
|
|
4662
|
-
const refreshed = await
|
|
4645
|
+
const refreshed = await storage3.getShare(userId);
|
|
4663
4646
|
if (refreshed?.unlockMethod === "passkey" && refreshed.credentialId && refreshed.prfSalt) {
|
|
4664
4647
|
await api.putRecoveryBackup(jwt, {
|
|
4665
4648
|
wrappedDeviceShare: refreshed.wrappedDeviceShare,
|
|
@@ -4692,13 +4675,13 @@ async function createDecaneConnect(config) {
|
|
|
4692
4675
|
}
|
|
4693
4676
|
async function passkeyLoginCandidate() {
|
|
4694
4677
|
if (!persisted?.userId || !isPasskeySupported()) return null;
|
|
4695
|
-
return usablePasskeyShare(await
|
|
4678
|
+
return usablePasskeyShare(await storage3.getShare(persisted.userId));
|
|
4696
4679
|
}
|
|
4697
4680
|
async function passkeyDeviceCandidate() {
|
|
4698
4681
|
if (!isPasskeySupported()) return null;
|
|
4699
4682
|
if (persisted?.userId) return passkeyLoginCandidate();
|
|
4700
|
-
if (lastUserId) return usablePasskeyShare(await
|
|
4701
|
-
const candidates = (await
|
|
4683
|
+
if (lastUserId) return usablePasskeyShare(await storage3.getShare(lastUserId));
|
|
4684
|
+
const candidates = (await storage3.listShares()).map(usablePasskeyShare).filter(Boolean);
|
|
4702
4685
|
return candidates.length === 1 ? candidates[0] : null;
|
|
4703
4686
|
}
|
|
4704
4687
|
async function canOfferPasskey() {
|
|
@@ -4706,15 +4689,15 @@ async function createDecaneConnect(config) {
|
|
|
4706
4689
|
if (await passkeyDeviceCandidate()) return true;
|
|
4707
4690
|
const known = persisted?.userId ?? lastUserId;
|
|
4708
4691
|
if (!known) return false;
|
|
4709
|
-
return await
|
|
4692
|
+
return await storage3.getShare(known) == null;
|
|
4710
4693
|
}
|
|
4711
4694
|
async function rememberProfile(userId, profile) {
|
|
4712
4695
|
const incoming = Object.fromEntries(Object.entries(profile).filter(([, v]) => v));
|
|
4713
4696
|
if (!Object.keys(incoming).length) return;
|
|
4714
4697
|
try {
|
|
4715
|
-
const stored = await
|
|
4698
|
+
const stored = await storage3.getShare(userId);
|
|
4716
4699
|
if (!stored) return;
|
|
4717
|
-
await
|
|
4700
|
+
await storage3.putShare({ ...stored, profile: { ...stored.profile, ...incoming } });
|
|
4718
4701
|
} catch {
|
|
4719
4702
|
}
|
|
4720
4703
|
}
|
|
@@ -4738,7 +4721,7 @@ async function createDecaneConnect(config) {
|
|
|
4738
4721
|
savePersisted(PERSIST_KEY, persisted);
|
|
4739
4722
|
events.emit("connected");
|
|
4740
4723
|
persistLiveSession();
|
|
4741
|
-
const stored = await
|
|
4724
|
+
const stored = await storage3.getShare(userId);
|
|
4742
4725
|
return {
|
|
4743
4726
|
addresses,
|
|
4744
4727
|
isNewUser: false,
|
|
@@ -4798,6 +4781,11 @@ async function createDecaneConnect(config) {
|
|
|
4798
4781
|
return new Promise(() => {
|
|
4799
4782
|
});
|
|
4800
4783
|
}
|
|
4784
|
+
async function finishKingsChatReturn(code) {
|
|
4785
|
+
clearKingsChatState();
|
|
4786
|
+
window.history.replaceState({}, "", window.location.pathname);
|
|
4787
|
+
return handleAuthResult(await api.authKingsChatCode(code));
|
|
4788
|
+
}
|
|
4801
4789
|
async function handleAuthResult(result) {
|
|
4802
4790
|
let addresses;
|
|
4803
4791
|
if (!result.hasShare) {
|
|
@@ -4860,6 +4848,8 @@ async function createDecaneConnect(config) {
|
|
|
4860
4848
|
* this never starts a flow.
|
|
4861
4849
|
*/
|
|
4862
4850
|
async completeRedirectSignIn() {
|
|
4851
|
+
const pending = readKingsChatReturn(window.location.search);
|
|
4852
|
+
if (pending) return finishKingsChatReturn(pending.code);
|
|
4863
4853
|
const outcome = readRedirectOutcome(window.location.search);
|
|
4864
4854
|
if (outcome.kind === "none") {
|
|
4865
4855
|
throw new CallSequenceError("No sign-in result in the URL to complete");
|
|
@@ -4878,12 +4868,12 @@ async function createDecaneConnect(config) {
|
|
|
4878
4868
|
return handleAuthResult(await api.authGoogleToken(idToken));
|
|
4879
4869
|
},
|
|
4880
4870
|
async connectWithKingsChat() {
|
|
4871
|
+
const pending = readKingsChatReturn(window.location.search);
|
|
4872
|
+
if (pending) return finishKingsChatReturn(pending.code);
|
|
4881
4873
|
const { clientId } = await api.getKingsChatConfig();
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
scopes: config.kingsChatScopes
|
|
4874
|
+
window.location.href = beginKingsChatLogin(clientId);
|
|
4875
|
+
return new Promise(() => {
|
|
4885
4876
|
});
|
|
4886
|
-
return client.connectWithKingsChatToken(tokens2.accessToken);
|
|
4887
4877
|
},
|
|
4888
4878
|
async connectWithKingsChatToken(accessToken) {
|
|
4889
4879
|
if (!accessToken) {
|
|
@@ -4925,7 +4915,7 @@ async function createDecaneConnect(config) {
|
|
|
4925
4915
|
events.emit("connected");
|
|
4926
4916
|
persistLiveSession();
|
|
4927
4917
|
scheduleAccessTokenExpiry(token, claims.expiresAt);
|
|
4928
|
-
const stored = await
|
|
4918
|
+
const stored = await storage3.getShare(claims.userId);
|
|
4929
4919
|
return {
|
|
4930
4920
|
addresses,
|
|
4931
4921
|
isNewUser: false,
|
|
@@ -4961,7 +4951,7 @@ async function createDecaneConnect(config) {
|
|
|
4961
4951
|
const candidate = await passkeyDeviceCandidate();
|
|
4962
4952
|
if (candidate) return passkeyLogin(candidate);
|
|
4963
4953
|
const known = persisted?.userId ?? lastUserId;
|
|
4964
|
-
if (isPasskeySupported() && known && await
|
|
4954
|
+
if (isPasskeySupported() && known && await storage3.getShare(known) == null) {
|
|
4965
4955
|
return passkeyLoginWithoutShare(known);
|
|
4966
4956
|
}
|
|
4967
4957
|
throw new NewDeviceError();
|
|
@@ -4980,7 +4970,7 @@ async function createDecaneConnect(config) {
|
|
|
4980
4970
|
async setUnlockPassword(password) {
|
|
4981
4971
|
if (!auth) throw new ReconnectRequiredError();
|
|
4982
4972
|
validateUnlockPassword(password);
|
|
4983
|
-
const stored = await
|
|
4973
|
+
const stored = await storage3.getShare(auth.userId);
|
|
4984
4974
|
if (!stored) throw new NewDeviceError();
|
|
4985
4975
|
const deviceShare = await aesGcmDecrypt(
|
|
4986
4976
|
await getWrappingKey(stored),
|
|
@@ -4999,7 +4989,7 @@ async function createDecaneConnect(config) {
|
|
|
4999
4989
|
);
|
|
5000
4990
|
const iv = generateRandomBytes(12);
|
|
5001
4991
|
const wrappedDeviceShare = await aesGcmEncrypt(shareKey, deviceShare, iv);
|
|
5002
|
-
await
|
|
4992
|
+
await storage3.putShare({
|
|
5003
4993
|
...stored,
|
|
5004
4994
|
unlockMethod: "password",
|
|
5005
4995
|
wrappedDeviceShare,
|
|
@@ -5079,9 +5069,9 @@ async function createDecaneConnect(config) {
|
|
|
5079
5069
|
async removeUnlockPassword() {
|
|
5080
5070
|
if (!auth) throw new ReconnectRequiredError();
|
|
5081
5071
|
await api.revokeUnlockTokens(auth.jwt);
|
|
5082
|
-
const stored = await
|
|
5072
|
+
const stored = await storage3.getShare(auth.userId);
|
|
5083
5073
|
if (!stored || stored.unlockMethod !== "password") return;
|
|
5084
|
-
await
|
|
5074
|
+
await storage3.putShare({
|
|
5085
5075
|
...stored,
|
|
5086
5076
|
wrappedUnlockToken: void 0,
|
|
5087
5077
|
unlockTokenIv: void 0
|
|
@@ -5089,7 +5079,7 @@ async function createDecaneConnect(config) {
|
|
|
5089
5079
|
},
|
|
5090
5080
|
async hasPasskey() {
|
|
5091
5081
|
if (!auth) return false;
|
|
5092
|
-
const stored = await
|
|
5082
|
+
const stored = await storage3.getShare(auth.userId);
|
|
5093
5083
|
return stored?.unlockMethod === "passkey";
|
|
5094
5084
|
},
|
|
5095
5085
|
async addPasskey() {
|
|
@@ -5100,7 +5090,7 @@ async function createDecaneConnect(config) {
|
|
|
5100
5090
|
}
|
|
5101
5091
|
if (!auth) throw new ReconnectRequiredError();
|
|
5102
5092
|
if (!isPasskeySupported()) throw new WebAuthnNotSupportedError();
|
|
5103
|
-
const stored = await
|
|
5093
|
+
const stored = await storage3.getShare(auth.userId);
|
|
5104
5094
|
if (stored?.unlockMethod === "passkey") return;
|
|
5105
5095
|
if (!stored) {
|
|
5106
5096
|
if (!serverAssistedRecovery) throw new NewDeviceError();
|
|
@@ -5133,12 +5123,12 @@ async function createDecaneConnect(config) {
|
|
|
5133
5123
|
},
|
|
5134
5124
|
async deviceProtected() {
|
|
5135
5125
|
if (!auth) return false;
|
|
5136
|
-
return await
|
|
5126
|
+
return await storage3.getShare(auth.userId) !== null;
|
|
5137
5127
|
},
|
|
5138
5128
|
async protectDevice() {
|
|
5139
5129
|
if (identityTier) return false;
|
|
5140
5130
|
if (!auth) throw new ReconnectRequiredError();
|
|
5141
|
-
if (await
|
|
5131
|
+
if (await storage3.getShare(auth.userId)) return true;
|
|
5142
5132
|
if (!serverAssistedRecovery) throw new NewDeviceError();
|
|
5143
5133
|
const tee = new TeeClient(teeUrl);
|
|
5144
5134
|
const provisioned = await tee.provisionDeviceShare(auth.jwt);
|
|
@@ -5148,7 +5138,7 @@ async function createDecaneConnect(config) {
|
|
|
5148
5138
|
} finally {
|
|
5149
5139
|
zeroize(provisioned);
|
|
5150
5140
|
}
|
|
5151
|
-
return await
|
|
5141
|
+
return await storage3.getShare(auth.userId) !== null;
|
|
5152
5142
|
},
|
|
5153
5143
|
async unlock() {
|
|
5154
5144
|
if (!auth) {
|
|
@@ -6111,7 +6101,6 @@ function SignInView({
|
|
|
6111
6101
|
await onKingsChatSignIn();
|
|
6112
6102
|
} catch (e) {
|
|
6113
6103
|
setError(e.message);
|
|
6114
|
-
} finally {
|
|
6115
6104
|
setKingsChatLoading(false);
|
|
6116
6105
|
}
|
|
6117
6106
|
}
|
|
@@ -6247,7 +6236,7 @@ function SignInView({
|
|
|
6247
6236
|
value: phone,
|
|
6248
6237
|
onChange: (e) => setPhone(e.target.value)
|
|
6249
6238
|
}
|
|
6250
|
-
), /* @__PURE__ */ React2.createElement("button", { type: "submit", className: "dcm-submit", disabled: busy || !phone.trim() }, phoneLoading ? "Sending\u2026" : "Submit")), authMethods.includes("x") && onXSignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-method", onClick: handleX, disabled: busy }, /* @__PURE__ */ React2.createElement(XLogo, { size: 16 }), /* @__PURE__ */ React2.createElement("span", null, xLoading ? "Redirecting to X\u2026" : "X")), authMethods.includes("kingschat") && onKingsChatSignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-method", onClick: handleKingsChat, disabled: busy }, /* @__PURE__ */ React2.createElement(ChatIcon, { size: 18 }), /* @__PURE__ */ React2.createElement("span", null, kingsChatLoading ? "
|
|
6239
|
+
), /* @__PURE__ */ React2.createElement("button", { type: "submit", className: "dcm-submit", disabled: busy || !phone.trim() }, phoneLoading ? "Sending\u2026" : "Submit")), authMethods.includes("x") && onXSignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-method", onClick: handleX, disabled: busy }, /* @__PURE__ */ React2.createElement(XLogo, { size: 16 }), /* @__PURE__ */ React2.createElement("span", null, xLoading ? "Redirecting to X\u2026" : "X")), authMethods.includes("kingschat") && onKingsChatSignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-method", onClick: handleKingsChat, disabled: busy }, /* @__PURE__ */ React2.createElement(ChatIcon, { size: 18 }), /* @__PURE__ */ React2.createElement("span", null, kingsChatLoading ? "Redirecting to KingsChat\u2026" : "KingsChat")), customAuthLabel && onCustomAuthSignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-method", onClick: handleCustomAuth, disabled: busy }, /* @__PURE__ */ React2.createElement(UserIcon, { size: 18 }), /* @__PURE__ */ React2.createElement("span", null, customLoading ? "Connecting\u2026" : customAuthLabel))), extra, errorLine, canUsePasskey && onPasskeySignIn && /* @__PURE__ */ React2.createElement("button", { type: "button", className: "dcm-link", onClick: () => {
|
|
6251
6240
|
setError("");
|
|
6252
6241
|
setShowOtherMethods(false);
|
|
6253
6242
|
} }, "Use my passkey instead"), /* @__PURE__ */ React2.createElement(ProtectedBy, null));
|
|
@@ -6911,7 +6900,7 @@ function DecaneKit({ config, children }) {
|
|
|
6911
6900
|
sdk.canSignInWithPasskey().then((can) => setSocialAuth((p) => ({ ...p, canUsePasskey: can }))).catch(() => {
|
|
6912
6901
|
});
|
|
6913
6902
|
const params = new URLSearchParams(window.location.search);
|
|
6914
|
-
if (params.has("decane_jwt") || params.has("decane_error")) {
|
|
6903
|
+
if (params.has("decane_jwt") || params.has("decane_error") || readKingsChatReturn(window.location.search)) {
|
|
6915
6904
|
sdk.completeRedirectSignIn().then(applyResult).catch((e) => {
|
|
6916
6905
|
setSocialAuth((p) => p.phase ? { ...p, phase: null } : p);
|
|
6917
6906
|
console.error(e);
|