decane-connect-kit 2.0.3 → 2.1.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.d.cts CHANGED
@@ -81,8 +81,24 @@ interface SocialConfig {
81
81
  apiBaseUrl?: string;
82
82
  chains?: string[];
83
83
  rpcUrls?: Record<string, string>;
84
+ /**
85
+ * Sign in with a token from your own auth system (Auth0, Clerk, Firebase,
86
+ * Cognito, or your own backend) — surfaced as a button in the built-in
87
+ * WalletSelector / SocialWalletModal UI, alongside Google and email.
88
+ * getToken() runs when the user clicks the button; wire it to however you
89
+ * already obtain a token from your auth system (e.g. after your own login
90
+ * flow completes). Configure the issuer and JWKS URL for this token
91
+ * against your project in the dashboard first. `providerId` is only
92
+ * needed when the project has more than one custom provider configured.
93
+ */
94
+ customAuth?: {
95
+ label: string;
96
+ providerId?: string;
97
+ getToken: () => Promise<string>;
98
+ };
84
99
  }
85
100
  interface DecaneConfig {
101
+ appId: string;
86
102
  chains?: ChainDescriptor[];
87
103
  supportedChainIds?: string[];
88
104
  networks?: "evm" | "solana" | "tron" | "bitcoin" | "all";
@@ -132,12 +148,13 @@ interface BitcoinProvider {
132
148
  on?(event: string, handler: (...args: unknown[]) => void): void;
133
149
  }
134
150
 
135
- declare function createDecane(config?: DecaneConfig): DecaneProvider;
151
+ declare function createDecane(config: DecaneConfig): DecaneProvider;
136
152
 
137
153
  declare class ApiClient {
138
154
  private readonly baseUrl;
139
155
  private readonly apiKey;
140
- constructor(baseUrl: string, apiKey: string);
156
+ private readonly appId;
157
+ constructor(baseUrl: string, apiKey: string, appId: string);
141
158
  private _request;
142
159
  getGoogleInitUrl(): Promise<{
143
160
  url: string;
@@ -157,6 +174,19 @@ declare class ApiClient {
157
174
  }): Promise<void>;
158
175
  getWebAuthnRegisterOptions(jwt: string, rpId: string, rpName: string): Promise<_simplewebauthn_browser.PublicKeyCredentialCreationOptionsJSON>;
159
176
  postWebAuthnRegister(jwt: string, response: _simplewebauthn_browser.RegistrationResponseJSON, origin: string): Promise<void>;
177
+ /**
178
+ * Exchange a token from the developer's own auth system for a Decane
179
+ * session. `providerId` is only needed when the project has more than one
180
+ * provider configured.
181
+ */
182
+ authExternal(token: string, providerId?: string): Promise<AuthResult>;
183
+ /**
184
+ * Returns null when this user has no server-registered passkey, which is the
185
+ * expected answer for wallets created before passkey sign-in existed or for
186
+ * PIN-only users — the caller falls back to the normal sign-in flow.
187
+ */
188
+ getWebAuthnLoginOptions(userId: string, rpId: string): Promise<_simplewebauthn_browser.PublicKeyCredentialRequestOptionsJSON | null>;
189
+ postWebAuthnLogin(userId: string, response: _simplewebauthn_browser.AuthenticationResponseJSON, origin: string): Promise<AuthResult>;
160
190
  revoke(jwt: string): Promise<void>;
161
191
  hasRecoveryBackup(jwt: string): Promise<boolean>;
162
192
  getRecoveryBackup(jwt: string): Promise<{
@@ -244,6 +274,9 @@ interface PortableBackup {
244
274
 
245
275
  type Chain = `evm:${number}` | `solana:${'mainnet' | 'devnet'}`;
246
276
  interface DecaneConnectConfig {
277
+ /** Dashboard project ID (public). Sent with every request so the backend can
278
+ * confirm the API key below actually belongs to this project. */
279
+ appId: string;
247
280
  apiKey: string;
248
281
  chains: Chain[];
249
282
  authMethods: Array<'google' | 'email'>;
@@ -313,6 +346,20 @@ interface DecaneConnect {
313
346
  connectWithEmail(email: string): Promise<{
314
347
  codeRequired: true;
315
348
  }>;
349
+ /**
350
+ * Sign in with a JWT from your own auth system (Auth0, Clerk, Firebase,
351
+ * Cognito, your own backend — anything that publishes a JWKS endpoint).
352
+ *
353
+ * Configure the issuer and JWKS URL against your project in the dashboard;
354
+ * no client-side setup and no secret in your dApp. `providerId` is only
355
+ * required if the project has more than one provider configured.
356
+ *
357
+ * The user's wallet is derived from the configured identity claim, so the
358
+ * same token subject always resolves to the same wallet.
359
+ */
360
+ connectWithToken(token: string, opts?: {
361
+ providerId?: string;
362
+ }): Promise<ConnectResult>;
316
363
  verifyEmailCode(email: string, code: string): Promise<ConnectResult>;
317
364
  unlock(): Promise<void>;
318
365
  isUnlocked(): boolean;
@@ -320,8 +367,25 @@ interface DecaneConnect {
320
367
  * True when the user's identity is known from a previous visit but there is
321
368
  * no live session, so unlock() cannot succeed and they must sign in again.
322
369
  * Check this before offering an "Unlock" action.
370
+ *
371
+ * Note this can be true while canSignInWithPasskey() is also true — in which
372
+ * case "sign in again" costs one biometric prompt, not a full auth round
373
+ * trip. Prefer offering that over a Google/email button.
323
374
  */
324
375
  needsReconnect(): boolean;
376
+ /**
377
+ * True when a returning user can be signed straight back in with the passkey
378
+ * already on this device — no Google redirect, no email code. Requires a
379
+ * remembered identity plus a passkey-wrapped device share in local storage,
380
+ * so it is false on new devices and for PIN-only wallets.
381
+ */
382
+ canSignInWithPasskey(): Promise<boolean>;
383
+ /**
384
+ * Sign in using only the device passkey. connect() already tries this first
385
+ * when it's available, so call this directly only when driving the UI
386
+ * yourself. Throws NewDeviceError when this device isn't eligible.
387
+ */
388
+ signInWithPasskey(): Promise<ConnectResult>;
325
389
  sessionExpiresAt(): number | null;
326
390
  signMessage(req: SignMessageRequest): Promise<string>;
327
391
  sendTransaction(req: SendTransactionRequest): Promise<string>;
@@ -568,8 +632,16 @@ interface WalletSelectorProps {
568
632
  codeRequired: true;
569
633
  }>;
570
634
  onEmailVerify?: (email: string, code: string) => Promise<void>;
571
- }
572
- declare function WalletSelector({ provider, open, onClose, onConnect, theme, networks, mode, socialAuthMethods, onGoogleSignIn, onEmailStart, onEmailVerify, }: WalletSelectorProps): React.ReactPortal | null;
635
+ /** Button text for the developer's own auth system, e.g. "Continue with Acme Inc". Omitted entirely when not configured. */
636
+ customAuthLabel?: string;
637
+ onCustomAuthSignIn?: () => Promise<void>;
638
+ /** Returning user on this device — offer the passkey instead of sign-in. */
639
+ canUsePasskey?: boolean;
640
+ /** Address shown alongside the passkey button, so they know who they're resuming as. */
641
+ passkeyAddress?: string | null;
642
+ onPasskeySignIn?: () => Promise<unknown>;
643
+ }
644
+ declare function WalletSelector({ provider, open, onClose, onConnect, theme, networks, mode, socialAuthMethods, onGoogleSignIn, onEmailStart, onEmailVerify, customAuthLabel, onCustomAuthSignIn, canUsePasskey, passkeyAddress, onPasskeySignIn, }: WalletSelectorProps): React.ReactPortal | null;
573
645
 
574
646
  type Theme = "light" | "dark" | "auto";
575
647
  interface SocialAuthState {
@@ -586,10 +658,16 @@ interface SocialAuthState {
586
658
  * requires a fresh, valid JWT to open a TEE session, and JWTs are
587
659
  * deliberately never persisted (short-lived, security-sensitive), so
588
660
  * there is no legitimate "just unlock" path in this state. A consumer
589
- * should route this into a full reconnect (connectWithGoogle() /
590
- * connectWithEmail()), not unlock() — which will always throw here.
661
+ * should route this into a full reconnect — but see canUsePasskey: when
662
+ * that is true the reconnect is a single biometric prompt rather than a
663
+ * Google redirect or an email code.
591
664
  */
592
665
  needsReconnect: boolean;
666
+ /**
667
+ * True when this device holds a passkey-wrapped share for the remembered
668
+ * user, so signing back in costs one passkey prompt and nothing else.
669
+ */
670
+ canUsePasskey: boolean;
593
671
  }
594
672
  interface DecaneKitProps {
595
673
  config?: DecaneConfig;
@@ -610,6 +688,8 @@ interface SocialWalletModalProps {
610
688
  }>;
611
689
  onEmailVerify: (email: string, code: string) => Promise<void>;
612
690
  authMethods?: Array<"google" | "email">;
691
+ customAuthLabel?: string;
692
+ onCustomAuthSignIn?: () => Promise<void>;
613
693
  onRotateShares: (opts: {
614
694
  password: string;
615
695
  passwordHint?: string;
@@ -621,7 +701,7 @@ interface SocialWalletModalProps {
621
701
  rpcUrls?: Record<string, string>;
622
702
  chains?: string[];
623
703
  }
624
- declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
704
+ declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
625
705
 
626
706
  declare function useSocialAuth(): {
627
707
  isConnected: boolean;
@@ -635,6 +715,9 @@ declare function useSocialAuth(): {
635
715
  needsReconnect: boolean;
636
716
  googleLoading: boolean;
637
717
  emailLoading: boolean;
718
+ tokenLoading: boolean;
719
+ customLoading: boolean;
720
+ customAuthLabel: string | undefined;
638
721
  error: string | null;
639
722
  clearError: () => void;
640
723
  signInWithGoogle: () => Promise<void>;
@@ -642,6 +725,10 @@ declare function useSocialAuth(): {
642
725
  codeRequired: true;
643
726
  }>;
644
727
  confirmEmailCode: (email: string, code: string) => Promise<void>;
728
+ signInWithToken: (token: string, opts?: {
729
+ providerId?: string;
730
+ }) => Promise<void>;
731
+ signInWithCustomAuth: (() => Promise<void>) | undefined;
645
732
  unlock: () => Promise<void>;
646
733
  disconnect: () => Promise<void>;
647
734
  };
package/dist/index.d.ts CHANGED
@@ -81,8 +81,24 @@ interface SocialConfig {
81
81
  apiBaseUrl?: string;
82
82
  chains?: string[];
83
83
  rpcUrls?: Record<string, string>;
84
+ /**
85
+ * Sign in with a token from your own auth system (Auth0, Clerk, Firebase,
86
+ * Cognito, or your own backend) — surfaced as a button in the built-in
87
+ * WalletSelector / SocialWalletModal UI, alongside Google and email.
88
+ * getToken() runs when the user clicks the button; wire it to however you
89
+ * already obtain a token from your auth system (e.g. after your own login
90
+ * flow completes). Configure the issuer and JWKS URL for this token
91
+ * against your project in the dashboard first. `providerId` is only
92
+ * needed when the project has more than one custom provider configured.
93
+ */
94
+ customAuth?: {
95
+ label: string;
96
+ providerId?: string;
97
+ getToken: () => Promise<string>;
98
+ };
84
99
  }
85
100
  interface DecaneConfig {
101
+ appId: string;
86
102
  chains?: ChainDescriptor[];
87
103
  supportedChainIds?: string[];
88
104
  networks?: "evm" | "solana" | "tron" | "bitcoin" | "all";
@@ -132,12 +148,13 @@ interface BitcoinProvider {
132
148
  on?(event: string, handler: (...args: unknown[]) => void): void;
133
149
  }
134
150
 
135
- declare function createDecane(config?: DecaneConfig): DecaneProvider;
151
+ declare function createDecane(config: DecaneConfig): DecaneProvider;
136
152
 
137
153
  declare class ApiClient {
138
154
  private readonly baseUrl;
139
155
  private readonly apiKey;
140
- constructor(baseUrl: string, apiKey: string);
156
+ private readonly appId;
157
+ constructor(baseUrl: string, apiKey: string, appId: string);
141
158
  private _request;
142
159
  getGoogleInitUrl(): Promise<{
143
160
  url: string;
@@ -157,6 +174,19 @@ declare class ApiClient {
157
174
  }): Promise<void>;
158
175
  getWebAuthnRegisterOptions(jwt: string, rpId: string, rpName: string): Promise<_simplewebauthn_browser.PublicKeyCredentialCreationOptionsJSON>;
159
176
  postWebAuthnRegister(jwt: string, response: _simplewebauthn_browser.RegistrationResponseJSON, origin: string): Promise<void>;
177
+ /**
178
+ * Exchange a token from the developer's own auth system for a Decane
179
+ * session. `providerId` is only needed when the project has more than one
180
+ * provider configured.
181
+ */
182
+ authExternal(token: string, providerId?: string): Promise<AuthResult>;
183
+ /**
184
+ * Returns null when this user has no server-registered passkey, which is the
185
+ * expected answer for wallets created before passkey sign-in existed or for
186
+ * PIN-only users — the caller falls back to the normal sign-in flow.
187
+ */
188
+ getWebAuthnLoginOptions(userId: string, rpId: string): Promise<_simplewebauthn_browser.PublicKeyCredentialRequestOptionsJSON | null>;
189
+ postWebAuthnLogin(userId: string, response: _simplewebauthn_browser.AuthenticationResponseJSON, origin: string): Promise<AuthResult>;
160
190
  revoke(jwt: string): Promise<void>;
161
191
  hasRecoveryBackup(jwt: string): Promise<boolean>;
162
192
  getRecoveryBackup(jwt: string): Promise<{
@@ -244,6 +274,9 @@ interface PortableBackup {
244
274
 
245
275
  type Chain = `evm:${number}` | `solana:${'mainnet' | 'devnet'}`;
246
276
  interface DecaneConnectConfig {
277
+ /** Dashboard project ID (public). Sent with every request so the backend can
278
+ * confirm the API key below actually belongs to this project. */
279
+ appId: string;
247
280
  apiKey: string;
248
281
  chains: Chain[];
249
282
  authMethods: Array<'google' | 'email'>;
@@ -313,6 +346,20 @@ interface DecaneConnect {
313
346
  connectWithEmail(email: string): Promise<{
314
347
  codeRequired: true;
315
348
  }>;
349
+ /**
350
+ * Sign in with a JWT from your own auth system (Auth0, Clerk, Firebase,
351
+ * Cognito, your own backend — anything that publishes a JWKS endpoint).
352
+ *
353
+ * Configure the issuer and JWKS URL against your project in the dashboard;
354
+ * no client-side setup and no secret in your dApp. `providerId` is only
355
+ * required if the project has more than one provider configured.
356
+ *
357
+ * The user's wallet is derived from the configured identity claim, so the
358
+ * same token subject always resolves to the same wallet.
359
+ */
360
+ connectWithToken(token: string, opts?: {
361
+ providerId?: string;
362
+ }): Promise<ConnectResult>;
316
363
  verifyEmailCode(email: string, code: string): Promise<ConnectResult>;
317
364
  unlock(): Promise<void>;
318
365
  isUnlocked(): boolean;
@@ -320,8 +367,25 @@ interface DecaneConnect {
320
367
  * True when the user's identity is known from a previous visit but there is
321
368
  * no live session, so unlock() cannot succeed and they must sign in again.
322
369
  * Check this before offering an "Unlock" action.
370
+ *
371
+ * Note this can be true while canSignInWithPasskey() is also true — in which
372
+ * case "sign in again" costs one biometric prompt, not a full auth round
373
+ * trip. Prefer offering that over a Google/email button.
323
374
  */
324
375
  needsReconnect(): boolean;
376
+ /**
377
+ * True when a returning user can be signed straight back in with the passkey
378
+ * already on this device — no Google redirect, no email code. Requires a
379
+ * remembered identity plus a passkey-wrapped device share in local storage,
380
+ * so it is false on new devices and for PIN-only wallets.
381
+ */
382
+ canSignInWithPasskey(): Promise<boolean>;
383
+ /**
384
+ * Sign in using only the device passkey. connect() already tries this first
385
+ * when it's available, so call this directly only when driving the UI
386
+ * yourself. Throws NewDeviceError when this device isn't eligible.
387
+ */
388
+ signInWithPasskey(): Promise<ConnectResult>;
325
389
  sessionExpiresAt(): number | null;
326
390
  signMessage(req: SignMessageRequest): Promise<string>;
327
391
  sendTransaction(req: SendTransactionRequest): Promise<string>;
@@ -568,8 +632,16 @@ interface WalletSelectorProps {
568
632
  codeRequired: true;
569
633
  }>;
570
634
  onEmailVerify?: (email: string, code: string) => Promise<void>;
571
- }
572
- declare function WalletSelector({ provider, open, onClose, onConnect, theme, networks, mode, socialAuthMethods, onGoogleSignIn, onEmailStart, onEmailVerify, }: WalletSelectorProps): React.ReactPortal | null;
635
+ /** Button text for the developer's own auth system, e.g. "Continue with Acme Inc". Omitted entirely when not configured. */
636
+ customAuthLabel?: string;
637
+ onCustomAuthSignIn?: () => Promise<void>;
638
+ /** Returning user on this device — offer the passkey instead of sign-in. */
639
+ canUsePasskey?: boolean;
640
+ /** Address shown alongside the passkey button, so they know who they're resuming as. */
641
+ passkeyAddress?: string | null;
642
+ onPasskeySignIn?: () => Promise<unknown>;
643
+ }
644
+ declare function WalletSelector({ provider, open, onClose, onConnect, theme, networks, mode, socialAuthMethods, onGoogleSignIn, onEmailStart, onEmailVerify, customAuthLabel, onCustomAuthSignIn, canUsePasskey, passkeyAddress, onPasskeySignIn, }: WalletSelectorProps): React.ReactPortal | null;
573
645
 
574
646
  type Theme = "light" | "dark" | "auto";
575
647
  interface SocialAuthState {
@@ -586,10 +658,16 @@ interface SocialAuthState {
586
658
  * requires a fresh, valid JWT to open a TEE session, and JWTs are
587
659
  * deliberately never persisted (short-lived, security-sensitive), so
588
660
  * there is no legitimate "just unlock" path in this state. A consumer
589
- * should route this into a full reconnect (connectWithGoogle() /
590
- * connectWithEmail()), not unlock() — which will always throw here.
661
+ * should route this into a full reconnect — but see canUsePasskey: when
662
+ * that is true the reconnect is a single biometric prompt rather than a
663
+ * Google redirect or an email code.
591
664
  */
592
665
  needsReconnect: boolean;
666
+ /**
667
+ * True when this device holds a passkey-wrapped share for the remembered
668
+ * user, so signing back in costs one passkey prompt and nothing else.
669
+ */
670
+ canUsePasskey: boolean;
593
671
  }
594
672
  interface DecaneKitProps {
595
673
  config?: DecaneConfig;
@@ -610,6 +688,8 @@ interface SocialWalletModalProps {
610
688
  }>;
611
689
  onEmailVerify: (email: string, code: string) => Promise<void>;
612
690
  authMethods?: Array<"google" | "email">;
691
+ customAuthLabel?: string;
692
+ onCustomAuthSignIn?: () => Promise<void>;
613
693
  onRotateShares: (opts: {
614
694
  password: string;
615
695
  passwordHint?: string;
@@ -621,7 +701,7 @@ interface SocialWalletModalProps {
621
701
  rpcUrls?: Record<string, string>;
622
702
  chains?: string[];
623
703
  }
624
- declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
704
+ declare function SocialWalletModal({ open, onClose, theme, socialAuth, onUnlock, onDisconnect, onGoogleSignIn, onEmailStart, onEmailVerify, authMethods, customAuthLabel, onCustomAuthSignIn, onRotateShares, onExportPortableBackup, rpcUrls, chains, }: SocialWalletModalProps): React.ReactPortal | null;
625
705
 
626
706
  declare function useSocialAuth(): {
627
707
  isConnected: boolean;
@@ -635,6 +715,9 @@ declare function useSocialAuth(): {
635
715
  needsReconnect: boolean;
636
716
  googleLoading: boolean;
637
717
  emailLoading: boolean;
718
+ tokenLoading: boolean;
719
+ customLoading: boolean;
720
+ customAuthLabel: string | undefined;
638
721
  error: string | null;
639
722
  clearError: () => void;
640
723
  signInWithGoogle: () => Promise<void>;
@@ -642,6 +725,10 @@ declare function useSocialAuth(): {
642
725
  codeRequired: true;
643
726
  }>;
644
727
  confirmEmailCode: (email: string, code: string) => Promise<void>;
728
+ signInWithToken: (token: string, opts?: {
729
+ providerId?: string;
730
+ }) => Promise<void>;
731
+ signInWithCustomAuth: (() => Promise<void>) | undefined;
645
732
  unlock: () => Promise<void>;
646
733
  disconnect: () => Promise<void>;
647
734
  };