decane-connect-kit 2.0.3 → 2.2.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 +454 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +113 -7
- package/dist/index.d.ts +113 -7
- package/dist/index.js +455 -38
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1868,7 +1868,12 @@ function startBitcoinDiscovery(registry) {
|
|
|
1868
1868
|
}
|
|
1869
1869
|
|
|
1870
1870
|
// src/factory.ts
|
|
1871
|
-
function createDecane(config
|
|
1871
|
+
function createDecane(config) {
|
|
1872
|
+
if (!config?.appId) {
|
|
1873
|
+
throw new Error(
|
|
1874
|
+
"decane-connect-kit: appId is required. Pass your dashboard project ID as config.appId."
|
|
1875
|
+
);
|
|
1876
|
+
}
|
|
1872
1877
|
const emitter = createEmitter();
|
|
1873
1878
|
const registry = createRegistry(emitter);
|
|
1874
1879
|
const session = createSessionManager();
|
|
@@ -1914,9 +1919,10 @@ var ERROR_MAP = {
|
|
|
1914
1919
|
ORIGIN_NOT_ALLOWED: OriginNotAllowedError
|
|
1915
1920
|
};
|
|
1916
1921
|
var ApiClient = class {
|
|
1917
|
-
constructor(baseUrl, apiKey) {
|
|
1922
|
+
constructor(baseUrl, apiKey, appId) {
|
|
1918
1923
|
this.baseUrl = baseUrl;
|
|
1919
1924
|
this.apiKey = apiKey;
|
|
1925
|
+
this.appId = appId;
|
|
1920
1926
|
}
|
|
1921
1927
|
async _request(path, init) {
|
|
1922
1928
|
let res;
|
|
@@ -1926,6 +1932,7 @@ var ApiClient = class {
|
|
|
1926
1932
|
headers: {
|
|
1927
1933
|
"Content-Type": "application/json",
|
|
1928
1934
|
"X-API-Key": this.apiKey,
|
|
1935
|
+
"X-App-Id": this.appId,
|
|
1929
1936
|
...init?.headers
|
|
1930
1937
|
}
|
|
1931
1938
|
});
|
|
@@ -1992,6 +1999,35 @@ var ApiClient = class {
|
|
|
1992
1999
|
body: JSON.stringify({ response, origin })
|
|
1993
2000
|
});
|
|
1994
2001
|
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Exchange a token from the developer's own auth system for a Decane
|
|
2004
|
+
* session. `providerId` is only needed when the project has more than one
|
|
2005
|
+
* provider configured.
|
|
2006
|
+
*/
|
|
2007
|
+
async authExternal(token, providerId) {
|
|
2008
|
+
return this._request("/auth/external", {
|
|
2009
|
+
method: "POST",
|
|
2010
|
+
body: JSON.stringify(providerId ? { token, providerId } : { token })
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
2013
|
+
/**
|
|
2014
|
+
* Returns null when this user has no server-registered passkey, which is the
|
|
2015
|
+
* expected answer for wallets created before passkey sign-in existed or for
|
|
2016
|
+
* PIN-only users — the caller falls back to the normal sign-in flow.
|
|
2017
|
+
*/
|
|
2018
|
+
async getWebAuthnLoginOptions(userId, rpId) {
|
|
2019
|
+
return this._request("/webauthn/login-options", {
|
|
2020
|
+
method: "POST",
|
|
2021
|
+
body: JSON.stringify({ userId, rpId }),
|
|
2022
|
+
allow404: true
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
async postWebAuthnLogin(userId, response, origin) {
|
|
2026
|
+
return this._request("/webauthn/login", {
|
|
2027
|
+
method: "POST",
|
|
2028
|
+
body: JSON.stringify({ userId, response, origin })
|
|
2029
|
+
});
|
|
2030
|
+
}
|
|
1995
2031
|
async revoke(jwt) {
|
|
1996
2032
|
await this._request("/auth/revoke", {
|
|
1997
2033
|
method: "POST",
|
|
@@ -2164,6 +2200,12 @@ var TeeClient = class {
|
|
|
2164
2200
|
this.teeUrl = teeUrl;
|
|
2165
2201
|
this.handle = null;
|
|
2166
2202
|
this.attested = false;
|
|
2203
|
+
/** Mirrors what the signer confirmed for this session, not what we asked for. */
|
|
2204
|
+
this.assertionPerSignature = false;
|
|
2205
|
+
}
|
|
2206
|
+
/** Whether this session actually got per-signature assertions. */
|
|
2207
|
+
requiresAssertionPerSignature() {
|
|
2208
|
+
return this.assertionPerSignature;
|
|
2167
2209
|
}
|
|
2168
2210
|
async ensureAttested() {
|
|
2169
2211
|
if (this.attested) return;
|
|
@@ -2179,16 +2221,20 @@ var TeeClient = class {
|
|
|
2179
2221
|
* couldn't derive them (older signer build, or a transient share-fetch
|
|
2180
2222
|
* failure); callers fall back to the backend's addresses in that case.
|
|
2181
2223
|
*/
|
|
2182
|
-
async openSession(deviceShare, jwt) {
|
|
2224
|
+
async openSession(deviceShare, jwt, requireAssertionPerSignature = false) {
|
|
2183
2225
|
await this.ensureAttested();
|
|
2184
2226
|
const res = await fetch(`${this.teeUrl}/signer/session`, {
|
|
2185
2227
|
method: "POST",
|
|
2186
2228
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2187
|
-
body: JSON.stringify({
|
|
2229
|
+
body: JSON.stringify({
|
|
2230
|
+
deviceShare: bytesToBase64(deviceShare),
|
|
2231
|
+
requireAssertionPerSignature
|
|
2232
|
+
})
|
|
2188
2233
|
});
|
|
2189
2234
|
if (!res.ok) throw new Error("session open failed");
|
|
2190
2235
|
const body = await res.json();
|
|
2191
2236
|
this.handle = body.handle;
|
|
2237
|
+
this.assertionPerSignature = body.requireAssertionPerSignature === true;
|
|
2192
2238
|
return body.addresses ?? null;
|
|
2193
2239
|
}
|
|
2194
2240
|
/**
|
|
@@ -2279,14 +2325,33 @@ var TeeClient = class {
|
|
|
2279
2325
|
hasSession() {
|
|
2280
2326
|
return this.handle !== null;
|
|
2281
2327
|
}
|
|
2282
|
-
|
|
2328
|
+
/**
|
|
2329
|
+
* `jwt` is only needed when this session requires per-signature assertions —
|
|
2330
|
+
* it authenticates the challenge request. Passed per call rather than cached
|
|
2331
|
+
* on the instance deliberately: the backend's JWTs are shorter-lived than the
|
|
2332
|
+
* TEE session TTL, so a stored copy would go stale mid-session.
|
|
2333
|
+
*/
|
|
2334
|
+
async sign(req, jwt) {
|
|
2283
2335
|
if (!this.handle) throw new Error("no session");
|
|
2336
|
+
let gate = null;
|
|
2337
|
+
if (this.assertionPerSignature) {
|
|
2338
|
+
if (!jwt) throw new Error("sign requires a jwt when per-signature assertion is enabled");
|
|
2339
|
+
gate = await this.completeAssertion(jwt);
|
|
2340
|
+
}
|
|
2284
2341
|
const res = await fetch(`${this.teeUrl}/signer/sign`, {
|
|
2285
2342
|
method: "POST",
|
|
2286
2343
|
headers: { "Content-Type": "application/json" },
|
|
2287
|
-
body: JSON.stringify({
|
|
2344
|
+
body: JSON.stringify({
|
|
2345
|
+
handle: this.handle,
|
|
2346
|
+
...req,
|
|
2347
|
+
...gate ? { ...gate, origin: window.location.origin } : {}
|
|
2348
|
+
})
|
|
2288
2349
|
});
|
|
2289
2350
|
if (res.status === 401) {
|
|
2351
|
+
const body = await res.clone().json().catch(() => ({}));
|
|
2352
|
+
if (body?.error === "assertion_required") {
|
|
2353
|
+
throw new ExportNotAuthorizedError();
|
|
2354
|
+
}
|
|
2290
2355
|
this.handle = null;
|
|
2291
2356
|
throw new SessionExpiredError();
|
|
2292
2357
|
}
|
|
@@ -2309,10 +2374,11 @@ var TeeClient = class {
|
|
|
2309
2374
|
// src/social/session.ts
|
|
2310
2375
|
init_errors();
|
|
2311
2376
|
var SessionManager = class {
|
|
2312
|
-
constructor(events, durationMs, teeUrl) {
|
|
2377
|
+
constructor(events, durationMs, teeUrl, requireAssertionPerSignature = false) {
|
|
2313
2378
|
this.events = events;
|
|
2314
2379
|
this.durationMs = durationMs;
|
|
2315
2380
|
this.teeUrl = teeUrl;
|
|
2381
|
+
this.requireAssertionPerSignature = requireAssertionPerSignature;
|
|
2316
2382
|
this.tee = null;
|
|
2317
2383
|
this.evmAddress = null;
|
|
2318
2384
|
this.solanaAddress = null;
|
|
@@ -2332,7 +2398,18 @@ var SessionManager = class {
|
|
|
2332
2398
|
*/
|
|
2333
2399
|
async start(deviceShare, jwt, fallback) {
|
|
2334
2400
|
this.tee = new TeeClient(this.teeUrl);
|
|
2335
|
-
const verified = await this.tee.openSession(
|
|
2401
|
+
const verified = await this.tee.openSession(
|
|
2402
|
+
deviceShare,
|
|
2403
|
+
jwt,
|
|
2404
|
+
this.requireAssertionPerSignature
|
|
2405
|
+
);
|
|
2406
|
+
if (this.requireAssertionPerSignature && !this.tee.requiresAssertionPerSignature()) {
|
|
2407
|
+
void this.tee.closeSession();
|
|
2408
|
+
this.tee = null;
|
|
2409
|
+
throw new Error(
|
|
2410
|
+
"requireAssertionPerSignature was requested but this signer does not support it \u2014 refusing to open a session with weaker authority than configured."
|
|
2411
|
+
);
|
|
2412
|
+
}
|
|
2336
2413
|
const addresses = verified ?? fallback;
|
|
2337
2414
|
if (!addresses) {
|
|
2338
2415
|
this.tee = null;
|
|
@@ -2450,7 +2527,13 @@ function validatePin(pin) {
|
|
|
2450
2527
|
|
|
2451
2528
|
// src/social/crypto/passkey.ts
|
|
2452
2529
|
init_errors();
|
|
2453
|
-
import { startRegistration } from "@simplewebauthn/browser";
|
|
2530
|
+
import { startRegistration, startAuthentication as startAuthentication2 } from "@simplewebauthn/browser";
|
|
2531
|
+
function b64u(u) {
|
|
2532
|
+
return btoa(String.fromCharCode(...u)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
2533
|
+
}
|
|
2534
|
+
function fromB64u(s) {
|
|
2535
|
+
return Uint8Array.from(atob(s.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
|
|
2536
|
+
}
|
|
2454
2537
|
function buf3(u) {
|
|
2455
2538
|
return u.buffer.slice(u.byteOffset, u.byteOffset + u.byteLength);
|
|
2456
2539
|
}
|
|
@@ -2477,6 +2560,29 @@ async function registerPasskey(opts) {
|
|
|
2477
2560
|
);
|
|
2478
2561
|
return { credentialId, prfOutput, response };
|
|
2479
2562
|
}
|
|
2563
|
+
async function authenticateWithPasskey(opts) {
|
|
2564
|
+
const optionsJSON = {
|
|
2565
|
+
...opts.optionsJSON,
|
|
2566
|
+
extensions: {
|
|
2567
|
+
...opts.optionsJSON.extensions,
|
|
2568
|
+
prf: { eval: { first: b64u(opts.prfSalt) } }
|
|
2569
|
+
}
|
|
2570
|
+
};
|
|
2571
|
+
let response;
|
|
2572
|
+
try {
|
|
2573
|
+
response = await startAuthentication2({ optionsJSON });
|
|
2574
|
+
} catch (e) {
|
|
2575
|
+
if (e.name === "NotAllowedError") throw new UserCancelledError();
|
|
2576
|
+
throw e;
|
|
2577
|
+
}
|
|
2578
|
+
const ext = response.clientExtensionResults;
|
|
2579
|
+
const prfResults = ext?.["prf"]?.results;
|
|
2580
|
+
let prfOutput;
|
|
2581
|
+
if (prfResults?.first) {
|
|
2582
|
+
prfOutput = typeof prfResults.first === "string" ? fromB64u(prfResults.first) : new Uint8Array(prfResults.first);
|
|
2583
|
+
}
|
|
2584
|
+
return { response, prfOutput };
|
|
2585
|
+
}
|
|
2480
2586
|
async function getPrfOutput(opts) {
|
|
2481
2587
|
const challenge = crypto.getRandomValues(new Uint8Array(32));
|
|
2482
2588
|
let cred;
|
|
@@ -2532,15 +2638,15 @@ async function verifyEmailCode(api, email, code) {
|
|
|
2532
2638
|
|
|
2533
2639
|
// src/social/signers/evm.ts
|
|
2534
2640
|
import { keccak256, Transaction, resolveProperties } from "ethers";
|
|
2535
|
-
async function signEvmTransaction(tee, chain, tx) {
|
|
2641
|
+
async function signEvmTransaction(tee, chain, tx, jwt) {
|
|
2536
2642
|
const resolved = await resolveProperties(tx);
|
|
2537
2643
|
const unsigned = Transaction.from(resolved).unsignedSerialized;
|
|
2538
2644
|
const payload = unsigned.startsWith("0x") ? unsigned.slice(2) : unsigned;
|
|
2539
2645
|
const txHash = keccak256("0x" + payload);
|
|
2540
|
-
return tee.sign({ chain, kind: "transaction", payload, txHash });
|
|
2646
|
+
return tee.sign({ chain, kind: "transaction", payload, txHash }, jwt);
|
|
2541
2647
|
}
|
|
2542
|
-
async function signEvmMessage(tee, chain, message) {
|
|
2543
|
-
return tee.sign({ chain, kind: "message", payload: message });
|
|
2648
|
+
async function signEvmMessage(tee, chain, message, jwt) {
|
|
2649
|
+
return tee.sign({ chain, kind: "message", payload: message }, jwt);
|
|
2544
2650
|
}
|
|
2545
2651
|
|
|
2546
2652
|
// src/social/signers/solana.ts
|
|
@@ -2553,13 +2659,13 @@ function b64(bytes) {
|
|
|
2553
2659
|
function unb64(s) {
|
|
2554
2660
|
return Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
|
|
2555
2661
|
}
|
|
2556
|
-
async function signSolanaTransaction(tee, chain, tx) {
|
|
2662
|
+
async function signSolanaTransaction(tee, chain, tx, jwt) {
|
|
2557
2663
|
const bytes = tx instanceof VTx ? tx.serialize() : tx.serialize({ requireAllSignatures: false });
|
|
2558
|
-
const signed = await tee.sign({ chain, kind: "transaction", payload: b64(bytes) });
|
|
2664
|
+
const signed = await tee.sign({ chain, kind: "transaction", payload: b64(bytes) }, jwt);
|
|
2559
2665
|
return unb64(signed);
|
|
2560
2666
|
}
|
|
2561
|
-
async function signSolanaMessage(tee, chain, message) {
|
|
2562
|
-
const sig = await tee.sign({ chain, kind: "message", payload: b64(message) });
|
|
2667
|
+
async function signSolanaMessage(tee, chain, message, jwt) {
|
|
2668
|
+
const sig = await tee.sign({ chain, kind: "message", payload: b64(message) }, jwt);
|
|
2563
2669
|
return unb64(sig);
|
|
2564
2670
|
}
|
|
2565
2671
|
|
|
@@ -2819,6 +2925,7 @@ function clearPersisted(key) {
|
|
|
2819
2925
|
}
|
|
2820
2926
|
}
|
|
2821
2927
|
async function createDecaneConnect(config) {
|
|
2928
|
+
if (!config.appId) throw new Error("decane-connect-kit: appId is required");
|
|
2822
2929
|
if (!config.apiKey) throw new Error("decane-connect-kit: apiKey is required");
|
|
2823
2930
|
if (!config.chains?.length)
|
|
2824
2931
|
throw new Error("decane-connect-kit: at least one chain is required");
|
|
@@ -2829,9 +2936,14 @@ async function createDecaneConnect(config) {
|
|
|
2829
2936
|
const PERSIST_KEY = makePersistKey(config.apiKey);
|
|
2830
2937
|
const teeUrl = config.teeUrl ?? DEFAULT_TEE_URL;
|
|
2831
2938
|
const events = new EventEmitter();
|
|
2832
|
-
const api = new ApiClient(baseUrl, config.apiKey);
|
|
2939
|
+
const api = new ApiClient(baseUrl, config.apiKey, config.appId);
|
|
2833
2940
|
const storage = new Storage();
|
|
2834
|
-
const session = new SessionManager(
|
|
2941
|
+
const session = new SessionManager(
|
|
2942
|
+
events,
|
|
2943
|
+
durationMs,
|
|
2944
|
+
teeUrl,
|
|
2945
|
+
config.requireAssertionPerSignature === true
|
|
2946
|
+
);
|
|
2835
2947
|
let auth = null;
|
|
2836
2948
|
let persisted = loadPersisted(PERSIST_KEY);
|
|
2837
2949
|
async function pinSetup() {
|
|
@@ -3079,6 +3191,45 @@ async function createDecaneConnect(config) {
|
|
|
3079
3191
|
}
|
|
3080
3192
|
return addresses;
|
|
3081
3193
|
}
|
|
3194
|
+
async function passkeyLoginCandidate() {
|
|
3195
|
+
if (!persisted?.userId || !isPasskeySupported()) return null;
|
|
3196
|
+
const stored = await storage.getShare(persisted.userId);
|
|
3197
|
+
if (!stored || stored.unlockMethod !== "passkey") return null;
|
|
3198
|
+
if (!stored.credentialId || !stored.prfSalt) return null;
|
|
3199
|
+
return stored;
|
|
3200
|
+
}
|
|
3201
|
+
async function passkeyLogin(stored) {
|
|
3202
|
+
const userId = stored.userId;
|
|
3203
|
+
const options = await api.getWebAuthnLoginOptions(userId, rpId);
|
|
3204
|
+
if (!options) {
|
|
3205
|
+
throw new NewDeviceError();
|
|
3206
|
+
}
|
|
3207
|
+
const { response, prfOutput } = await authenticateWithPasskey({
|
|
3208
|
+
optionsJSON: options,
|
|
3209
|
+
prfSalt: stored.prfSalt
|
|
3210
|
+
});
|
|
3211
|
+
if (!prfOutput) throw new ShareCorruptedError();
|
|
3212
|
+
const result = await api.postWebAuthnLogin(userId, response, window.location.origin);
|
|
3213
|
+
let deviceShare;
|
|
3214
|
+
try {
|
|
3215
|
+
deviceShare = await aesGcmDecrypt(
|
|
3216
|
+
await importAesKey(prfOutput),
|
|
3217
|
+
stored.wrappedDeviceShare,
|
|
3218
|
+
stored.iv
|
|
3219
|
+
);
|
|
3220
|
+
} catch {
|
|
3221
|
+
throw new ShareCorruptedError();
|
|
3222
|
+
}
|
|
3223
|
+
const rawAddresses = await api.getAddresses(result.jwt);
|
|
3224
|
+
const recordedAddresses = rawAddresses.evm && rawAddresses.solana ? { evm: rawAddresses.evm, solana: rawAddresses.solana } : null;
|
|
3225
|
+
const addresses = await session.start(deviceShare, result.jwt, recordedAddresses);
|
|
3226
|
+
zeroize(deviceShare);
|
|
3227
|
+
auth = { jwt: result.jwt, userId, addresses };
|
|
3228
|
+
persisted = { userId, addresses };
|
|
3229
|
+
savePersisted(PERSIST_KEY, persisted);
|
|
3230
|
+
events.emit("connected");
|
|
3231
|
+
return { addresses, isNewUser: false };
|
|
3232
|
+
}
|
|
3082
3233
|
async function handleAuthResult(result) {
|
|
3083
3234
|
let addresses;
|
|
3084
3235
|
if (!result.hasShare) {
|
|
@@ -3097,6 +3248,15 @@ async function createDecaneConnect(config) {
|
|
|
3097
3248
|
if (auth && session.isActive()) {
|
|
3098
3249
|
return { addresses: auth.addresses, isNewUser: false };
|
|
3099
3250
|
}
|
|
3251
|
+
const candidate = await passkeyLoginCandidate();
|
|
3252
|
+
if (candidate) {
|
|
3253
|
+
try {
|
|
3254
|
+
return await passkeyLogin(candidate);
|
|
3255
|
+
} catch (e) {
|
|
3256
|
+
if (e instanceof UserCancelledError) throw e;
|
|
3257
|
+
console.warn("decane-connect-kit: passkey sign-in unavailable, falling back to standard sign-in.", e);
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3100
3260
|
const method = config.authMethods[0];
|
|
3101
3261
|
if (!method) throw new CallSequenceError("No authMethods configured");
|
|
3102
3262
|
if (method === "google") return client.connectWithGoogle();
|
|
@@ -3129,6 +3289,11 @@ async function createDecaneConnect(config) {
|
|
|
3129
3289
|
return new Promise(() => {
|
|
3130
3290
|
});
|
|
3131
3291
|
},
|
|
3292
|
+
async connectWithToken(token, opts) {
|
|
3293
|
+
if (!token) throw new CallSequenceError("connectWithToken requires a token");
|
|
3294
|
+
const result = await api.authExternal(token, opts?.providerId);
|
|
3295
|
+
return handleAuthResult(result);
|
|
3296
|
+
},
|
|
3132
3297
|
async connectWithEmail(email) {
|
|
3133
3298
|
await startEmailAuth(api, email);
|
|
3134
3299
|
return { codeRequired: true };
|
|
@@ -3137,7 +3302,22 @@ async function createDecaneConnect(config) {
|
|
|
3137
3302
|
const result = await verifyEmailCode(api, email, code);
|
|
3138
3303
|
return handleAuthResult(result);
|
|
3139
3304
|
},
|
|
3305
|
+
async canSignInWithPasskey() {
|
|
3306
|
+
return await passkeyLoginCandidate() !== null;
|
|
3307
|
+
},
|
|
3308
|
+
async signInWithPasskey() {
|
|
3309
|
+
const candidate = await passkeyLoginCandidate();
|
|
3310
|
+
if (!candidate) throw new NewDeviceError();
|
|
3311
|
+
return passkeyLogin(candidate);
|
|
3312
|
+
},
|
|
3140
3313
|
async unlock() {
|
|
3314
|
+
if (!auth) {
|
|
3315
|
+
const candidate = await passkeyLoginCandidate();
|
|
3316
|
+
if (candidate) {
|
|
3317
|
+
await passkeyLogin(candidate);
|
|
3318
|
+
return;
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3141
3321
|
if (!auth) {
|
|
3142
3322
|
throw persisted ? new ReconnectRequiredError() : new CallSequenceError("Not connected \u2014 call connect() first");
|
|
3143
3323
|
}
|
|
@@ -3162,11 +3342,11 @@ async function createDecaneConnect(config) {
|
|
|
3162
3342
|
async signMessage(req) {
|
|
3163
3343
|
const tee = session.requireActive();
|
|
3164
3344
|
if (req.chain.startsWith("evm:")) {
|
|
3165
|
-
return signEvmMessage(tee, req.chain, req.message);
|
|
3345
|
+
return signEvmMessage(tee, req.chain, req.message, auth?.jwt);
|
|
3166
3346
|
}
|
|
3167
3347
|
if (req.chain.startsWith("solana:")) {
|
|
3168
3348
|
const msg = typeof req.message === "string" ? new TextEncoder().encode(req.message) : req.message;
|
|
3169
|
-
const sig = await signSolanaMessage(tee, req.chain, msg);
|
|
3349
|
+
const sig = await signSolanaMessage(tee, req.chain, msg, auth?.jwt);
|
|
3170
3350
|
return bufferToB64u(sig);
|
|
3171
3351
|
}
|
|
3172
3352
|
throw new CallSequenceError(`Unsupported chain: ${req.chain}`);
|
|
@@ -3214,7 +3394,7 @@ async function createDecaneConnect(config) {
|
|
|
3214
3394
|
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? 0n
|
|
3215
3395
|
} : { gasPrice: req.gasPrice ?? feeData.gasPrice ?? void 0 }
|
|
3216
3396
|
};
|
|
3217
|
-
const signedTx = await signEvmTransaction(tee, req.chain, tx);
|
|
3397
|
+
const signedTx = await signEvmTransaction(tee, req.chain, tx, auth?.jwt);
|
|
3218
3398
|
const txResponse = await provider.broadcastTransaction(signedTx);
|
|
3219
3399
|
return txResponse.hash;
|
|
3220
3400
|
},
|
|
@@ -3227,7 +3407,7 @@ async function createDecaneConnect(config) {
|
|
|
3227
3407
|
tx = Transaction2.from(serializedTx);
|
|
3228
3408
|
}
|
|
3229
3409
|
const solChain = config.chains.find((c) => c.startsWith("solana:")) ?? "solana:mainnet";
|
|
3230
|
-
return signSolanaTransaction(tee, solChain, tx);
|
|
3410
|
+
return signSolanaTransaction(tee, solChain, tx, auth?.jwt);
|
|
3231
3411
|
},
|
|
3232
3412
|
async disconnect() {
|
|
3233
3413
|
session.end();
|
|
@@ -3340,8 +3520,8 @@ async function createDecaneConnect(config) {
|
|
|
3340
3520
|
function bufferToB64u(buf6) {
|
|
3341
3521
|
return btoa(String.fromCharCode(...buf6)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
3342
3522
|
}
|
|
3343
|
-
function b64uToBuffer(
|
|
3344
|
-
const b644 =
|
|
3523
|
+
function b64uToBuffer(b64u2) {
|
|
3524
|
+
const b644 = b64u2.replace(/-/g, "+").replace(/_/g, "/");
|
|
3345
3525
|
return Uint8Array.from(atob(b644), (c) => c.charCodeAt(0));
|
|
3346
3526
|
}
|
|
3347
3527
|
function toHex(bytes) {
|
|
@@ -3728,11 +3908,16 @@ function WalletSelector({
|
|
|
3728
3908
|
socialAuthMethods = ["google", "email"],
|
|
3729
3909
|
onGoogleSignIn,
|
|
3730
3910
|
onEmailStart,
|
|
3731
|
-
onEmailVerify
|
|
3911
|
+
onEmailVerify,
|
|
3912
|
+
customAuthLabel,
|
|
3913
|
+
onCustomAuthSignIn,
|
|
3914
|
+
canUsePasskey = false,
|
|
3915
|
+
passkeyAddress = null,
|
|
3916
|
+
onPasskeySignIn
|
|
3732
3917
|
}) {
|
|
3733
3918
|
const showSocial = hasSocialMode(mode);
|
|
3734
3919
|
const showWallets = mode !== "social";
|
|
3735
|
-
const [view, setView] = useState(
|
|
3920
|
+
const [view, setView] = useState(showSocial ? "social" : "wallets");
|
|
3736
3921
|
const [search, setSearch] = useState("");
|
|
3737
3922
|
const [filter, setFilter] = useState("all");
|
|
3738
3923
|
const [pendingWallet, setPendingWallet] = useState(null);
|
|
@@ -3741,6 +3926,8 @@ function WalletSelector({
|
|
|
3741
3926
|
const [codeVal, setCodeVal] = useState("");
|
|
3742
3927
|
const [socialLoad, setSocialLoad] = useState(false);
|
|
3743
3928
|
const [socialErr, setSocialErr] = useState("");
|
|
3929
|
+
const [showOtherMethods, setShowOtherMethods] = useState(false);
|
|
3930
|
+
const passkeyFirst = canUsePasskey && !!onPasskeySignIn && !showOtherMethods;
|
|
3744
3931
|
useEffect(() => {
|
|
3745
3932
|
if (!open) return;
|
|
3746
3933
|
injectFont();
|
|
@@ -3756,17 +3943,36 @@ function WalletSelector({
|
|
|
3756
3943
|
setSearch("");
|
|
3757
3944
|
setFilter("all");
|
|
3758
3945
|
setPendingWallet(null);
|
|
3759
|
-
setView(
|
|
3946
|
+
setView(showSocial ? "social" : "wallets");
|
|
3760
3947
|
setEmailStep("input");
|
|
3761
3948
|
setEmailVal("");
|
|
3762
3949
|
setCodeVal("");
|
|
3763
3950
|
setSocialErr("");
|
|
3951
|
+
setShowOtherMethods(false);
|
|
3764
3952
|
return;
|
|
3765
3953
|
}
|
|
3766
3954
|
return () => {
|
|
3767
3955
|
document.getElementById("decane-tokens")?.remove();
|
|
3768
3956
|
};
|
|
3769
3957
|
}, [open, showWallets]);
|
|
3958
|
+
async function handlePasskeySignIn() {
|
|
3959
|
+
if (!onPasskeySignIn) return;
|
|
3960
|
+
setSocialLoad(true);
|
|
3961
|
+
setSocialErr("");
|
|
3962
|
+
try {
|
|
3963
|
+
await onPasskeySignIn();
|
|
3964
|
+
} catch (e) {
|
|
3965
|
+
const err = e;
|
|
3966
|
+
if (err?.name !== "UserCancelledError") {
|
|
3967
|
+
setSocialErr(err?.message ?? "Passkey sign-in failed");
|
|
3968
|
+
setShowOtherMethods(true);
|
|
3969
|
+
}
|
|
3970
|
+
setSocialLoad(false);
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
async function handlePasskeyClick() {
|
|
3974
|
+
void handlePasskeySignIn();
|
|
3975
|
+
}
|
|
3770
3976
|
async function handleGoogleClick() {
|
|
3771
3977
|
if (!onGoogleSignIn) return;
|
|
3772
3978
|
setSocialLoad(true);
|
|
@@ -3778,6 +3984,17 @@ function WalletSelector({
|
|
|
3778
3984
|
setSocialLoad(false);
|
|
3779
3985
|
}
|
|
3780
3986
|
}
|
|
3987
|
+
async function handleCustomAuthClick() {
|
|
3988
|
+
if (!onCustomAuthSignIn) return;
|
|
3989
|
+
setSocialLoad(true);
|
|
3990
|
+
setSocialErr("");
|
|
3991
|
+
try {
|
|
3992
|
+
await onCustomAuthSignIn();
|
|
3993
|
+
} catch (e) {
|
|
3994
|
+
setSocialErr(e.message);
|
|
3995
|
+
setSocialLoad(false);
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3781
3998
|
async function handleEmailStart(e) {
|
|
3782
3999
|
e.preventDefault();
|
|
3783
4000
|
if (!onEmailStart) return;
|
|
@@ -4052,7 +4269,7 @@ function WalletSelector({
|
|
|
4052
4269
|
}
|
|
4053
4270
|
},
|
|
4054
4271
|
/* @__PURE__ */ React2.createElement("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none" }, /* @__PURE__ */ React2.createElement("path", { d: "M1.5 1.5L10.5 10.5M10.5 1.5L1.5 10.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }))
|
|
4055
|
-
)), showWallets && showSocial && /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", gap: 4, marginBottom: 14, background: "var(--dc-surface)", borderRadius: 10, padding: 3 } }, ["
|
|
4272
|
+
)), showWallets && showSocial && /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", gap: 4, marginBottom: 14, background: "var(--dc-surface)", borderRadius: 10, padding: 3 } }, ["social", "wallets"].map((v) => /* @__PURE__ */ React2.createElement(
|
|
4056
4273
|
"button",
|
|
4057
4274
|
{
|
|
4058
4275
|
key: v,
|
|
@@ -4111,7 +4328,53 @@ function WalletSelector({
|
|
|
4111
4328
|
e.currentTarget.style.borderColor = "var(--dc-border)";
|
|
4112
4329
|
}
|
|
4113
4330
|
}
|
|
4114
|
-
))), view === "social" && /* @__PURE__ */ React2.createElement("div", { style: { padding: "4px 20px 24px", display: "flex", flexDirection: "column", gap: 10 } }, socialErr && /* @__PURE__ */ React2.createElement("div", { style: { background: "rgba(204,51,0,0.1)", border: "1px solid var(--dc-danger)", borderRadius: 8, padding: "9px 12px", fontSize: 12, color: "var(--dc-danger)" } }, socialErr),
|
|
4331
|
+
))), view === "social" && /* @__PURE__ */ React2.createElement("div", { style: { padding: "4px 20px 24px", display: "flex", flexDirection: "column", gap: 10 } }, socialErr && /* @__PURE__ */ React2.createElement("div", { style: { background: "rgba(204,51,0,0.1)", border: "1px solid var(--dc-danger)", borderRadius: 8, padding: "9px 12px", fontSize: 12, color: "var(--dc-danger)" } }, socialErr), passkeyFirst && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(
|
|
4332
|
+
"button",
|
|
4333
|
+
{
|
|
4334
|
+
onClick: handlePasskeyClick,
|
|
4335
|
+
disabled: socialLoad,
|
|
4336
|
+
style: {
|
|
4337
|
+
display: "flex",
|
|
4338
|
+
alignItems: "center",
|
|
4339
|
+
justifyContent: "center",
|
|
4340
|
+
gap: 10,
|
|
4341
|
+
width: "100%",
|
|
4342
|
+
padding: "13px 16px",
|
|
4343
|
+
background: "var(--dc-accent)",
|
|
4344
|
+
border: "none",
|
|
4345
|
+
borderRadius: 10,
|
|
4346
|
+
cursor: socialLoad ? "not-allowed" : "pointer",
|
|
4347
|
+
color: "var(--dc-accent-text)",
|
|
4348
|
+
fontSize: 14,
|
|
4349
|
+
fontWeight: 700,
|
|
4350
|
+
fontFamily: "inherit",
|
|
4351
|
+
opacity: socialLoad ? 0.6 : 1
|
|
4352
|
+
}
|
|
4353
|
+
},
|
|
4354
|
+
/* @__PURE__ */ React2.createElement("svg", { width: "17", height: "17", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }, /* @__PURE__ */ React2.createElement("circle", { cx: "12", cy: "8", r: "4" }), /* @__PURE__ */ React2.createElement("path", { d: "M6 21v-1a4 4 0 0 1 4-4h1" }), /* @__PURE__ */ React2.createElement("path", { d: "M17 15v6M14.5 18h5" })),
|
|
4355
|
+
socialLoad ? "Verifying\u2026" : "Continue with passkey"
|
|
4356
|
+
), passkeyAddress && /* @__PURE__ */ React2.createElement("div", { style: { textAlign: "center", fontSize: 12, color: "var(--dc-text-muted)" } }, "Resuming ", passkeyAddress.slice(0, 6), "\u2026", passkeyAddress.slice(-4)), /* @__PURE__ */ React2.createElement(
|
|
4357
|
+
"button",
|
|
4358
|
+
{
|
|
4359
|
+
onClick: () => {
|
|
4360
|
+
setShowOtherMethods(true);
|
|
4361
|
+
setSocialErr("");
|
|
4362
|
+
},
|
|
4363
|
+
disabled: socialLoad,
|
|
4364
|
+
style: {
|
|
4365
|
+
background: "none",
|
|
4366
|
+
border: "none",
|
|
4367
|
+
padding: "6px 0",
|
|
4368
|
+
color: "var(--dc-text-muted)",
|
|
4369
|
+
fontSize: 12.5,
|
|
4370
|
+
cursor: socialLoad ? "not-allowed" : "pointer",
|
|
4371
|
+
fontFamily: "inherit",
|
|
4372
|
+
textDecoration: "underline",
|
|
4373
|
+
textUnderlineOffset: 3
|
|
4374
|
+
}
|
|
4375
|
+
},
|
|
4376
|
+
"Use a different account"
|
|
4377
|
+
)), !passkeyFirst && socialAuthMethods.includes("google") && /* @__PURE__ */ React2.createElement(
|
|
4115
4378
|
"button",
|
|
4116
4379
|
{
|
|
4117
4380
|
onClick: handleGoogleClick,
|
|
@@ -4143,7 +4406,7 @@ function WalletSelector({
|
|
|
4143
4406
|
},
|
|
4144
4407
|
/* @__PURE__ */ React2.createElement("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none" }, /* @__PURE__ */ React2.createElement("path", { d: "M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.875 2.684-6.615z", fill: "#4285F4" }), /* @__PURE__ */ React2.createElement("path", { d: "M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 0 0 9 18z", fill: "#34A853" }), /* @__PURE__ */ React2.createElement("path", { d: "M3.964 10.71A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.957A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.957 4.042l3.007-2.332z", fill: "#FBBC05" }), /* @__PURE__ */ React2.createElement("path", { d: "M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 0 0 .957 4.958L3.964 6.29C4.672 4.163 6.656 3.58 9 3.58z", fill: "#EA4335" })),
|
|
4145
4408
|
socialLoad ? "Redirecting\u2026" : "Continue with Google"
|
|
4146
|
-
), socialAuthMethods.includes("google") && socialAuthMethods.includes("email") && /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React2.createElement("div", { style: { flex: 1, height: 1, background: "var(--dc-border)" } }), /* @__PURE__ */ React2.createElement("span", { style: { fontSize: 11, color: "var(--dc-text-muted)", whiteSpace: "nowrap" } }, "or continue with email"), /* @__PURE__ */ React2.createElement("div", { style: { flex: 1, height: 1, background: "var(--dc-border)" } })), socialAuthMethods.includes("email") && (emailStep === "input" ? /* @__PURE__ */ React2.createElement("form", { onSubmit: handleEmailStart, style: { display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ React2.createElement(
|
|
4409
|
+
), !passkeyFirst && socialAuthMethods.includes("google") && socialAuthMethods.includes("email") && /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React2.createElement("div", { style: { flex: 1, height: 1, background: "var(--dc-border)" } }), /* @__PURE__ */ React2.createElement("span", { style: { fontSize: 11, color: "var(--dc-text-muted)", whiteSpace: "nowrap" } }, "or continue with email"), /* @__PURE__ */ React2.createElement("div", { style: { flex: 1, height: 1, background: "var(--dc-border)" } })), !passkeyFirst && socialAuthMethods.includes("email") && (emailStep === "input" ? /* @__PURE__ */ React2.createElement("form", { onSubmit: handleEmailStart, style: { display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ React2.createElement(
|
|
4147
4410
|
"input",
|
|
4148
4411
|
{
|
|
4149
4412
|
type: "email",
|
|
@@ -4262,7 +4525,38 @@ function WalletSelector({
|
|
|
4262
4525
|
}
|
|
4263
4526
|
},
|
|
4264
4527
|
socialLoad ? "Verifying\u2026" : "Verify"
|
|
4265
|
-
))))
|
|
4528
|
+
)))), !passkeyFirst && customAuthLabel && onCustomAuthSignIn && /* @__PURE__ */ React2.createElement(React2.Fragment, null, (socialAuthMethods.includes("google") || socialAuthMethods.includes("email")) && /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10 } }, /* @__PURE__ */ React2.createElement("div", { style: { flex: 1, height: 1, background: "var(--dc-border)" } }), /* @__PURE__ */ React2.createElement("span", { style: { fontSize: 11, color: "var(--dc-text-muted)", whiteSpace: "nowrap" } }, "or"), /* @__PURE__ */ React2.createElement("div", { style: { flex: 1, height: 1, background: "var(--dc-border)" } })), /* @__PURE__ */ React2.createElement(
|
|
4529
|
+
"button",
|
|
4530
|
+
{
|
|
4531
|
+
onClick: handleCustomAuthClick,
|
|
4532
|
+
disabled: socialLoad,
|
|
4533
|
+
style: {
|
|
4534
|
+
display: "flex",
|
|
4535
|
+
alignItems: "center",
|
|
4536
|
+
justifyContent: "center",
|
|
4537
|
+
gap: 10,
|
|
4538
|
+
width: "100%",
|
|
4539
|
+
padding: "11px 16px",
|
|
4540
|
+
background: "var(--dc-surface)",
|
|
4541
|
+
border: "1px solid var(--dc-border)",
|
|
4542
|
+
borderRadius: 10,
|
|
4543
|
+
cursor: socialLoad ? "not-allowed" : "pointer",
|
|
4544
|
+
fontSize: 14,
|
|
4545
|
+
fontWeight: 600,
|
|
4546
|
+
color: "var(--dc-text)",
|
|
4547
|
+
fontFamily: "inherit",
|
|
4548
|
+
transition: "border-color 0.12s, background 0.12s",
|
|
4549
|
+
opacity: socialLoad ? 0.6 : 1
|
|
4550
|
+
},
|
|
4551
|
+
onMouseEnter: (e) => {
|
|
4552
|
+
if (!socialLoad) e.currentTarget.style.borderColor = "var(--dc-border-accent)";
|
|
4553
|
+
},
|
|
4554
|
+
onMouseLeave: (e) => {
|
|
4555
|
+
e.currentTarget.style.borderColor = "var(--dc-border)";
|
|
4556
|
+
}
|
|
4557
|
+
},
|
|
4558
|
+
socialLoad ? "Connecting\u2026" : customAuthLabel
|
|
4559
|
+
))), view === "wallets" && /* @__PURE__ */ React2.createElement("div", { style: { overflowY: "auto", flex: 1, padding: "0 12px 20px" } }, filtered.length > 0 ? /* @__PURE__ */ React2.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 2 } }, filtered.map((w) => /* @__PURE__ */ React2.createElement(WalletRow, { key: w.id, wallet: w, onClick: () => handleWalletClick(w) }))) : /* @__PURE__ */ React2.createElement("div", { style: { textAlign: "center", padding: "32px 16px", color: "var(--dc-text-muted)", fontSize: 13 } }, q ? `No wallets matching "${q}"` : "No wallets detected")), view === "wallets" && /* @__PURE__ */ React2.createElement("div", { style: { padding: "10px 20px 14px", borderTop: "1px solid var(--dc-border)", textAlign: "center", fontSize: 11, color: "var(--dc-text-muted)" } }, "Don't see your wallet? Make sure it's installed and unlocked"))
|
|
4266
4560
|
)
|
|
4267
4561
|
)
|
|
4268
4562
|
);
|
|
@@ -4384,13 +4678,16 @@ function SignInView({
|
|
|
4384
4678
|
onEmailStart,
|
|
4385
4679
|
onEmailVerify,
|
|
4386
4680
|
authMethods,
|
|
4387
|
-
resumeHint
|
|
4681
|
+
resumeHint,
|
|
4682
|
+
customAuthLabel,
|
|
4683
|
+
onCustomAuthSignIn
|
|
4388
4684
|
}) {
|
|
4389
4685
|
const [googleLoading, setGoogleLoading] = useState2(false);
|
|
4390
4686
|
const [email, setEmail] = useState2("");
|
|
4391
4687
|
const [code, setCode] = useState2("");
|
|
4392
4688
|
const [emailSent, setEmailSent] = useState2(false);
|
|
4393
4689
|
const [emailLoading, setEmailLoading] = useState2(false);
|
|
4690
|
+
const [customLoading, setCustomLoading] = useState2(false);
|
|
4394
4691
|
const [error, setError] = useState2("");
|
|
4395
4692
|
async function handleGoogle() {
|
|
4396
4693
|
setError("");
|
|
@@ -4427,6 +4724,18 @@ function SignInView({
|
|
|
4427
4724
|
setEmailLoading(false);
|
|
4428
4725
|
}
|
|
4429
4726
|
}
|
|
4727
|
+
async function handleCustomAuth() {
|
|
4728
|
+
if (!onCustomAuthSignIn) return;
|
|
4729
|
+
setError("");
|
|
4730
|
+
setCustomLoading(true);
|
|
4731
|
+
try {
|
|
4732
|
+
await onCustomAuthSignIn();
|
|
4733
|
+
} catch (e) {
|
|
4734
|
+
setError(e.message);
|
|
4735
|
+
} finally {
|
|
4736
|
+
setCustomLoading(false);
|
|
4737
|
+
}
|
|
4738
|
+
}
|
|
4430
4739
|
const inputStyle = {
|
|
4431
4740
|
width: "100%",
|
|
4432
4741
|
background: "var(--dc-surface)",
|
|
@@ -4499,7 +4808,27 @@ function SignInView({
|
|
|
4499
4808
|
fontWeight: 700,
|
|
4500
4809
|
cursor: "pointer",
|
|
4501
4810
|
fontFamily: "inherit"
|
|
4502
|
-
} }, emailLoading ? "\u2026" : "Verify"))),
|
|
4811
|
+
} }, emailLoading ? "\u2026" : "Verify"))), customAuthLabel && onCustomAuthSignIn && /* @__PURE__ */ React3.createElement(React3.Fragment, null, (authMethods.includes("google") || authMethods.includes("email")) && /* @__PURE__ */ React3.createElement("div", { style: { textAlign: "center", color: "var(--dc-text-muted)", fontSize: 12, margin: "10px 0" } }, "\u2014 or \u2014"), /* @__PURE__ */ React3.createElement(
|
|
4812
|
+
"button",
|
|
4813
|
+
{
|
|
4814
|
+
onClick: handleCustomAuth,
|
|
4815
|
+
disabled: customLoading,
|
|
4816
|
+
style: {
|
|
4817
|
+
width: "100%",
|
|
4818
|
+
padding: "10px 0",
|
|
4819
|
+
background: "var(--dc-surface)",
|
|
4820
|
+
color: "var(--dc-text)",
|
|
4821
|
+
border: "1px solid var(--dc-border)",
|
|
4822
|
+
borderRadius: 10,
|
|
4823
|
+
fontSize: 13,
|
|
4824
|
+
fontWeight: 600,
|
|
4825
|
+
cursor: customLoading ? "not-allowed" : "pointer",
|
|
4826
|
+
opacity: customLoading ? 0.7 : 1,
|
|
4827
|
+
fontFamily: "inherit"
|
|
4828
|
+
}
|
|
4829
|
+
},
|
|
4830
|
+
customLoading ? "Connecting\u2026" : customAuthLabel
|
|
4831
|
+
)), error && /* @__PURE__ */ React3.createElement("div", { style: { marginTop: 10, fontSize: 12, color: "var(--dc-danger, #ff5533)" } }, error));
|
|
4503
4832
|
}
|
|
4504
4833
|
function RecoverySection({
|
|
4505
4834
|
addresses,
|
|
@@ -4616,6 +4945,8 @@ function SocialWalletModal({
|
|
|
4616
4945
|
onEmailStart,
|
|
4617
4946
|
onEmailVerify,
|
|
4618
4947
|
authMethods = ["google", "email"],
|
|
4948
|
+
customAuthLabel,
|
|
4949
|
+
onCustomAuthSignIn,
|
|
4619
4950
|
onRotateShares,
|
|
4620
4951
|
onExportPortableBackup,
|
|
4621
4952
|
rpcUrls,
|
|
@@ -4771,7 +5102,9 @@ function SocialWalletModal({
|
|
|
4771
5102
|
onEmailStart,
|
|
4772
5103
|
onEmailVerify,
|
|
4773
5104
|
authMethods,
|
|
4774
|
-
resumeHint: socialAuth.needsReconnect ? "your wallet" : void 0
|
|
5105
|
+
resumeHint: socialAuth.needsReconnect ? "your wallet" : void 0,
|
|
5106
|
+
customAuthLabel,
|
|
5107
|
+
onCustomAuthSignIn
|
|
4775
5108
|
}
|
|
4776
5109
|
) : /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("div", { style: {
|
|
4777
5110
|
margin: "0 16px 14px",
|
|
@@ -4864,7 +5197,8 @@ var DEFAULT_SOCIAL = {
|
|
|
4864
5197
|
isNewUser: false,
|
|
4865
5198
|
isUnlocked: false,
|
|
4866
5199
|
sessionExpiresAt: null,
|
|
4867
|
-
needsReconnect: false
|
|
5200
|
+
needsReconnect: false,
|
|
5201
|
+
canUsePasskey: false
|
|
4868
5202
|
};
|
|
4869
5203
|
var DecaneContext = createContext(null);
|
|
4870
5204
|
function hasSocial(mode) {
|
|
@@ -4879,6 +5213,16 @@ function deriveNetworks(mode, explicit) {
|
|
|
4879
5213
|
}
|
|
4880
5214
|
function DecaneKit({ config, children }) {
|
|
4881
5215
|
const mode = config?.mode ?? "wallets";
|
|
5216
|
+
if (!config?.appId) {
|
|
5217
|
+
throw new Error(
|
|
5218
|
+
"DecaneKit: config.appId is required. Pass your dashboard project ID."
|
|
5219
|
+
);
|
|
5220
|
+
}
|
|
5221
|
+
if (hasSocial(mode) && !config.social?.apiKey) {
|
|
5222
|
+
throw new Error(
|
|
5223
|
+
`DecaneKit: config.social.apiKey is required for social sign-in (mode "${mode}"). Create an API key for this project in the dashboard.`
|
|
5224
|
+
);
|
|
5225
|
+
}
|
|
4882
5226
|
const walletRef = useRef(null);
|
|
4883
5227
|
if (!walletRef.current) walletRef.current = createDecane(config);
|
|
4884
5228
|
const socialRef = useRef(null);
|
|
@@ -4909,6 +5253,7 @@ function DecaneKit({ config, children }) {
|
|
|
4909
5253
|
socialInitialized.current = true;
|
|
4910
5254
|
const { apiKey, authMethods, promptPin, apiBaseUrl, chains, rpcUrls } = config.social;
|
|
4911
5255
|
createDecaneConnect({
|
|
5256
|
+
appId: config.appId,
|
|
4912
5257
|
apiKey,
|
|
4913
5258
|
apiBaseUrl,
|
|
4914
5259
|
authMethods: authMethods ?? ["google", "email"],
|
|
@@ -4926,6 +5271,8 @@ function DecaneKit({ config, children }) {
|
|
|
4926
5271
|
if (saved) {
|
|
4927
5272
|
setSocialAuth((p) => ({ ...p, addresses: saved, isUnlocked: false, needsReconnect: true }));
|
|
4928
5273
|
}
|
|
5274
|
+
sdk.canSignInWithPasskey().then((can) => setSocialAuth((p) => ({ ...p, canUsePasskey: can }))).catch(() => {
|
|
5275
|
+
});
|
|
4929
5276
|
const params = new URLSearchParams(window.location.search);
|
|
4930
5277
|
if (params.has("decane_jwt") || params.has("decane_error")) {
|
|
4931
5278
|
sdk.connectWithGoogle().then(applyResult).catch(console.error);
|
|
@@ -4939,9 +5286,20 @@ function DecaneKit({ config, children }) {
|
|
|
4939
5286
|
isNewUser: result.isNewUser,
|
|
4940
5287
|
isUnlocked: sdk?.isUnlocked() ?? false,
|
|
4941
5288
|
sessionExpiresAt: sdk?.sessionExpiresAt() ?? null,
|
|
4942
|
-
needsReconnect: false
|
|
5289
|
+
needsReconnect: false,
|
|
5290
|
+
canUsePasskey: false
|
|
5291
|
+
});
|
|
5292
|
+
sdk?.canSignInWithPasskey().then((can) => setSocialAuth((p) => ({ ...p, canUsePasskey: can }))).catch(() => {
|
|
4943
5293
|
});
|
|
4944
5294
|
}
|
|
5295
|
+
const signInWithPasskey = useCallback3(async () => {
|
|
5296
|
+
const sdk = socialRef.current;
|
|
5297
|
+
if (!sdk) throw new Error("Social sign-in not configured");
|
|
5298
|
+
const result = await sdk.signInWithPasskey();
|
|
5299
|
+
applyResult(result);
|
|
5300
|
+
setSelectorOpen(false);
|
|
5301
|
+
return result;
|
|
5302
|
+
}, []);
|
|
4945
5303
|
const connectWithGoogle = useCallback3(async () => {
|
|
4946
5304
|
const sdk = socialRef.current;
|
|
4947
5305
|
if (!sdk) throw new Error("Social sign-in not configured");
|
|
@@ -4959,6 +5317,19 @@ function DecaneKit({ config, children }) {
|
|
|
4959
5317
|
applyResult(result);
|
|
4960
5318
|
setSelectorOpen(false);
|
|
4961
5319
|
}, []);
|
|
5320
|
+
const connectWithToken = useCallback3(async (token, opts) => {
|
|
5321
|
+
const sdk = socialRef.current;
|
|
5322
|
+
if (!sdk) throw new Error("Social sign-in not configured");
|
|
5323
|
+
const result = await sdk.connectWithToken(token, opts);
|
|
5324
|
+
applyResult(result);
|
|
5325
|
+
setSelectorOpen(false);
|
|
5326
|
+
}, []);
|
|
5327
|
+
const customAuthConfig = config?.social?.customAuth;
|
|
5328
|
+
const connectWithCustomAuth = useCallback3(async () => {
|
|
5329
|
+
if (!customAuthConfig) throw new Error("Custom auth not configured");
|
|
5330
|
+
const token = await customAuthConfig.getToken();
|
|
5331
|
+
await connectWithToken(token, customAuthConfig.providerId ? { providerId: customAuthConfig.providerId } : void 0);
|
|
5332
|
+
}, [customAuthConfig, connectWithToken]);
|
|
4962
5333
|
const socialUnlock = useCallback3(async () => {
|
|
4963
5334
|
const sdk = socialRef.current;
|
|
4964
5335
|
if (!sdk) return;
|
|
@@ -5035,9 +5406,13 @@ function DecaneKit({ config, children }) {
|
|
|
5035
5406
|
setTheme,
|
|
5036
5407
|
mode,
|
|
5037
5408
|
socialAuth,
|
|
5409
|
+
signInWithPasskey,
|
|
5038
5410
|
connectWithGoogle,
|
|
5039
5411
|
connectWithEmail,
|
|
5040
5412
|
verifyEmailCode: verifyEmailCode2,
|
|
5413
|
+
connectWithToken,
|
|
5414
|
+
customAuthLabel: customAuthConfig?.label,
|
|
5415
|
+
connectWithCustomAuth: customAuthConfig ? connectWithCustomAuth : void 0,
|
|
5041
5416
|
socialUnlock,
|
|
5042
5417
|
socialDisconnect,
|
|
5043
5418
|
socialSignMessage,
|
|
@@ -5067,7 +5442,12 @@ function DecaneKit({ config, children }) {
|
|
|
5067
5442
|
socialAuthMethods: config?.social?.authMethods ?? ["google", "email"],
|
|
5068
5443
|
onGoogleSignIn: connectWithGoogle,
|
|
5069
5444
|
onEmailStart: connectWithEmail,
|
|
5070
|
-
onEmailVerify: verifyEmailCode2
|
|
5445
|
+
onEmailVerify: verifyEmailCode2,
|
|
5446
|
+
canUsePasskey: socialAuth.canUsePasskey,
|
|
5447
|
+
passkeyAddress: socialAuth.addresses?.evm ?? socialAuth.addresses?.solana ?? null,
|
|
5448
|
+
onPasskeySignIn: signInWithPasskey,
|
|
5449
|
+
customAuthLabel: customAuthConfig?.label,
|
|
5450
|
+
onCustomAuthSignIn: customAuthConfig ? connectWithCustomAuth : void 0
|
|
5071
5451
|
}
|
|
5072
5452
|
),
|
|
5073
5453
|
/* @__PURE__ */ React4.createElement(
|
|
@@ -5079,6 +5459,8 @@ function DecaneKit({ config, children }) {
|
|
|
5079
5459
|
socialAuth,
|
|
5080
5460
|
onUnlock: socialUnlock,
|
|
5081
5461
|
onDisconnect: socialDisconnect,
|
|
5462
|
+
customAuthLabel: customAuthConfig?.label,
|
|
5463
|
+
onCustomAuthSignIn: customAuthConfig ? connectWithCustomAuth : void 0,
|
|
5082
5464
|
onGoogleSignIn: connectWithGoogle,
|
|
5083
5465
|
onEmailStart: connectWithEmail,
|
|
5084
5466
|
onEmailVerify: verifyEmailCode2,
|
|
@@ -5109,11 +5491,16 @@ function useSocialAuth() {
|
|
|
5109
5491
|
connectWithGoogle,
|
|
5110
5492
|
connectWithEmail,
|
|
5111
5493
|
verifyEmailCode: verifyEmailCode2,
|
|
5494
|
+
connectWithToken,
|
|
5495
|
+
customAuthLabel,
|
|
5496
|
+
connectWithCustomAuth,
|
|
5112
5497
|
socialUnlock,
|
|
5113
5498
|
socialDisconnect
|
|
5114
5499
|
} = useDecaneContext();
|
|
5115
5500
|
const [googleLoading, setGoogleLoading] = useState4(false);
|
|
5116
5501
|
const [emailLoading, setEmailLoading] = useState4(false);
|
|
5502
|
+
const [tokenLoading, setTokenLoading] = useState4(false);
|
|
5503
|
+
const [customLoading, setCustomLoading] = useState4(false);
|
|
5117
5504
|
const [error, setError] = useState4(null);
|
|
5118
5505
|
const signInWithGoogle = useCallback4(async () => {
|
|
5119
5506
|
setGoogleLoading(true);
|
|
@@ -5150,6 +5537,31 @@ function useSocialAuth() {
|
|
|
5150
5537
|
setEmailLoading(false);
|
|
5151
5538
|
}
|
|
5152
5539
|
}, [verifyEmailCode2]);
|
|
5540
|
+
const signInWithToken = useCallback4(async (token, opts) => {
|
|
5541
|
+
setTokenLoading(true);
|
|
5542
|
+
setError(null);
|
|
5543
|
+
try {
|
|
5544
|
+
await connectWithToken(token, opts);
|
|
5545
|
+
} catch (e) {
|
|
5546
|
+
setError(e.message);
|
|
5547
|
+
throw e;
|
|
5548
|
+
} finally {
|
|
5549
|
+
setTokenLoading(false);
|
|
5550
|
+
}
|
|
5551
|
+
}, [connectWithToken]);
|
|
5552
|
+
const signInWithCustomAuth = useCallback4(async () => {
|
|
5553
|
+
if (!connectWithCustomAuth) throw new Error("Custom auth not configured");
|
|
5554
|
+
setCustomLoading(true);
|
|
5555
|
+
setError(null);
|
|
5556
|
+
try {
|
|
5557
|
+
await connectWithCustomAuth();
|
|
5558
|
+
} catch (e) {
|
|
5559
|
+
setError(e.message);
|
|
5560
|
+
throw e;
|
|
5561
|
+
} finally {
|
|
5562
|
+
setCustomLoading(false);
|
|
5563
|
+
}
|
|
5564
|
+
}, [connectWithCustomAuth]);
|
|
5153
5565
|
const unlock = useCallback4(async () => {
|
|
5154
5566
|
setError(null);
|
|
5155
5567
|
try {
|
|
@@ -5181,11 +5593,16 @@ function useSocialAuth() {
|
|
|
5181
5593
|
needsReconnect: socialAuth.needsReconnect,
|
|
5182
5594
|
googleLoading,
|
|
5183
5595
|
emailLoading,
|
|
5596
|
+
tokenLoading,
|
|
5597
|
+
customLoading,
|
|
5598
|
+
customAuthLabel,
|
|
5184
5599
|
error,
|
|
5185
5600
|
clearError: () => setError(null),
|
|
5186
5601
|
signInWithGoogle,
|
|
5187
5602
|
sendEmailCode,
|
|
5188
5603
|
confirmEmailCode,
|
|
5604
|
+
signInWithToken,
|
|
5605
|
+
signInWithCustomAuth: connectWithCustomAuth ? signInWithCustomAuth : void 0,
|
|
5189
5606
|
unlock,
|
|
5190
5607
|
disconnect
|
|
5191
5608
|
};
|