@abstraxn/signer-react 3.1.2 → 3.1.4

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/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.1.4] - 2026-04-06
9
+
10
+ ### Changed
11
+
12
+ - **Passkey completion via login code (`@abstraxn/signer-core`)** – Passkey signup and login now expect a `loginCode` (and optional `mfaRequired`) from the backend, then exchange it through `POST /auth/exchange` before tokens and user state are finalized. Passkey responses no longer persist access/refresh tokens directly from those calls.
13
+ - **Auth route prefixes** – OTP init/verify and passkey signup/login client paths use `/auth/otp/...` and `/auth/passkey/...` instead of `/login/otp/...` and `/login/passkey/...`.
14
+ - **Passkey login stamp** – The WebAuthn/Turnkey stamp is serialized and sent as a JSON string in the passkey login request, matching the updated API contract.
15
+ - **Passkey signup payload** – `targetPublicKey` is optional when a device public key is not available; session completion merges profile fields from the passkey API response where the JWT omits them (e.g. `isPolicy`, `publicKey`, `authProvider`).
16
+ - **MFA after passkey** – `AbstraxnWallet.loginWithPasskey` / `signupWithPasskey` and the React provider defer `connect()` when MFA is still required; `AuthManager` tracks `pendingSessionAuthMethod: 'passkey'` so post-MFA session setup uses the correct path.
17
+
18
+ ### Added
19
+
20
+ - **`PasskeyFlowResult` and onboarding hooks** – `onPasskeySignup` / `onPasskeyLogin` may return `{ mfaRequired: true }`; `onPasskeyPendingMfa` lets UIs jump to the MFA step (`OnboardingUIReact`, `OnboardingUIWeb`, and `useAuthMethods`). `PasskeyFlowResult` is re-exported from `@abstraxn/signer-core`.
21
+ - **Server logout on full disconnect** – When the Abstraxn session disconnect runs, the SDK best-effort calls `POST /auth/logout` with the current access token (external-wallet-only disconnect continues to skip this).
22
+
8
23
  ## [3.1.2] - 2026-03-27
9
24
 
10
25
  ### Changed
@@ -766,10 +766,13 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
766
766
  }
767
767
  const authManager = walletInstance.getAuthManager();
768
768
  const user = await authManager.loginWithPasskey();
769
- // Set user immediately so onLoginSuccess can access it
770
769
  setUser(user);
771
- // Connect wallet after successful authentication
770
+ const lastAuthState = authManager.getLastAuthState();
771
+ if (lastAuthState?.mfaRequired === true) {
772
+ return { mfaRequired: true };
773
+ }
772
774
  await walletInstance.connect();
775
+ return undefined;
773
776
  },
774
777
  onPasskeySignup: async () => {
775
778
  if (!walletInstance)
@@ -784,11 +787,13 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
784
787
  }
785
788
  const authManager = walletInstance.getAuthManager();
786
789
  const user = await authManager.signupWithPasskey();
787
- // Clear any previous errors on successful verification
788
- // setError(null);
789
790
  setUser(user);
790
- // Connect wallet after successful authentication
791
+ const lastAuthState = authManager.getLastAuthState();
792
+ if (lastAuthState?.mfaRequired === true) {
793
+ return { mfaRequired: true };
794
+ }
791
795
  await walletInstance.connect();
796
+ return undefined;
792
797
  },
793
798
  onLoginSuccess: async (_data) => {
794
799
  // Clear any previous errors on successful login
@@ -1605,7 +1610,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
1605
1610
  setLoading(false);
1606
1611
  }
1607
1612
  }, [showOnboarding]);
