@openfort/react 1.0.4 → 1.0.5

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.
@@ -73,9 +73,12 @@ const ConnectWithOAuth = () => {
73
73
  case states.REDIRECT: {
74
74
  if (hasProvider)
75
75
  return;
76
- const cleanURL = win.location.origin + win.location.pathname;
76
+ const baseURL = win.location.origin + win.location.pathname;
77
+ const hash = win.location.hash;
77
78
  const queryParams = Object.fromEntries([...url.searchParams.entries()].filter(([key]) => ['openfortAuthProviderUI', 'refresh_token', 'access_token', 'player_id'].includes(key)));
78
79
  queryParams.openfortAuthProviderUI = provider;
80
+ // Query params must come before the hash fragment in a valid URL
81
+ const redirectTo = `${baseURL}?${new URLSearchParams(queryParams).toString()}${hash}`;
79
82
  try {
80
83
  if (user) {
81
84
  const authToken = await client.getAccessToken();
@@ -86,7 +89,7 @@ const ConnectWithOAuth = () => {
86
89
  }
87
90
  const linkResponse = await client.auth.initLinkOAuth({
88
91
  provider,
89
- redirectTo: `${cleanURL}?${new URLSearchParams(queryParams).toString()}`,
92
+ redirectTo,
90
93
  });
91
94
  logger.log(linkResponse);
92
95
  win.location.href = linkResponse;
@@ -94,7 +97,7 @@ const ConnectWithOAuth = () => {
94
97
  else {
95
98
  const r = await client.auth.initOAuth({
96
99
  provider,
97
- redirectTo: `${cleanURL}?${new URLSearchParams(queryParams).toString()}`,
100
+ redirectTo,
98
101
  });
99
102
  logger.log(r);
100
103
  win.location.href = r;
@@ -1 +1 @@
1
- {"version":3,"file":"ConnectWithOAuth.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"ConnectWithOAuth.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -63,13 +63,14 @@ const OtherMethod = ({ currentMethod, onChangeMethod, }) => {
63
63
  };
64
64
  const CreateWalletAutomaticRecovery = ({ onBack, logoutOnBack, }) => {
65
65
  var _a;
66
- const { embeddedState } = useOpenfortCore();
66
+ const { embeddedState, isLoadingAccounts } = useOpenfortCore();
67
67
  const { setRoute, triggerResize, walletConfig } = useOpenfort();
68
68
  const [recoveryError, setRecoveryError] = useState(null);
69
69
  const { create } = useEthereumEmbeddedWallet();
70
70
  const { isEnabled: isWalletRecoveryOTPEnabled, requestOTP } = useRecoveryOTP();
71
71
  const [shouldCreateWallet, setShouldCreateWallet] = useState(false);
72
72
  const isCreatingRef = useRef(false);
73
+ const hasAttemptedCreationRef = useRef(false);
73
74
  const [needsOTP, setNeedsOTP] = useState(false);
74
75
  const [otpResponse, setOtpResponse] = useState(null);
75
76
  const [otpStatus, setOtpStatus] = useState('idle');
@@ -100,6 +101,10 @@ const CreateWalletAutomaticRecovery = ({ onBack, logoutOnBack, }) => {
100
101
  return;
101
102
  if (isCreatingRef.current)
102
103
  return;
104
+ // Wait for the state machine's fetchEmbeddedAccounts to finish before
105
+ // calling create() — concurrent SDK operations corrupt shared state.
106
+ if (isLoadingAccounts)
107
+ return;
103
108
  isCreatingRef.current = true;
104
109
  (async () => {
105
110
  logger.log('Creating wallet Automatic recover');
@@ -132,12 +137,18 @@ const CreateWalletAutomaticRecovery = ({ onBack, logoutOnBack, }) => {
132
137
  }
133
138
  triggerResize();
134
139
  })();
135
- }, [shouldCreateWallet, create, isWalletRecoveryOTPEnabled, requestOTP, triggerResize]);
140
+ }, [shouldCreateWallet, create, isWalletRecoveryOTPEnabled, requestOTP, triggerResize, isLoadingAccounts]);
136
141
  useEffect(() => {
137
142
  if (embeddedState !== EmbeddedState.EMBEDDED_SIGNER_NOT_CONFIGURED)
138
143
  return;
139
144
  if ((walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.connectOnLogin) === false)
140
145
  return;
146
+ // Guard against retry loop: when create() fails the SDK cycles the
147
+ // embeddedState back to EMBEDDED_SIGNER_NOT_CONFIGURED which re-triggers
148
+ // this effect. Only attempt creation once — the user can retry manually.
149
+ if (hasAttemptedCreationRef.current)
150
+ return;
151
+ hasAttemptedCreationRef.current = true;
141
152
  setShouldCreateWallet(true);
142
153
  }, [embeddedState, walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.connectOnLogin]);
143
154
  const handleResendClick = useCallback(() => {
@@ -160,13 +171,28 @@ const CreateWalletAutomaticRecovery = ({ onBack, logoutOnBack, }) => {
160
171
  logo: (otpResponse === null || otpResponse === void 0 ? void 0 : otpResponse.sentTo) === 'phone' ? jsx(PhoneIcon, {}) : jsx(EmailIcon, {}),
161
172
  } }), jsxs(ModalBody, { children: [jsxs(Body, { children: ["Please check ", jsx("b", { children: (otpResponse === null || otpResponse === void 0 ? void 0 : otpResponse.sentTo) === 'phone' ? otpResponse === null || otpResponse === void 0 ? void 0 : otpResponse.phone : otpResponse === null || otpResponse === void 0 ? void 0 : otpResponse.email }), " and enter your code below."] }), jsx(OtpInputStandalone, { length: 9, scale: "80%", onComplete: handleCompleteOtp, isLoading: otpStatus === 'loading', isError: otpStatus === 'error', isSuccess: otpStatus === 'success' }), jsxs(ResultContainer, { children: [otpStatus === 'success' && jsx(ModalBody, { "$valid": true, children: "Code verified successfully!" }), otpStatus === 'error' && jsx(ModalBody, { "$error": true, children: error || 'Invalid code. Please try again.' })] }), jsxs(FooterTextButton, { children: ["Didn't receive the code?", ' ', jsx(FooterButtonText, { type: "button", onClick: handleResendClick, disabled: isResendDisabled, children: sendButtonText })] })] })] }));
162
173
  }
