decane-connect-kit 2.7.1 → 2.7.3
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 +121 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -2
- package/dist/index.d.ts +32 -2
- package/dist/index.js +120 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -256,6 +256,13 @@ declare class ApiClient {
|
|
|
256
256
|
savedAt: string | null;
|
|
257
257
|
nagged: boolean;
|
|
258
258
|
shareSetVersion: number;
|
|
259
|
+
/**
|
|
260
|
+
* Whether this wallet is enrolled for server-assisted provisioning, i.e.
|
|
261
|
+
* whether a new device can be given a device share from login alone.
|
|
262
|
+
* Optional because backends older than this field simply omit it — treat
|
|
263
|
+
* `undefined` as "unknown", never as "not enrolled".
|
|
264
|
+
*/
|
|
265
|
+
enrolled?: boolean;
|
|
259
266
|
}>;
|
|
260
267
|
dismissRecoveryNag(jwt: string): Promise<void>;
|
|
261
268
|
}
|
|
@@ -521,7 +528,16 @@ interface SignTypedDataRequest {
|
|
|
521
528
|
message: Record<string, unknown>;
|
|
522
529
|
primaryType?: string;
|
|
523
530
|
}
|
|
524
|
-
type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started' | 'wallet-creating' | 'wallet-unlocking'
|
|
531
|
+
type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started' | 'wallet-creating' | 'wallet-unlocking'
|
|
532
|
+
/**
|
|
533
|
+
* Emitted when this wallet could not be enrolled for server-assisted
|
|
534
|
+
* recovery. Not fatal — the wallet works — but until it is enrolled the user
|
|
535
|
+
* cannot get a device share from login alone, so a future new device will
|
|
536
|
+
* need a passkey backup or a recovery file. The SDK retries automatically on
|
|
537
|
+
* later unlocks; surface or report this if you want visibility rather than a
|
|
538
|
+
* console line.
|
|
539
|
+
*/
|
|
540
|
+
| 'enrolment-failed';
|
|
525
541
|
interface DecaneConnect {
|
|
526
542
|
connect(): Promise<ConnectResult>;
|
|
527
543
|
connectWithGoogle(): Promise<ConnectResult>;
|
|
@@ -808,6 +824,20 @@ declare class NetworkError extends DecaneConnectError {
|
|
|
808
824
|
declare class ShareCorruptedError extends DecaneConnectError {
|
|
809
825
|
constructor();
|
|
810
826
|
}
|
|
827
|
+
/**
|
|
828
|
+
* Wallet creation exceeded its time budget.
|
|
829
|
+
*
|
|
830
|
+
* Provisioning is bounded so a stalled network or an unresponsive enclave
|
|
831
|
+
* surfaces as an error the app can act on, rather than a signup that appears to
|
|
832
|
+
* hang forever. The budget covers only the SDK's own work — key generation,
|
|
833
|
+
* share storage, opening the signer session — never time spent waiting on the
|
|
834
|
+
* user (a passkey prompt, choosing a PIN, the recovery-file dialog).
|
|
835
|
+
*
|
|
836
|
+
* Retryable: nothing is left half-created that a later sign-in cannot complete.
|
|
837
|
+
*/
|
|
838
|
+
declare class ProvisioningTimeoutError extends DecaneConnectError {
|
|
839
|
+
constructor(step: string);
|
|
840
|
+
}
|
|
811
841
|
declare class NewDeviceError extends DecaneConnectError {
|
|
812
842
|
constructor();
|
|
813
843
|
}
|
|
@@ -1238,4 +1268,4 @@ declare function useSignTypedData(): {
|
|
|
1238
1268
|
reset: () => void;
|
|
1239
1269
|
};
|
|
1240
1270
|
|
|
1241
|
-
export { type ActiveSession, AttestationError, type AuthProfile, type AuthResult, BUILT_IN_CHAINS, type BalanceResult, type CAIP25Method, type CAIP25SessionRequest, type CAIP25SessionResult, CHAINS, CallSequenceError, type Chain, type ChainDescriptor, type ChainId, type ChainScope, type ChainSupport, type ChainType, type ConnectMode, type ConnectResult, type DecaneConfig, type DecaneConnect, type DecaneConnectConfig, DecaneConnectError, DecaneError, DecaneErrorCode, DecaneKit, DecaneLogo, DecaneMark, type DecaneProvider, DecaneWordmark, type Eip1193Provider, type EthereumProviderOptions, ExportNotAuthorizedError, InvalidApiKeyError, InvalidPinError, InvalidRecoveryFileError, type KingsChatTokens, NetworkError, NewDeviceError, OriginNotAllowedError, type PortableBackup, PromptPinNotConfiguredError, ReconnectRequiredError, type RecordTransactionInput, RecoveryFailedError, type RecoveryProgress, type RecoveryShareFile, RecoveryWalletMismatchError, type SdkEvent, type SendTransactionRequest, SessionExpiredError, ShareCorruptedError, type SignMessageRequest, type SignTypedDataRequest, type SignedAuthorization, type SocialAuthState, type SocialConfig, SocialWalletModal, StaleDeviceShareError, type Theme, type TransactionRecord, type TxParams, type UnifiedWalletAccount, UnlockRequiredError, UserCancelledError, WalletSelector, WebAuthnNotSupportedError, type WrappedShareMeta, WrongPasswordError, createDecane, createDecaneConnect, createEthereumProvider, loginWithKingsChatPopup, startEmailAuth, useChainSwitch, useDecane, useEthereumProvider, useSendTransaction, useSignMessage, useSignTypedData, useSocialAuth, useSocialBalance, useSocialWallet, useWalletSelector, verifyEmailCode };
|
|
1271
|
+
export { type ActiveSession, AttestationError, type AuthProfile, type AuthResult, BUILT_IN_CHAINS, type BalanceResult, type CAIP25Method, type CAIP25SessionRequest, type CAIP25SessionResult, CHAINS, CallSequenceError, type Chain, type ChainDescriptor, type ChainId, type ChainScope, type ChainSupport, type ChainType, type ConnectMode, type ConnectResult, type DecaneConfig, type DecaneConnect, type DecaneConnectConfig, DecaneConnectError, DecaneError, DecaneErrorCode, DecaneKit, DecaneLogo, DecaneMark, type DecaneProvider, DecaneWordmark, type Eip1193Provider, type EthereumProviderOptions, ExportNotAuthorizedError, InvalidApiKeyError, InvalidPinError, InvalidRecoveryFileError, type KingsChatTokens, NetworkError, NewDeviceError, OriginNotAllowedError, type PortableBackup, PromptPinNotConfiguredError, ProvisioningTimeoutError, ReconnectRequiredError, type RecordTransactionInput, RecoveryFailedError, type RecoveryProgress, type RecoveryShareFile, RecoveryWalletMismatchError, type SdkEvent, type SendTransactionRequest, SessionExpiredError, ShareCorruptedError, type SignMessageRequest, type SignTypedDataRequest, type SignedAuthorization, type SocialAuthState, type SocialConfig, SocialWalletModal, StaleDeviceShareError, type Theme, type TransactionRecord, type TxParams, type UnifiedWalletAccount, UnlockRequiredError, UserCancelledError, WalletSelector, WebAuthnNotSupportedError, type WrappedShareMeta, WrongPasswordError, createDecane, createDecaneConnect, createEthereumProvider, loginWithKingsChatPopup, startEmailAuth, useChainSwitch, useDecane, useEthereumProvider, useSendTransaction, useSignMessage, useSignTypedData, useSocialAuth, useSocialBalance, useSocialWallet, useWalletSelector, verifyEmailCode };
|
package/dist/index.d.ts
CHANGED
|
@@ -256,6 +256,13 @@ declare class ApiClient {
|
|
|
256
256
|
savedAt: string | null;
|
|
257
257
|
nagged: boolean;
|
|
258
258
|
shareSetVersion: number;
|
|
259
|
+
/**
|
|
260
|
+
* Whether this wallet is enrolled for server-assisted provisioning, i.e.
|
|
261
|
+
* whether a new device can be given a device share from login alone.
|
|
262
|
+
* Optional because backends older than this field simply omit it — treat
|
|
263
|
+
* `undefined` as "unknown", never as "not enrolled".
|
|
264
|
+
*/
|
|
265
|
+
enrolled?: boolean;
|
|
259
266
|
}>;
|
|
260
267
|
dismissRecoveryNag(jwt: string): Promise<void>;
|
|
261
268
|
}
|
|
@@ -521,7 +528,16 @@ interface SignTypedDataRequest {
|
|
|
521
528
|
message: Record<string, unknown>;
|
|
522
529
|
primaryType?: string;
|
|
523
530
|
}
|
|
524
|
-
type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started' | 'wallet-creating' | 'wallet-unlocking'
|
|
531
|
+
type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started' | 'wallet-creating' | 'wallet-unlocking'
|
|
532
|
+
/**
|
|
533
|
+
* Emitted when this wallet could not be enrolled for server-assisted
|
|
534
|
+
* recovery. Not fatal — the wallet works — but until it is enrolled the user
|
|
535
|
+
* cannot get a device share from login alone, so a future new device will
|
|
536
|
+
* need a passkey backup or a recovery file. The SDK retries automatically on
|
|
537
|
+
* later unlocks; surface or report this if you want visibility rather than a
|
|
538
|
+
* console line.
|
|
539
|
+
*/
|
|
540
|
+
| 'enrolment-failed';
|
|
525
541
|
interface DecaneConnect {
|
|
526
542
|
connect(): Promise<ConnectResult>;
|
|
527
543
|
connectWithGoogle(): Promise<ConnectResult>;
|
|
@@ -808,6 +824,20 @@ declare class NetworkError extends DecaneConnectError {
|
|
|
808
824
|
declare class ShareCorruptedError extends DecaneConnectError {
|
|
809
825
|
constructor();
|
|
810
826
|
}
|
|
827
|
+
/**
|
|
828
|
+
* Wallet creation exceeded its time budget.
|
|
829
|
+
*
|
|
830
|
+
* Provisioning is bounded so a stalled network or an unresponsive enclave
|
|
831
|
+
* surfaces as an error the app can act on, rather than a signup that appears to
|
|
832
|
+
* hang forever. The budget covers only the SDK's own work — key generation,
|
|
833
|
+
* share storage, opening the signer session — never time spent waiting on the
|
|
834
|
+
* user (a passkey prompt, choosing a PIN, the recovery-file dialog).
|
|
835
|
+
*
|
|
836
|
+
* Retryable: nothing is left half-created that a later sign-in cannot complete.
|
|
837
|
+
*/
|
|
838
|
+
declare class ProvisioningTimeoutError extends DecaneConnectError {
|
|
839
|
+
constructor(step: string);
|
|
840
|
+
}
|
|
811
841
|
declare class NewDeviceError extends DecaneConnectError {
|
|
812
842
|
constructor();
|
|
813
843
|
}
|
|
@@ -1238,4 +1268,4 @@ declare function useSignTypedData(): {
|
|
|
1238
1268
|
reset: () => void;
|
|
1239
1269
|
};
|
|
1240
1270
|
|
|
1241
|
-
export { type ActiveSession, AttestationError, type AuthProfile, type AuthResult, BUILT_IN_CHAINS, type BalanceResult, type CAIP25Method, type CAIP25SessionRequest, type CAIP25SessionResult, CHAINS, CallSequenceError, type Chain, type ChainDescriptor, type ChainId, type ChainScope, type ChainSupport, type ChainType, type ConnectMode, type ConnectResult, type DecaneConfig, type DecaneConnect, type DecaneConnectConfig, DecaneConnectError, DecaneError, DecaneErrorCode, DecaneKit, DecaneLogo, DecaneMark, type DecaneProvider, DecaneWordmark, type Eip1193Provider, type EthereumProviderOptions, ExportNotAuthorizedError, InvalidApiKeyError, InvalidPinError, InvalidRecoveryFileError, type KingsChatTokens, NetworkError, NewDeviceError, OriginNotAllowedError, type PortableBackup, PromptPinNotConfiguredError, ReconnectRequiredError, type RecordTransactionInput, RecoveryFailedError, type RecoveryProgress, type RecoveryShareFile, RecoveryWalletMismatchError, type SdkEvent, type SendTransactionRequest, SessionExpiredError, ShareCorruptedError, type SignMessageRequest, type SignTypedDataRequest, type SignedAuthorization, type SocialAuthState, type SocialConfig, SocialWalletModal, StaleDeviceShareError, type Theme, type TransactionRecord, type TxParams, type UnifiedWalletAccount, UnlockRequiredError, UserCancelledError, WalletSelector, WebAuthnNotSupportedError, type WrappedShareMeta, WrongPasswordError, createDecane, createDecaneConnect, createEthereumProvider, loginWithKingsChatPopup, startEmailAuth, useChainSwitch, useDecane, useEthereumProvider, useSendTransaction, useSignMessage, useSignTypedData, useSocialAuth, useSocialBalance, useSocialWallet, useWalletSelector, verifyEmailCode };
|
|
1271
|
+
export { type ActiveSession, AttestationError, type AuthProfile, type AuthResult, BUILT_IN_CHAINS, type BalanceResult, type CAIP25Method, type CAIP25SessionRequest, type CAIP25SessionResult, CHAINS, CallSequenceError, type Chain, type ChainDescriptor, type ChainId, type ChainScope, type ChainSupport, type ChainType, type ConnectMode, type ConnectResult, type DecaneConfig, type DecaneConnect, type DecaneConnectConfig, DecaneConnectError, DecaneError, DecaneErrorCode, DecaneKit, DecaneLogo, DecaneMark, type DecaneProvider, DecaneWordmark, type Eip1193Provider, type EthereumProviderOptions, ExportNotAuthorizedError, InvalidApiKeyError, InvalidPinError, InvalidRecoveryFileError, type KingsChatTokens, NetworkError, NewDeviceError, OriginNotAllowedError, type PortableBackup, PromptPinNotConfiguredError, ProvisioningTimeoutError, ReconnectRequiredError, type RecordTransactionInput, RecoveryFailedError, type RecoveryProgress, type RecoveryShareFile, RecoveryWalletMismatchError, type SdkEvent, type SendTransactionRequest, SessionExpiredError, ShareCorruptedError, type SignMessageRequest, type SignTypedDataRequest, type SignedAuthorization, type SocialAuthState, type SocialConfig, SocialWalletModal, StaleDeviceShareError, type Theme, type TransactionRecord, type TxParams, type UnifiedWalletAccount, UnlockRequiredError, UserCancelledError, WalletSelector, WebAuthnNotSupportedError, type WrappedShareMeta, WrongPasswordError, createDecane, createDecaneConnect, createEthereumProvider, loginWithKingsChatPopup, startEmailAuth, useChainSwitch, useDecane, useEthereumProvider, useSendTransaction, useSignMessage, useSignTypedData, useSocialAuth, useSocialBalance, useSocialWallet, useWalletSelector, verifyEmailCode };
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ __export(errors_exports, {
|
|
|
22
22
|
NewDeviceError: () => NewDeviceError,
|
|
23
23
|
OriginNotAllowedError: () => OriginNotAllowedError,
|
|
24
24
|
PromptPinNotConfiguredError: () => PromptPinNotConfiguredError,
|
|
25
|
+
ProvisioningTimeoutError: () => ProvisioningTimeoutError,
|
|
25
26
|
ReconnectRequiredError: () => ReconnectRequiredError,
|
|
26
27
|
RecoveryFailedError: () => RecoveryFailedError,
|
|
27
28
|
RecoveryWalletMismatchError: () => RecoveryWalletMismatchError,
|
|
@@ -33,7 +34,7 @@ __export(errors_exports, {
|
|
|
33
34
|
WebAuthnNotSupportedError: () => WebAuthnNotSupportedError,
|
|
34
35
|
WrongPasswordError: () => WrongPasswordError
|
|
35
36
|
});
|
|
36
|
-
var DecaneConnectError, InvalidApiKeyError, OriginNotAllowedError, SessionExpiredError, UnlockRequiredError, ReconnectRequiredError, UserCancelledError, WebAuthnNotSupportedError, InvalidPinError, CallSequenceError, NetworkError, ShareCorruptedError, NewDeviceError, StaleDeviceShareError, PromptPinNotConfiguredError, AttestationError, RecoveryFailedError, InvalidRecoveryFileError, WrongPasswordError, RecoveryWalletMismatchError, ExportNotAuthorizedError;
|
|
37
|
+
var DecaneConnectError, InvalidApiKeyError, OriginNotAllowedError, SessionExpiredError, UnlockRequiredError, ReconnectRequiredError, UserCancelledError, WebAuthnNotSupportedError, InvalidPinError, CallSequenceError, NetworkError, ShareCorruptedError, ProvisioningTimeoutError, NewDeviceError, StaleDeviceShareError, PromptPinNotConfiguredError, AttestationError, RecoveryFailedError, InvalidRecoveryFileError, WrongPasswordError, RecoveryWalletMismatchError, ExportNotAuthorizedError;
|
|
37
38
|
var init_errors = __esm({
|
|
38
39
|
"src/social/errors.ts"() {
|
|
39
40
|
"use strict";
|
|
@@ -104,6 +105,11 @@ var init_errors = __esm({
|
|
|
104
105
|
super("SHARE_CORRUPTED", "Stored device share is corrupted");
|
|
105
106
|
}
|
|
106
107
|
};
|
|
108
|
+
ProvisioningTimeoutError = class extends DecaneConnectError {
|
|
109
|
+
constructor(step) {
|
|
110
|
+
super("PROVISIONING_TIMEOUT", `Wallet setup timed out while ${step}. Check your connection and try again.`, true);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
107
113
|
NewDeviceError = class extends DecaneConnectError {
|
|
108
114
|
constructor() {
|
|
109
115
|
super("NEW_DEVICE", "No device share found. Re-authenticate to set up this device.");
|
|
@@ -1932,6 +1938,7 @@ var ERROR_MAP = {
|
|
|
1932
1938
|
// generic failure message.
|
|
1933
1939
|
UNAUTHORIZED: ReconnectRequiredError
|
|
1934
1940
|
};
|
|
1941
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
1935
1942
|
var ApiClient = class {
|
|
1936
1943
|
constructor(baseUrl, apiKey, appId) {
|
|
1937
1944
|
this.baseUrl = baseUrl;
|
|
@@ -1940,9 +1947,12 @@ var ApiClient = class {
|
|
|
1940
1947
|
}
|
|
1941
1948
|
async _request(path, init) {
|
|
1942
1949
|
let res;
|
|
1950
|
+
const controller = new AbortController();
|
|
1951
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
1943
1952
|
try {
|
|
1944
1953
|
res = await fetch(`${this.baseUrl}${path}`, {
|
|
1945
1954
|
...init,
|
|
1955
|
+
signal: controller.signal,
|
|
1946
1956
|
headers: {
|
|
1947
1957
|
"Content-Type": "application/json",
|
|
1948
1958
|
"X-API-Key": this.apiKey,
|
|
@@ -1952,6 +1962,8 @@ var ApiClient = class {
|
|
|
1952
1962
|
});
|
|
1953
1963
|
} catch {
|
|
1954
1964
|
throw new NetworkError();
|
|
1965
|
+
} finally {
|
|
1966
|
+
clearTimeout(timer);
|
|
1955
1967
|
}
|
|
1956
1968
|
if (res.status === 404 && init?.allow404) return null;
|
|
1957
1969
|
if (!res.ok) {
|
|
@@ -2254,6 +2266,16 @@ function bytesToBase64(b) {
|
|
|
2254
2266
|
function base64ToBytes(s) {
|
|
2255
2267
|
return Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
|
|
2256
2268
|
}
|
|
2269
|
+
var TEE_TIMEOUT_MS = 3e4;
|
|
2270
|
+
async function teeFetch(url, init) {
|
|
2271
|
+
const controller = new AbortController();
|
|
2272
|
+
const timer = setTimeout(() => controller.abort(), TEE_TIMEOUT_MS);
|
|
2273
|
+
try {
|
|
2274
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
2275
|
+
} finally {
|
|
2276
|
+
clearTimeout(timer);
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2257
2279
|
var TeeClient = class {
|
|
2258
2280
|
constructor(teeUrl) {
|
|
2259
2281
|
this.teeUrl = teeUrl;
|
|
@@ -2309,7 +2331,7 @@ var TeeClient = class {
|
|
|
2309
2331
|
*/
|
|
2310
2332
|
async openSession(deviceShare, jwt, requireAssertionPerSignature = false) {
|
|
2311
2333
|
await this.ensureAttested();
|
|
2312
|
-
const res = await
|
|
2334
|
+
const res = await teeFetch(`${this.teeUrl}/signer/session`, {
|
|
2313
2335
|
method: "POST",
|
|
2314
2336
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2315
2337
|
body: JSON.stringify({
|
|
@@ -2341,7 +2363,7 @@ var TeeClient = class {
|
|
|
2341
2363
|
*/
|
|
2342
2364
|
async recoverWithShare(jwt, recoveryShare) {
|
|
2343
2365
|
await this.ensureAttested();
|
|
2344
|
-
const res = await
|
|
2366
|
+
const res = await teeFetch(`${this.teeUrl}/signer/recover`, {
|
|
2345
2367
|
method: "POST",
|
|
2346
2368
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2347
2369
|
body: JSON.stringify({ recoveryShare: bytesToBase64(recoveryShare) })
|
|
@@ -2366,7 +2388,7 @@ var TeeClient = class {
|
|
|
2366
2388
|
* require proof of fresh authenticator possession, not just an active session.
|
|
2367
2389
|
*/
|
|
2368
2390
|
async completeAssertion(jwt) {
|
|
2369
|
-
const res = await
|
|
2391
|
+
const res = await teeFetch(`${this.teeUrl}/signer/assertion-challenge`, {
|
|
2370
2392
|
method: "POST",
|
|
2371
2393
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2372
2394
|
body: JSON.stringify({ rpId: window.location.hostname })
|
|
@@ -2395,12 +2417,17 @@ var TeeClient = class {
|
|
|
2395
2417
|
*/
|
|
2396
2418
|
async enrolAuthShare(jwt, recoveryShare) {
|
|
2397
2419
|
await this.ensureAttested();
|
|
2398
|
-
const res = await
|
|
2420
|
+
const res = await teeFetch(`${this.teeUrl}/signer/auth-share`, {
|
|
2399
2421
|
method: "POST",
|
|
2400
2422
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2401
2423
|
body: JSON.stringify({ share: bytesToBase64(recoveryShare) })
|
|
2402
2424
|
});
|
|
2403
|
-
if (!res.ok)
|
|
2425
|
+
if (!res.ok) {
|
|
2426
|
+
const detail = await res.text().catch(() => "");
|
|
2427
|
+
throw new Error(
|
|
2428
|
+
`auth share enrolment failed (HTTP ${res.status})${detail ? `: ${detail.slice(0, 200)}` : ""}`
|
|
2429
|
+
);
|
|
2430
|
+
}
|
|
2404
2431
|
}
|
|
2405
2432
|
/**
|
|
2406
2433
|
* Ask the enclave for a fresh device share for this authenticated user.
|
|
@@ -2413,12 +2440,17 @@ var TeeClient = class {
|
|
|
2413
2440
|
*/
|
|
2414
2441
|
async provisionDeviceShare(jwt) {
|
|
2415
2442
|
await this.ensureAttested();
|
|
2416
|
-
const res = await
|
|
2443
|
+
const res = await teeFetch(`${this.teeUrl}/signer/provision`, {
|
|
2417
2444
|
method: "POST",
|
|
2418
2445
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" }
|
|
2419
2446
|
});
|
|
2420
2447
|
if (res.status === 409) return null;
|
|
2421
|
-
if (!res.ok)
|
|
2448
|
+
if (!res.ok) {
|
|
2449
|
+
const detail = await res.text().catch(() => "");
|
|
2450
|
+
throw new Error(
|
|
2451
|
+
`device share provisioning failed (HTTP ${res.status})${detail ? `: ${detail.slice(0, 200)}` : ""}`
|
|
2452
|
+
);
|
|
2453
|
+
}
|
|
2422
2454
|
const body = await res.json();
|
|
2423
2455
|
return base64ToBytes(body.deviceShare);
|
|
2424
2456
|
}
|
|
@@ -2426,7 +2458,7 @@ var TeeClient = class {
|
|
|
2426
2458
|
async rotateShares(jwt) {
|
|
2427
2459
|
if (!this.handle) throw new Error("no session");
|
|
2428
2460
|
const { challengeId, assertion } = await this.completeAssertion(jwt);
|
|
2429
|
-
const res = await
|
|
2461
|
+
const res = await teeFetch(`${this.teeUrl}/signer/rotate`, {
|
|
2430
2462
|
method: "POST",
|
|
2431
2463
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2432
2464
|
body: JSON.stringify({ handle: this.handle, challengeId, assertion, origin: window.location.origin })
|
|
@@ -2443,7 +2475,7 @@ var TeeClient = class {
|
|
|
2443
2475
|
async exportPortableBackup(jwt) {
|
|
2444
2476
|
if (!this.handle) throw new Error("no session");
|
|
2445
2477
|
const { challengeId, assertion } = await this.completeAssertion(jwt);
|
|
2446
|
-
const res = await
|
|
2478
|
+
const res = await teeFetch(`${this.teeUrl}/signer/export-portable`, {
|
|
2447
2479
|
method: "POST",
|
|
2448
2480
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2449
2481
|
body: JSON.stringify({ handle: this.handle, challengeId, assertion, origin: window.location.origin })
|
|
@@ -2473,7 +2505,7 @@ var TeeClient = class {
|
|
|
2473
2505
|
if (!jwt) throw new Error("sign requires a jwt when per-signature assertion is enabled");
|
|
2474
2506
|
gate = await this.completeAssertion(jwt);
|
|
2475
2507
|
}
|
|
2476
|
-
const res = await
|
|
2508
|
+
const res = await teeFetch(`${this.teeUrl}/signer/sign`, {
|
|
2477
2509
|
method: "POST",
|
|
2478
2510
|
headers: { "Content-Type": "application/json" },
|
|
2479
2511
|
body: JSON.stringify({
|
|
@@ -3414,6 +3446,44 @@ async function createDecaneConnect(config) {
|
|
|
3414
3446
|
let ethProvider = null;
|
|
3415
3447
|
const persistSession = config.persistSession !== false;
|
|
3416
3448
|
const serverAssistedRecovery = true;
|
|
3449
|
+
const RECOVERY_OFFER_TIMEOUT_MS = 5 * 6e4;
|
|
3450
|
+
const selfHealAttempted = /* @__PURE__ */ new Set();
|
|
3451
|
+
async function selfHealEnrolment(userId, jwt, share) {
|
|
3452
|
+
try {
|
|
3453
|
+
if (selfHealAttempted.has(userId)) return;
|
|
3454
|
+
const status = await api.getRecoveryStatus(jwt);
|
|
3455
|
+
if (status.enrolled !== false) return;
|
|
3456
|
+
selfHealAttempted.add(userId);
|
|
3457
|
+
await new TeeClient(teeUrl).enrolAuthShare(jwt, share);
|
|
3458
|
+
} catch (e) {
|
|
3459
|
+
events.emit("enrolment-failed");
|
|
3460
|
+
console.warn(
|
|
3461
|
+
"decane-connect-kit: this wallet is not enrolled for server-assisted recovery and the automatic repair failed. It still works on this device, but a new device will need a passkey backup or a recovery file. The SDK will try again on the next unlock.",
|
|
3462
|
+
e
|
|
3463
|
+
);
|
|
3464
|
+
} finally {
|
|
3465
|
+
zeroize(share);
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
const PROVISIONING_BUDGET_MS = 9e4;
|
|
3469
|
+
function withDeadline(promise, ms, step) {
|
|
3470
|
+
let timer;
|
|
3471
|
+
return Promise.race([
|
|
3472
|
+
promise,
|
|
3473
|
+
new Promise((_, reject) => {
|
|
3474
|
+
timer = setTimeout(() => reject(new ProvisioningTimeoutError(step)), Math.max(0, ms));
|
|
3475
|
+
})
|
|
3476
|
+
]).finally(() => clearTimeout(timer));
|
|
3477
|
+
}
|
|
3478
|
+
function withTimeout(promise, ms) {
|
|
3479
|
+
let timer;
|
|
3480
|
+
return Promise.race([
|
|
3481
|
+
promise,
|
|
3482
|
+
new Promise((resolve) => {
|
|
3483
|
+
timer = setTimeout(() => resolve(null), ms);
|
|
3484
|
+
})
|
|
3485
|
+
]).finally(() => clearTimeout(timer));
|
|
3486
|
+
}
|
|
3417
3487
|
const SESSION_KEY = sessionPersistKey(config.apiKey);
|
|
3418
3488
|
function persistLiveSession() {
|
|
3419
3489
|
if (!persistSession) return;
|
|
@@ -3554,6 +3624,8 @@ async function createDecaneConnect(config) {
|
|
|
3554
3624
|
({ wrappingKey, meta } = await pinSetup());
|
|
3555
3625
|
unlockMethod = "pin";
|
|
3556
3626
|
}
|
|
3627
|
+
const provisioningStartedAt = Date.now();
|
|
3628
|
+
const remainingBudget = () => PROVISIONING_BUDGET_MS - (Date.now() - provisioningStartedAt);
|
|
3557
3629
|
const master = generateMaster();
|
|
3558
3630
|
let shareSet;
|
|
3559
3631
|
let evmAddress;
|
|
@@ -3585,7 +3657,7 @@ async function createDecaneConnect(config) {
|
|
|
3585
3657
|
version: 1
|
|
3586
3658
|
});
|
|
3587
3659
|
const serverIv = generateRandomBytes(12);
|
|
3588
|
-
await api.putShare(jwt, {
|
|
3660
|
+
await withDeadline(api.putShare(jwt, {
|
|
3589
3661
|
ciphertext: shareSet.server,
|
|
3590
3662
|
iv: serverIv,
|
|
3591
3663
|
wrappedShareMeta: {
|
|
@@ -3595,7 +3667,7 @@ async function createDecaneConnect(config) {
|
|
|
3595
3667
|
evmAddress,
|
|
3596
3668
|
solanaAddress,
|
|
3597
3669
|
tronAddress
|
|
3598
|
-
});
|
|
3670
|
+
}), remainingBudget(), "storing your wallet");
|
|
3599
3671
|
zeroize(shareSet.server);
|
|
3600
3672
|
const addresses = { evm: evmAddress, solana: solanaAddress, tron: tronAddress };
|
|
3601
3673
|
if (unlockMethod === "passkey" && meta.credentialId && meta.prfSalt) {
|
|
@@ -3611,7 +3683,11 @@ async function createDecaneConnect(config) {
|
|
|
3611
3683
|
console.warn("decane-connect-kit: wrapped share backup failed; user can retry from settings.", e);
|
|
3612
3684
|
}
|
|
3613
3685
|
}
|
|
3614
|
-
const confirmed = await
|
|
3686
|
+
const confirmed = await withDeadline(
|
|
3687
|
+
session.start(shareSet.device, jwt, addresses),
|
|
3688
|
+
remainingBudget(),
|
|
3689
|
+
"opening the signing session"
|
|
3690
|
+
);
|
|
3615
3691
|
zeroize(shareSet.device);
|
|
3616
3692
|
if (confirmed.evm.toLowerCase() !== addresses.evm.toLowerCase()) {
|
|
3617
3693
|
console.warn(
|
|
@@ -3621,10 +3697,33 @@ async function createDecaneConnect(config) {
|
|
|
3621
3697
|
addresses.evm = evmAddress = confirmed.evm;
|
|
3622
3698
|
addresses.solana = solanaAddress = confirmed.solana;
|
|
3623
3699
|
addresses.tron = tronAddress = confirmed.tron ?? evmToTronAddress2(confirmed.evm);
|
|
3700
|
+
if (serverAssistedRecovery) {
|
|
3701
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
3702
|
+
try {
|
|
3703
|
+
const tee = new TeeClient(teeUrl);
|
|
3704
|
+
await tee.enrolAuthShare(jwt, shareSet.recovery);
|
|
3705
|
+
break;
|
|
3706
|
+
} catch (e) {
|
|
3707
|
+
if (attempt === 2) {
|
|
3708
|
+
console.warn(
|
|
3709
|
+
"decane-connect-kit: could not enrol this wallet for server-assisted recovery; a recovery file will be required on a new device. This wallet will get a 409 not_enrolled from /signer/provision until it is re-enrolled.",
|
|
3710
|
+
e
|
|
3711
|
+
);
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3624
3716
|
if (config.offerRecoveryAtSignup !== false && config.onRecoveryShareOffer) {
|
|
3625
3717
|
try {
|
|
3626
|
-
const choice = await
|
|
3627
|
-
|
|
3718
|
+
const choice = await withTimeout(
|
|
3719
|
+
config.onRecoveryShareOffer({ evmAddress, solanaAddress }),
|
|
3720
|
+
RECOVERY_OFFER_TIMEOUT_MS
|
|
3721
|
+
);
|
|
3722
|
+
if (!choice) {
|
|
3723
|
+
console.warn(
|
|
3724
|
+
`decane-connect-kit: onRecoveryShareOffer did not settle within ${RECOVERY_OFFER_TIMEOUT_MS / 6e4} minutes, so wallet creation continued without offering a recovery file. The wallet is fully created and enrolled; the user can generate a recovery share later via rotateShares(). Check that your handler always resolves or rejects.`
|
|
3725
|
+
);
|
|
3726
|
+
} else if (choice.wants) {
|
|
3628
3727
|
const file = await buildRecoveryShareFile(
|
|
3629
3728
|
shareSet.recovery,
|
|
3630
3729
|
choice.password,
|
|
@@ -3639,17 +3738,6 @@ async function createDecaneConnect(config) {
|
|
|
3639
3738
|
console.warn("decane-connect-kit: recovery share offer failed or was skipped.", e);
|
|
3640
3739
|
}
|
|
3641
3740
|
}
|
|
3642
|
-
if (serverAssistedRecovery) {
|
|
3643
|
-
try {
|
|
3644
|
-
const tee = new TeeClient(teeUrl);
|
|
3645
|
-
await tee.enrolAuthShare(jwt, shareSet.recovery);
|
|
3646
|
-
} catch (e) {
|
|
3647
|
-
console.warn(
|
|
3648
|
-
"decane-connect-kit: could not enrol this wallet for server-assisted recovery; a recovery file will be required on a new device.",
|
|
3649
|
-
e
|
|
3650
|
-
);
|
|
3651
|
-
}
|
|
3652
|
-
}
|
|
3653
3741
|
zeroize(shareSet.recovery);
|
|
3654
3742
|
return addresses;
|
|
3655
3743
|
}
|
|
@@ -3675,6 +3763,9 @@ async function createDecaneConnect(config) {
|
|
|
3675
3763
|
}
|
|
3676
3764
|
return addrs;
|
|
3677
3765
|
}
|
|
3766
|
+
console.warn(
|
|
3767
|
+
"decane-connect-kit: this wallet is not enrolled for server-assisted provisioning (/signer/provision returned 409 not_enrolled), so a device share cannot be issued from the JWT alone. Falling back to passkey sync, then the recovery file."
|
|
3768
|
+
);
|
|
3678
3769
|
} catch (e) {
|
|
3679
3770
|
console.warn(
|
|
3680
3771
|
"decane-connect-kit: server-assisted provisioning unavailable; falling back to passkey/recovery-file recovery.",
|
|
@@ -3735,6 +3826,7 @@ async function createDecaneConnect(config) {
|
|
|
3735
3826
|
}
|
|
3736
3827
|
throw e;
|
|
3737
3828
|
}
|
|
3829
|
+
void selfHealEnrolment(userId, jwt, new Uint8Array(deviceShare));
|
|
3738
3830
|
zeroize(deviceShare);
|
|
3739
3831
|
if (recordedAddresses && recordedAddresses.evm.toLowerCase() !== addresses.evm.toLowerCase()) {
|
|
3740
3832
|
console.warn(
|
|
@@ -7186,6 +7278,7 @@ export {
|
|
|
7186
7278
|
NewDeviceError,
|
|
7187
7279
|
OriginNotAllowedError,
|
|
7188
7280
|
PromptPinNotConfiguredError,
|
|
7281
|
+
ProvisioningTimeoutError,
|
|
7189
7282
|
ReconnectRequiredError,
|
|
7190
7283
|
RecoveryFailedError,
|
|
7191
7284
|
RecoveryWalletMismatchError,
|