@dexterai/connect 0.3.0 → 0.4.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.
|
@@ -276,7 +276,7 @@ function getActiveHandle() {
|
|
|
276
276
|
return null;
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
|
-
function setActiveHandle(handle, label) {
|
|
279
|
+
function setActiveHandle(handle, label, credentialId) {
|
|
280
280
|
if (!hasStorage() || !handle) return;
|
|
281
281
|
try {
|
|
282
282
|
window.localStorage.setItem(ACTIVE_HANDLE_KEY, handle);
|
|
@@ -289,12 +289,16 @@ function setActiveHandle(handle, label) {
|
|
|
289
289
|
if (existing) {
|
|
290
290
|
existing.lastUsedAt = now;
|
|
291
291
|
if (label !== void 0) existing.label = label;
|
|
292
|
+
if (credentialId !== void 0) existing.credentialId = credentialId;
|
|
292
293
|
} else {
|
|
293
|
-
roster.push({ handle, label, lastUsedAt: now });
|
|
294
|
+
roster.push({ handle, label, credentialId, lastUsedAt: now });
|
|
294
295
|
}
|
|
295
296
|
writeRoster(roster);
|
|
296
297
|
emit();
|
|
297
298
|
}
|
|
299
|
+
function getCredentialId(handle) {
|
|
300
|
+
return readRoster().find((w) => w.handle === handle)?.credentialId;
|
|
301
|
+
}
|
|
298
302
|
function ejectActiveWallet(opts) {
|
|
299
303
|
if (!hasStorage()) return;
|
|
300
304
|
const current = getActiveHandle();
|
|
@@ -340,6 +344,66 @@ function onStorageEvent(e) {
|
|
|
340
344
|
}
|
|
341
345
|
var ACTIVE_WALLET_STORAGE_KEY = ACTIVE_HANDLE_KEY;
|
|
342
346
|
|
|
347
|
+
// src/signals.ts
|
|
348
|
+
function pkc() {
|
|
349
|
+
if (typeof window === "undefined") return null;
|
|
350
|
+
const g = globalThis.PublicKeyCredential;
|
|
351
|
+
return g ?? null;
|
|
352
|
+
}
|
|
353
|
+
function defaultRpId() {
|
|
354
|
+
return typeof window !== "undefined" ? window.location.hostname : "";
|
|
355
|
+
}
|
|
356
|
+
function passkeySignalSupport() {
|
|
357
|
+
const p = pkc();
|
|
358
|
+
return {
|
|
359
|
+
rename: typeof p?.signalCurrentUserDetails === "function",
|
|
360
|
+
prune: typeof p?.signalUnknownCredential === "function",
|
|
361
|
+
syncAccepted: typeof p?.signalAllAcceptedCredentials === "function"
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
async function renamePasskey(args) {
|
|
365
|
+
const p = pkc();
|
|
366
|
+
if (typeof p?.signalCurrentUserDetails !== "function") return false;
|
|
367
|
+
try {
|
|
368
|
+
await p.signalCurrentUserDetails({
|
|
369
|
+
rpId: args.rpId ?? defaultRpId(),
|
|
370
|
+
userId: args.userId,
|
|
371
|
+
name: args.name,
|
|
372
|
+
displayName: args.displayName ?? args.name
|
|
373
|
+
});
|
|
374
|
+
return true;
|
|
375
|
+
} catch {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
async function prunePasskey(args) {
|
|
380
|
+
const p = pkc();
|
|
381
|
+
if (typeof p?.signalUnknownCredential !== "function") return false;
|
|
382
|
+
try {
|
|
383
|
+
await p.signalUnknownCredential({
|
|
384
|
+
rpId: args.rpId ?? defaultRpId(),
|
|
385
|
+
credentialId: args.credentialId
|
|
386
|
+
});
|
|
387
|
+
return true;
|
|
388
|
+
} catch {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
async function syncAcceptedPasskeys(args) {
|
|
393
|
+
const p = pkc();
|
|
394
|
+
if (typeof p?.signalAllAcceptedCredentials !== "function") return false;
|
|
395
|
+
try {
|
|
396
|
+
await p.signalAllAcceptedCredentials({
|
|
397
|
+
rpId: args.rpId ?? defaultRpId(),
|
|
398
|
+
userId: args.userId,
|
|
399
|
+
allAcceptedCredentialIds: args.acceptedCredentialIds
|
|
400
|
+
});
|
|
401
|
+
return true;
|
|
402
|
+
} catch {
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
343
407
|
export {
|
|
344
408
|
ConnectError,
|
|
345
409
|
passkeyLogin,
|
|
@@ -347,10 +411,15 @@ export {
|
|
|
347
411
|
createPasskeySigner,
|
|
348
412
|
getActiveHandle,
|
|
349
413
|
setActiveHandle,
|
|
414
|
+
getCredentialId,
|
|
350
415
|
ejectActiveWallet,
|
|
351
416
|
listWallets,
|
|
352
417
|
switchWallet,
|
|
353
418
|
forgetWallet,
|
|
354
419
|
subscribe,
|
|
355
|
-
ACTIVE_WALLET_STORAGE_KEY
|
|
420
|
+
ACTIVE_WALLET_STORAGE_KEY,
|
|
421
|
+
passkeySignalSupport,
|
|
422
|
+
renamePasskey,
|
|
423
|
+
prunePasskey,
|
|
424
|
+
syncAcceptedPasskeys
|
|
356
425
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as DexterConnectConfig, S as SignInResult, C as ConnectVault } from './
|
|
2
|
-
export { A as ACTIVE_WALLET_STORAGE_KEY, a as ConnectError, P as PasskeyLoginTokens, b as StoredWallet, e as ejectActiveWallet, f as forgetWallet, g as getActiveHandle, l as listWallets, s as setActiveHandle,
|
|
1
|
+
import { D as DexterConnectConfig, S as SignInResult, C as ConnectVault } from './signals-CkBFwCNw.js';
|
|
2
|
+
export { A as ACTIVE_WALLET_STORAGE_KEY, a as ConnectError, P as PasskeyLoginTokens, b as PasskeySignalSupport, c as StoredWallet, e as ejectActiveWallet, f as forgetWallet, g as getActiveHandle, d as getCredentialId, l as listWallets, p as passkeySignalSupport, h as prunePasskey, r as renamePasskey, s as setActiveHandle, i as subscribeWallet, j as switchWallet, k as syncAcceptedPasskeys } from './signals-CkBFwCNw.js';
|
|
3
3
|
import { DexterApiBrowserPasskeySigner } from '@dexterai/vault/signers/browser';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/index.js
CHANGED
|
@@ -6,12 +6,17 @@ import {
|
|
|
6
6
|
ejectActiveWallet,
|
|
7
7
|
forgetWallet,
|
|
8
8
|
getActiveHandle,
|
|
9
|
+
getCredentialId,
|
|
9
10
|
listWallets,
|
|
10
11
|
passkeyLogin,
|
|
12
|
+
passkeySignalSupport,
|
|
13
|
+
prunePasskey,
|
|
14
|
+
renamePasskey,
|
|
11
15
|
setActiveHandle,
|
|
12
16
|
subscribe,
|
|
13
|
-
switchWallet
|
|
14
|
-
|
|
17
|
+
switchWallet,
|
|
18
|
+
syncAcceptedPasskeys
|
|
19
|
+
} from "./chunk-2V6EGIHV.js";
|
|
15
20
|
export {
|
|
16
21
|
ACTIVE_WALLET_STORAGE_KEY,
|
|
17
22
|
ConnectError,
|
|
@@ -20,9 +25,14 @@ export {
|
|
|
20
25
|
ejectActiveWallet,
|
|
21
26
|
forgetWallet,
|
|
22
27
|
getActiveHandle,
|
|
28
|
+
getCredentialId,
|
|
23
29
|
listWallets,
|
|
24
30
|
passkeyLogin,
|
|
31
|
+
passkeySignalSupport,
|
|
32
|
+
prunePasskey,
|
|
33
|
+
renamePasskey,
|
|
25
34
|
setActiveHandle,
|
|
26
35
|
subscribe as subscribeWallet,
|
|
27
|
-
switchWallet
|
|
36
|
+
switchWallet,
|
|
37
|
+
syncAcceptedPasskeys
|
|
28
38
|
};
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DexterApiBrowserPasskeySigner } from '@dexterai/vault/signers/browser';
|
|
2
|
-
import { S as SignInResult, P as PasskeyLoginTokens, C as ConnectVault, a as ConnectError,
|
|
2
|
+
import { S as SignInResult, P as PasskeyLoginTokens, C as ConnectVault, a as ConnectError, c as StoredWallet, b as PasskeySignalSupport } from './signals-CkBFwCNw.js';
|
|
3
3
|
import { ReactElement } from 'react';
|
|
4
4
|
|
|
5
5
|
type ConnectStatus = 'idle' | 'pending' | 'done' | 'error';
|
|
@@ -69,19 +69,28 @@ interface UseDexterWallet {
|
|
|
69
69
|
activeHandle: string | null;
|
|
70
70
|
/** Known wallets on this browser, most-recently-used first. */
|
|
71
71
|
wallets: StoredWallet[];
|
|
72
|
+
/** What the WebAuthn Signal API supports in THIS browser (rename / prune). */
|
|
73
|
+
support: PasskeySignalSupport;
|
|
72
74
|
/**
|
|
73
|
-
* Eject the active wallet — "switch / start fresh
|
|
74
|
-
*
|
|
75
|
-
*
|
|
75
|
+
* Eject the active wallet — "switch / start fresh". Clears the local binding
|
|
76
|
+
* and, where supported, prunes the old passkey from the OS manager so it
|
|
77
|
+
* disappears from the user's list. `{ forget: true }` also drops it from the
|
|
78
|
+
* roster.
|
|
76
79
|
*/
|
|
77
80
|
eject: (opts?: {
|
|
78
81
|
forget?: boolean;
|
|
79
82
|
}) => void;
|
|
80
83
|
/** Switch the active wallet to a known handle. No-op if unknown. */
|
|
81
84
|
switchTo: (handle: string) => boolean;
|
|
82
|
-
/** Record/activate a handle (after enroll or recover). Prefer
|
|
83
|
-
* hand
|
|
84
|
-
setActive: (handle: string, label?: string) => void;
|
|
85
|
+
/** Record/activate a handle (after enroll or recover). Prefer over writing
|
|
86
|
+
* localStorage by hand so the roster + subscribers stay correct. */
|
|
87
|
+
setActive: (handle: string, label?: string, credentialId?: string) => void;
|
|
88
|
+
/**
|
|
89
|
+
* Rename the ACTIVE passkey in the OS keychain (post-creation). Returns true
|
|
90
|
+
* if the browser supported it and the signal fired; false otherwise (the
|
|
91
|
+
* keychain entry is then just left as-is).
|
|
92
|
+
*/
|
|
93
|
+
rename: (name: string, displayName?: string) => Promise<boolean>;
|
|
85
94
|
}
|
|
86
95
|
declare function useDexterWallet(): UseDexterWallet;
|
|
87
96
|
|
package/dist/react.js
CHANGED
|
@@ -3,12 +3,16 @@ import {
|
|
|
3
3
|
createPasskeySigner,
|
|
4
4
|
ejectActiveWallet,
|
|
5
5
|
getActiveHandle,
|
|
6
|
+
getCredentialId,
|
|
6
7
|
listWallets,
|
|
7
8
|
passkeyLogin,
|
|
9
|
+
passkeySignalSupport,
|
|
10
|
+
prunePasskey,
|
|
11
|
+
renamePasskey,
|
|
8
12
|
setActiveHandle,
|
|
9
13
|
subscribe,
|
|
10
14
|
switchWallet
|
|
11
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-2V6EGIHV.js";
|
|
12
16
|
|
|
13
17
|
// src/useSignInWithDexter.ts
|
|
14
18
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
@@ -207,23 +211,44 @@ var DISCONNECT = {
|
|
|
207
211
|
|
|
208
212
|
// src/useDexterWallet.ts
|
|
209
213
|
import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
|
|
214
|
+
var NO_SUPPORT = { rename: false, prune: false, syncAccepted: false };
|
|
210
215
|
function useDexterWallet() {
|
|
211
216
|
const [activeHandle, setHandle] = useState2(() => getActiveHandle());
|
|
212
217
|
const [wallets, setWallets] = useState2(() => listWallets());
|
|
218
|
+
const [support, setSupport] = useState2(NO_SUPPORT);
|
|
213
219
|
useEffect2(() => {
|
|
214
220
|
const sync = () => {
|
|
215
221
|
setHandle(getActiveHandle());
|
|
216
222
|
setWallets(listWallets());
|
|
217
223
|
};
|
|
224
|
+
setSupport(passkeySignalSupport());
|
|
218
225
|
sync();
|
|
219
226
|
return subscribe(sync);
|
|
220
227
|
}, []);
|
|
228
|
+
const eject = useCallback2((opts) => {
|
|
229
|
+
const handle = getActiveHandle();
|
|
230
|
+
const credentialId = handle ? getCredentialId(handle) : void 0;
|
|
231
|
+
ejectActiveWallet(opts);
|
|
232
|
+
if (credentialId) void prunePasskey({ credentialId });
|
|
233
|
+
}, []);
|
|
234
|
+
const rename = useCallback2(async (name, displayName) => {
|
|
235
|
+
const handle = getActiveHandle();
|
|
236
|
+
if (!handle) return false;
|
|
237
|
+
const ok = await renamePasskey({ userId: handle, name, displayName });
|
|
238
|
+
if (ok) setActiveHandle(handle, name);
|
|
239
|
+
return ok;
|
|
240
|
+
}, []);
|
|
221
241
|
return {
|
|
222
242
|
activeHandle,
|
|
223
243
|
wallets,
|
|
224
|
-
|
|
244
|
+
support,
|
|
245
|
+
eject,
|
|
225
246
|
switchTo: useCallback2((handle) => switchWallet(handle), []),
|
|
226
|
-
setActive: useCallback2(
|
|
247
|
+
setActive: useCallback2(
|
|
248
|
+
(handle, label, credentialId) => setActiveHandle(handle, label, credentialId),
|
|
249
|
+
[]
|
|
250
|
+
),
|
|
251
|
+
rename
|
|
227
252
|
};
|
|
228
253
|
}
|
|
229
254
|
export {
|
|
@@ -48,6 +48,9 @@ interface StoredWallet {
|
|
|
48
48
|
handle: string;
|
|
49
49
|
/** Human label for switch UIs (e.g. an email, or "Dexter Wallet"). */
|
|
50
50
|
label?: string;
|
|
51
|
+
/** base64url credential id — enables the WebAuthn Signal API to prune this
|
|
52
|
+
* passkey from the OS manager on eject (see ./signals). */
|
|
53
|
+
credentialId?: string;
|
|
51
54
|
/** Epoch ms of last activation — for ordering the switcher. */
|
|
52
55
|
lastUsedAt?: number;
|
|
53
56
|
}
|
|
@@ -58,7 +61,9 @@ declare function getActiveHandle(): string | null;
|
|
|
58
61
|
* Set the active wallet handle (e.g. after enroll or recover), upserting it into
|
|
59
62
|
* the roster with a fresh `lastUsedAt`. Idempotent. Fires subscribers.
|
|
60
63
|
*/
|
|
61
|
-
declare function setActiveHandle(handle: string, label?: string): void;
|
|
64
|
+
declare function setActiveHandle(handle: string, label?: string, credentialId?: string): void;
|
|
65
|
+
/** Look up a known wallet's stored credentialId (for Signal-API prune on eject). */
|
|
66
|
+
declare function getCredentialId(handle: string): string | undefined;
|
|
62
67
|
/**
|
|
63
68
|
* EJECT — clear the active wallet so the browser is no longer bound to it. This
|
|
64
69
|
* is "switch / start fresh / sign out of this wallet". The wallet stays in the
|
|
@@ -89,4 +94,49 @@ declare function subscribe(listener: Listener): () => void;
|
|
|
89
94
|
* tests). Prefer the accessors above — do NOT read localStorage by hand. */
|
|
90
95
|
declare const ACTIVE_WALLET_STORAGE_KEY = "dexter:passkey:userHandle";
|
|
91
96
|
|
|
92
|
-
|
|
97
|
+
interface PasskeySignalSupport {
|
|
98
|
+
/** signalCurrentUserDetails — rename a passkey post-creation. */
|
|
99
|
+
rename: boolean;
|
|
100
|
+
/** signalUnknownCredential — remove one stale passkey from the manager. */
|
|
101
|
+
prune: boolean;
|
|
102
|
+
/** signalAllAcceptedCredentials — reconcile the full valid set. */
|
|
103
|
+
syncAccepted: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* What the CURRENT browser supports, by direct feature-detection. Instant, no
|
|
107
|
+
* network, no UA sniffing — tells you exactly what will light up on THIS device
|
|
108
|
+
* (e.g. call once on Branch's iPhone to learn its Safari's status).
|
|
109
|
+
*/
|
|
110
|
+
declare function passkeySignalSupport(): PasskeySignalSupport;
|
|
111
|
+
/**
|
|
112
|
+
* Rename a passkey in the OS keychain AFTER creation. `userId` is the base64url
|
|
113
|
+
* user handle; `rpId` defaults to the current host. Returns true if the signal
|
|
114
|
+
* fired, false if unsupported/failed (caller treats false as "left as-is").
|
|
115
|
+
*/
|
|
116
|
+
declare function renamePasskey(args: {
|
|
117
|
+
userId: string;
|
|
118
|
+
name: string;
|
|
119
|
+
displayName?: string;
|
|
120
|
+
rpId?: string;
|
|
121
|
+
}): Promise<boolean>;
|
|
122
|
+
/**
|
|
123
|
+
* Tell the OS manager a credential is gone so it removes that passkey from the
|
|
124
|
+
* user's list — the welded-old-wallet auto-cleanup. `credentialId` is base64url.
|
|
125
|
+
* Returns true if fired, false if unsupported/failed.
|
|
126
|
+
*/
|
|
127
|
+
declare function prunePasskey(args: {
|
|
128
|
+
credentialId: string;
|
|
129
|
+
rpId?: string;
|
|
130
|
+
}): Promise<boolean>;
|
|
131
|
+
/**
|
|
132
|
+
* Declare the FULL set of still-valid credential IDs for a user; the manager
|
|
133
|
+
* prunes anything not listed. Use after sign-in or eject to reconcile in one
|
|
134
|
+
* shot (pass `[]` to clear all of a user's passkeys). Returns true if fired.
|
|
135
|
+
*/
|
|
136
|
+
declare function syncAcceptedPasskeys(args: {
|
|
137
|
+
userId: string;
|
|
138
|
+
acceptedCredentialIds: string[];
|
|
139
|
+
rpId?: string;
|
|
140
|
+
}): Promise<boolean>;
|
|
141
|
+
|
|
142
|
+
export { ACTIVE_WALLET_STORAGE_KEY as A, type ConnectVault as C, type DexterConnectConfig as D, type PasskeyLoginTokens as P, type SignInResult as S, ConnectError as a, type PasskeySignalSupport as b, type StoredWallet as c, getCredentialId as d, ejectActiveWallet as e, forgetWallet as f, getActiveHandle as g, prunePasskey as h, subscribe as i, switchWallet as j, syncAcceptedPasskeys as k, listWallets as l, passkeySignalSupport as p, renamePasskey as r, setActiveHandle as s };
|