1608
- // Disconnect wallet
1613
+ // Full disconnect: when Abstraxn is connected, `wallet.disconnect()` runs and signer-core notifies POST /auth/logout; external-wallet-only flows use `disconnectExternalWallet` instead (no core disconnect).
1609
1614
  const disconnect = useCallback(async () => {
1610
1615
  setLoading(true);
1611
1616
  setDisconnecting(true);
@@ -2972,7 +2977,7 @@ export function AbstraxnProviderInner({ config, children, base, wagmi, solana, }
2972
2977
  wagmiDisconnect,
2973
2978
  wagmiAccount?.isConnected,
2974
2979
  ]);
2975
- // Disconnect external wallet
2980
+ // External wallet only — does not call AbstraxnWallet.disconnect (no server /auth/logout).
2976
2981
  const disconnectExternalWallet = useCallback(async () => {
2977
2982
  if (!externalWalletsEnabled || !wagmiDisconnect) {
2978
2983
  return;
@@ -351,10 +351,13 @@ export function useWalletInitialization({ config, walletRef, onboardingRef, otpI
351
351
  }
352
352
  const authManager = walletInstance.getAuthManager();
353
353
  const user = await authManager.loginWithPasskey();
354
- // Set user immediately so onLoginSuccess can access it
355
354
  setUser(user);
356
- // Connect wallet after successful authentication
355
+ const lastAuthState = authManager.getLastAuthState();
356
+ if (lastAuthState?.mfaRequired === true) {
357
+ return { mfaRequired: true };
358
+ }
357
359
  await walletInstance.connect();
360
+ return undefined;
358
361
  },
359
362
  onPasskeySignup: async () => {
360
363
  if (!walletInstance)
@@ -369,11 +372,13 @@ export function useWalletInitialization({ config, walletRef, onboardingRef, otpI
369
372
  }
370
373
  const authManager = walletInstance.getAuthManager();
371
374
  const user = await authManager.signupWithPasskey();
372
- // Clear any previous errors on successful verification
373
- // setError(null);
374
375
  setUser(user);
375
- // Connect wallet after successful authentication
376
+ const lastAuthState = authManager.getLastAuthState();
377
+ if (lastAuthState?.mfaRequired === true) {
378
+ return { mfaRequired: true };
379
+ }
376
380
  await walletInstance.connect();
381
+ return undefined;
377
382
  },
378
383
  onLoginSuccess: async (_data) => {
379
384
  // Clear any previous errors on successful login
@@ -27,7 +27,10 @@ export const OnboardingUIReact = ({ config = {}, modal = false, onClose, externa
27
27
  const authMethods = config.authMethods || ["otp", "google"];
28
28
  const showModalCloseButton = config.showCloseButton === true;
29
29
  const onboarding = useOnboarding(config);
30
- const authMethodsHook = useAuthMethods(config);
30
+ const authMethodsHook = useAuthMethods({
31
+ ...config,
32
+ onPasskeyPendingMfa: config.onPasskeyPendingMfa ?? (() => onboarding.setStep("mfa")),
33
+ });
31
34
  const { user, isConnected, whoami, loading: providerLoading, error: providerError, emailForOtp, } = useAbstraxnWallet();
32
35
  const [showExternalWallets, setShowExternalWallets] = useState(false);
33
36
  const [oauthLoading, setOauthLoading] = useState(false);
@@ -4286,8 +4286,11 @@ export class OnboardingUIWeb {
4286
4286
  this.setLoading(true, "passkey");
4287
4287
  this.hidePasskeyError();
4288
4288
  if (this.config.onPasskeyLogin) {
4289
- await this.config.onPasskeyLogin();
4290
- // Call onLoginSuccess to close modal and update state
4289
+ const result = await this.config.onPasskeyLogin();
4290
+ if (result && typeof result === "object" && result.mfaRequired === true) {
4291
+ this.showMfaVerificationScreenForOAuth();
4292
+ return;
4293
+ }
4291
4294
  if (this.config.onLoginSuccess) {
4292
4295
  this.config.onLoginSuccess({ user: null });
4293
4296
  }
@@ -4318,8 +4321,11 @@ export class OnboardingUIWeb {
4318
4321
  this.setLoading(true, "passkey");
4319
4322
  this.hidePasskeyError();
4320
4323
  if (this.config.onPasskeySignup) {
4321
- await this.config.onPasskeySignup();
4322
- // Call onLoginSuccess to close modal and update state
4324
+ const result = await this.config.onPasskeySignup();
4325
+ if (result && typeof result === "object" && result.mfaRequired === true) {
4326
+ this.showMfaVerificationScreenForOAuth();
4327
+ return;
4328
+ }
4323
4329
  if (this.config.onLoginSuccess) {
4324
4330
  this.config.onLoginSuccess({ user: null });
4325
4331
  }
@@ -192,6 +192,11 @@ export const useAuthMethods = (config) => {
192
192
  else {
193
193
  await wallet.loginWithPasskey();
194
194
  }
195
+ const mfaPending = wallet.getAuthManager().getLastAuthState()?.mfaRequired === true;
196
+ if (mfaPending) {
197
+ config?.onPasskeyPendingMfa?.();
198
+ return;
199
+ }
195
200
  config?.onLoginSuccess?.({});
196
201
  }
197
202
  catch (err) {
@@ -225,6 +230,11 @@ export const useAuthMethods = (config) => {
225
230
  else {
226
231
  await wallet.signupWithPasskey();
227
232
  }
233
+ const mfaPending = wallet.getAuthManager().getLastAuthState()?.mfaRequired === true;
234
+ if (mfaPending) {
235
+ config?.onPasskeyPendingMfa?.();
236
+ return;
237
+ }
228
238
  config?.onLoginSuccess?.({});
229
239
  }
230
240
  catch (err) {
@@ -270,6 +270,7 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
270
270
  request?: (parameters: import("viem").CcipRequestParameters) => Promise<import("viem/_types/utils/ccip").CcipRequestReturnType>;
271
271
  } | undefined;
272
272
  chain: Chain | undefined;
273
+ dataSuffix?: import("viem").DataSuffix | undefined;
273
274
  experimental_blockTag?: import("viem").BlockTag | undefined;
274
275
  key: string;
275
276
  name: string;
@@ -452,6 +453,7 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
452
453
  getChainId: () => Promise<import("viem").GetChainIdReturnType>;
453
454
  getCode: (args: import("viem").GetBytecodeParameters) => Promise<import("viem").GetBytecodeReturnType>;
454
455
  getContractEvents: <const abi extends Abi | readonly unknown[], eventName extends import("viem").ContractEventName<abi> | undefined = undefined, strict extends boolean | undefined = undefined, fromBlock extends import("viem").BlockNumber | import("viem").BlockTag | undefined = undefined, toBlock extends import("viem").BlockNumber | import("viem").BlockTag | undefined = undefined>(args: import("viem").GetContractEventsParameters<abi, eventName, strict, fromBlock, toBlock>) => Promise<import("viem").GetContractEventsReturnType<abi, eventName, strict, fromBlock, toBlock>>;
456
+ getDelegation: (args: import("viem").GetDelegationParameters) => Promise<import("viem").GetDelegationReturnType>;
455
457
  getEip712Domain: (args: import("viem").GetEip712DomainParameters) => Promise<import("viem").GetEip712DomainReturnType>;
456
458
  getEnsAddress: (args: import("viem").GetEnsAddressParameters) => Promise<import("viem").GetEnsAddressReturnType>;
457
459
  getEnsAvatar: (args: import("viem").GetEnsAvatarParameters) => Promise<import("viem").GetEnsAvatarReturnType>;
@@ -3877,6 +3879,7 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
3877
3879
  cacheTime?: undefined;
3878
3880
  ccipRead?: undefined;
3879
3881
  chain?: undefined;
3882
+ dataSuffix?: undefined;
3880
3883
  experimental_blockTag?: undefined;
3881
3884
  key?: undefined;
3882
3885
  name?: undefined;
@@ -3926,6 +3929,7 @@ export declare function useWalletClient(chain: any, rpcUrl?: string): {
3926
3929
  request?: (parameters: import("viem").CcipRequestParameters) => Promise<import("viem/_types/utils/ccip").CcipRequestReturnType>;
3927
3930
  } | undefined;
3928
3931
  chain: Chain | undefined;
3932
+ dataSuffix?: import("viem").DataSuffix | undefined;
3929
3933
  experimental_blockTag?: import("viem").BlockTag | undefined;
3930
3934
  key: string;
3931
3935
  name: string;
@@ -8337,6 +8341,7 @@ export declare function useWalletClient(chain: any, rpcUrl?: string): {
8337
8341
  cacheTime?: undefined;
8338
8342
  ccipRead?: undefined;
8339
8343
  chain?: undefined;
8344
+ dataSuffix?: undefined;
8340
8345
  experimental_blockTag?: undefined;
8341
8346
  key?: undefined;
8342
8347
  name?: undefined;
@@ -8863,6 +8868,7 @@ export declare function useExternalWalletClient(): {
8863
8868
  request?: (parameters: import("viem").CcipRequestParameters) => Promise<import("viem/_types/utils/ccip").CcipRequestReturnType>;
8864
8869
  } | undefined;
8865
8870
  chain: Chain;
8871
+ dataSuffix?: import("viem").DataSuffix | undefined;
8866
8872
  experimental_blockTag?: import("viem").BlockTag | undefined;
8867
8873
  key: string;
8868
8874
  name: string;
@@ -13274,6 +13280,7 @@ export declare function useExternalWalletClient(): {
13274
13280
  cacheTime?: undefined;
13275
13281
  ccipRead?: undefined;
13276
13282
  chain?: undefined;
13283
+ dataSuffix?: undefined;
13277
13284
  experimental_blockTag?: undefined;
13278
13285
  key?: undefined;
13279
13286
  name?: undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abstraxn/signer-react",
3
- "version": "3.1.2",
3
+ "version": "3.1.4",
4
4
  "description": "React SDK for Abstraxn Wallet - React components, hooks, and providers for seamless Web3 wallet integration",
5
5
  "main": "./dist/src/index.js",
6
6
  "module": "./dist/src/index.js",
@@ -40,7 +40,7 @@
40
40
  "access": "public"
41
41
  },
42
42
  "dependencies": {
43
- "@abstraxn/signer-core": "^2.1.1",
43
+ "@abstraxn/signer-core": "2.1.2",
44
44
  "@solana/wallet-adapter-base": "^0.9.27",
45
45
  "@solana/wallet-adapter-react": "^0.15.39",
46
46
  "@solana/wallet-adapter-react-ui": "^0.9.39",