163
- return (jsx(PageContent, { onBack: onBack, logoutOnBack: logoutOnBack, children: jsx(Loader, { isError: !!recoveryError, header: recoveryError ? 'Error creating wallet.' : `Creating wallet...`, description: recoveryError ? recoveryError.message : undefined }) }));
174
+ // When connectOnLogin is false, auto-creation is skipped show a manual
175
+ // trigger instead of an infinite spinner.
176
+ if (!shouldCreateWallet && !isCreatingRef.current && !recoveryError) {
177
+ return (jsxs(PageContent, { onBack: onBack, logoutOnBack: logoutOnBack, children: [jsx(ModalHeading, { children: "Create wallet" }), jsx(ModalBody, { style: { textAlign: 'center' }, children: "Create an embedded wallet to get started." }), jsx(Button, { onClick: () => {
178
+ hasAttemptedCreationRef.current = false;
179
+ setShouldCreateWallet(true);
180
+ }, children: "Create wallet" })] }));
181
+ }
182
+ return (jsx(PageContent, { onBack: onBack, logoutOnBack: logoutOnBack, children: jsx(Loader, { isError: !!recoveryError, header: recoveryError ? 'Error creating wallet.' : `Creating wallet...`, description: recoveryError ? recoveryError.message : undefined, onRetry: recoveryError
183
+ ? () => {
184
+ hasAttemptedCreationRef.current = false;
185
+ setRecoveryError(null);
186
+ setShouldCreateWallet(true);
187
+ }
188
+ : undefined }) }));
164
189
  };
165
190
  const CreateWalletPasskeyRecovery = ({ onChangeMethod, onBack, logoutOnBack, }) => {
166
191
  const { triggerResize, setRoute, walletConfig } = useOpenfort();
167
192
  const { create } = useEthereumEmbeddedWallet();
168
193
  const [shouldCreateWallet, setShouldCreateWallet] = useState(false);
169
194
  const isCreatingRef = useRef(false);
195
+ const hasAttemptedCreationRef = useRef(false);
170
196
  const [recoveryError, setRecoveryError] = useState(null);
171
197
  const { embeddedState } = useOpenfortCore();
172
198
  useEffect(() => {
@@ -197,6 +223,9 @@ const CreateWalletPasskeyRecovery = ({ onChangeMethod, onBack, logoutOnBack, })
197
223
  return;
198
224
  if ((walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.connectOnLogin) === false)
199
225
  return;
226
+ if (hasAttemptedCreationRef.current)
227
+ return;
228
+ hasAttemptedCreationRef.current = true;
200
229
  setShouldCreateWallet(true);
201
230
  }, [embeddedState, walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.connectOnLogin]);
202
231
  useEffect(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -64,10 +64,11 @@ const LoadWallets = () => {
64
64
  }
65
65
  logger.log('User wallets loaded:', wallets.length);
66
66
  if (wallets.length === 0) {
67
- if ((walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.connectOnLogin) === false) {
68
- setRoute(routes.CONNECTED);
69
- return;
70
- }
67
+ // Always show the create wallet page when no wallets exist.
68
+ // connectOnLogin only controls whether creation starts automatically
69
+ // (handled inside CreateWalletAutomaticRecovery), not whether the
70
+ // page is shown — routing to CONNECTED with 0 wallets would show a
71
+ // broken "Connect wallet" button aimed at external wagmi wallets.
71
72
  setRoute(createRoute(chainType));
72
73
  return;
73
74
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { ChainTypeEnum } from '@openfort/openfort-js';
2
+ import { ChainTypeEnum, EmbeddedState } from '@openfort/openfort-js';
3
3
  import React, { useEffect } from 'react';
4
4
  import { useEthereumEmbeddedWallet } from '../../../ethereum/hooks/useEthereumEmbeddedWallet.js';
5
5
  import { useOpenfortCore } from '../../../openfort/useOpenfort.js';
@@ -11,7 +11,7 @@ import { PageContent } from '../../PageContent/index.js';
11
11
 
12
12
  const Loading = () => {
13
13
  const { setRoute, walletConfig } = useOpenfort();
14
- const { user, isLoadingAccounts, needsRecovery } = useOpenfortCore();
14
+ const { user, isLoadingAccounts, isLoading, needsRecovery, embeddedState } = useOpenfortCore();
15
15
  const { chainType } = useOpenfortCore();
16
16
  // Use chain-specific hooks
17
17
  const ethereumWallet = useEthereumEmbeddedWallet();
@@ -24,7 +24,13 @@ const Loading = () => {
24
24
  useEffect(() => {
25
25
  if (isFirstFrame)
26
26
  return;
27
- if (isLoadingAccounts)
27
+ // Wait for the SDK to settle. After storeCredentials the embedded state
28
+ // briefly stays UNAUTHENTICATED/NONE while the SDK processes the token.
29
+ // Routing to PROVIDERS here would abort the auth flow.
30
+ if (embeddedState === EmbeddedState.NONE || embeddedState === EmbeddedState.UNAUTHENTICATED)
31
+ return;
32
+ // Also wait while accounts or user are still loading.
33
+ if (isLoadingAccounts || isLoading)
28
34
  return;
29
35
  else if (!user)
30
36
  setRoute(routes.PROVIDERS);
@@ -42,7 +48,7 @@ const Loading = () => {
42
48
  }
43
49
  else
44
50
  setRoute(routes.CONNECTED);
45
- }, [isLoadingAccounts, user, address, needsRecovery, isFirstFrame, retryCount]);
51
+ }, [embeddedState, isLoadingAccounts, isLoading, user, address, needsRecovery, isFirstFrame, retryCount]);
46
52
  // Retry every 250ms
47
53
  useEffect(() => {
48
54
  const interval = setInterval(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -35,8 +35,11 @@ function useEmbeddedStateMachine({ openfort, storeEmbeddedState, storeUser, stor
35
35
  setIsConnectedWithEmbeddedSigner(false);
36
36
  // Validate token and fetch accounts. Auto-recovery is handled by the
37
37
  // dedicated useAutoRecovery hook (keyed on storeActiveEmbeddedAddress).
38
+ // Never auto-logout here: during first OAuth sign-in the user isn't in
39
+ // the store yet and user.get() may briefly 401/404 before the token
40
+ // propagates — logging out would abort the entire auth flow.
38
41
  const doFetch = async () => {
39
- updateUserRef.current(undefined, !userRef.current);
42
+ updateUserRef.current(undefined, false);
40
43
  await fetchEmbeddedAccountsRef.current();
41
44
  };
42
45
  doFetch().catch((err) => {
@@ -1 +1 @@
1
- {"version":3,"file":"useEmbeddedStateMachine.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useEmbeddedStateMachine.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- export declare const OPENFORT_VERSION = "1.0.4";
1
+ export declare const OPENFORT_VERSION = "1.0.5";
package/build/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const OPENFORT_VERSION = '1.0.4';
1
+ const OPENFORT_VERSION = '1.0.5';
2
2
 
3
3
  export { OPENFORT_VERSION };
4
4
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfort/react",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "author": "Openfort (https://www.openfort.io)",
5
5
  "license": "BSD-2-Clause license",
6
6
  "description": "The easiest way to integrate Openfort to your project.",