@openfort/react-native 0.1.25 → 1.0.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/README.md CHANGED
@@ -0,0 +1,20 @@
1
+ <div align="center">
2
+ <h4>
3
+ <a href="https://www.openfort.io/">
4
+ Website
5
+ </a>
6
+ <span> | </span>
7
+ <a href="https://www.openfort.io/docs/products/embedded-wallet/react-native/">
8
+ Documentation
9
+ </a>
10
+ <span> | </span>
11
+ <a href="https://x.com/openfort_hq">
12
+ X
13
+ </a>
14
+ </h4>
15
+ </div>
16
+
17
+ # Openfort React Native SDK
18
+
19
+ Quickstart sample:
20
+ https://github.com/openfort-xyz/react-native-auth-sample
@@ -5,10 +5,12 @@
5
5
  */
6
6
  // Email authentication
7
7
  export { useEmailAuth } from './useEmailAuth';
8
+ export { useEmailAuthOtp } from './useEmailAuthOtp';
8
9
  // Guest accounts
9
10
  export { useGuestAuth } from './useGuestAuth';
10
11
  // OAuth authentication
11
12
  export { useOAuth } from './useOAuth';
13
+ export { usePhoneAuthOtp } from './usePhoneAuthOtp';
12
14
  export { useSignOut } from './useSignOut';
13
15
  // Wallet-based authentication (SIWE)
14
16
  export { useWalletAuth } from './useWalletAuth';
@@ -76,7 +76,7 @@ export const useEmailAuth = (hookOptions = {}) => {
76
76
  else {
77
77
  // Login successful
78
78
  setPasswordState({ status: 'done' });
79
- const user = result.player;
79
+ const user = result.user;
80
80
  // Refresh user state in provider
81
81
  await _internal.refreshUserState(user);
82
82
  return onSuccess({
@@ -106,6 +106,7 @@ export const useEmailAuth = (hookOptions = {}) => {
106
106
  const result = await client.auth.signUpWithEmailPassword({
107
107
  email: options.email,
108
108
  password: options.password,
109
+ callbackURL: options.emailVerificationRedirectTo || createOAuthRedirectUri(),
109
110
  ...(options.name && { name: options.name }),
110
111
  });
111
112
  // Check if action is required (email verification)
@@ -123,7 +124,7 @@ export const useEmailAuth = (hookOptions = {}) => {
123
124
  else {
124
125
  // Signup successful
125
126
  setPasswordState({ status: 'done' });
126
- const user = result.player;
127
+ const user = result.user;
127
128
  // Refresh user state in provider
128
129
  await _internal.refreshUserState(user);
129
130
  return onSuccess({
@@ -146,59 +147,61 @@ export const useEmailAuth = (hookOptions = {}) => {
146
147
  });
147
148
  }
148
149
  }, [client, setPasswordState, _internal, hookOptions]);
149
- const linkEmail = useCallback(async (options) => {
150
- try {
151
- setPasswordState({ status: 'sending-verification-code' });
152
- // Get current user access token
153
- const accessToken = await client.getAccessToken();
154
- if (!accessToken) {
155
- throw new Error('User must be authenticated to link email');
156
- }
157
- // Link email account
158
- const result = await client.auth.linkEmailPassword({
159
- email: options.email,
160
- password: options.password,
161
- authToken: accessToken,
162
- });
163
- // Check if action is required (email verification)
164
- if ('action' in result) {
165
- setPasswordState({
166
- status: 'awaiting-code-input',
167
- });
168
- // Return undefined as email verification is required
169
- return onSuccess({
170
- hookOptions,
171
- options,
172
- data: { requiresEmailVerification: true },
173
- });
174
- }
175
- else {
176
- // Link successful
177
- setPasswordState({ status: 'done' });
178
- // Refresh user state to reflect email linking
179
- await _internal.refreshUserState();
180
- return onSuccess({
181
- hookOptions,
182
- options,
183
- data: { user: result },
184
- });
185
- }
186
- }
187
- catch (e) {
188
- const error = new OpenfortError('Failed to link email and password', OpenfortErrorType.AUTHENTICATION_ERROR, {
189
- error: e,
190
- });
191
- setPasswordState({
192
- status: 'error',
193
- error,
194
- });
195
- return onError({
196
- hookOptions,
197
- options,
198
- error,
199
- });
200
- }
201
- }, [client, setPasswordState, _internal, hookOptions]);
150
+ // TODO: Auth V2
151
+ // const linkEmail = useCallback(
152
+ // async (options: LinkEmailOptions): Promise<EmailAuthResult> => {
153
+ // try {
154
+ // setPasswordState({ status: 'sending-verification-code' })
155
+ // // Get current user access token
156
+ // const accessToken = await client.getAccessToken()
157
+ // if (!accessToken) {
158
+ // throw new Error('User must be authenticated to link email')
159
+ // }
160
+ // // Link email account
161
+ // const result = await client.auth.linkEmailPassword({
162
+ // email: options.email,
163
+ // password: options.password,
164
+ // authToken: accessToken,
165
+ // })
166
+ // // Check if action is required (email verification)
167
+ // if ('action' in result) {
168
+ // setPasswordState({
169
+ // status: 'awaiting-code-input',
170
+ // })
171
+ // // Return undefined as email verification is required
172
+ // return onSuccess({
173
+ // hookOptions,
174
+ // options,
175
+ // data: { requiresEmailVerification: true },
176
+ // })
177
+ // } else {
178
+ // // Link successful
179
+ // setPasswordState({ status: 'done' })
180
+ // // Refresh user state to reflect email linking
181
+ // await _internal.refreshUserState()
182
+ // return onSuccess({
183
+ // hookOptions,
184
+ // options,
185
+ // data: { user: result },
186
+ // })
187
+ // }
188
+ // } catch (e) {
189
+ // const error = new OpenfortError('Failed to link email and password', OpenfortErrorType.AUTHENTICATION_ERROR, {
190
+ // error: e,
191
+ // })
192
+ // setPasswordState({
193
+ // status: 'error',
194
+ // error,
195
+ // })
196
+ // return onError({
197
+ // hookOptions,
198
+ // options,
199
+ // error,
200
+ // })
201
+ // }
202
+ // },
203
+ // [client, setPasswordState, _internal, hookOptions]
204
+ // )
202
205
  const requestResetPassword = useCallback(async (options) => {
203
206
  try {
204
207
  setPasswordState({ status: 'sending-verification-code' });
@@ -234,9 +237,8 @@ export const useEmailAuth = (hookOptions = {}) => {
234
237
  setPasswordState({ status: 'submitting-code' });
235
238
  // Reset password with new password and state token
236
239
  await client.auth.resetPassword({
237
- email: options.email,
238
240
  password: options.password,
239
- state: options.state,
241
+ token: options.token,
240
242
  });
241
243
  setPasswordState({ status: 'done' });
242
244
  return onSuccess({
@@ -265,14 +267,13 @@ export const useEmailAuth = (hookOptions = {}) => {
265
267
  setPasswordState({ status: 'submitting-code' });
266
268
  // Verify email with state token
267
269
  await client.auth.verifyEmail({
268
- email: options.email,
269
- state: options.state,
270
+ token: options.token,
270
271
  });
271
272
  setPasswordState({ status: 'done' });
272
273
  return onSuccess({
273
274
  hookOptions,
274
275
  options,
275
- data: { email: options.email },
276
+ data: {},
276
277
  });
277
278
  }
278
279
  catch (e) {
@@ -295,7 +296,7 @@ export const useEmailAuth = (hookOptions = {}) => {
295
296
  signInEmail,
296
297
  signUpEmail,
297
298
  verifyEmail,
298
- linkEmail,
299
+ linkEmail: () => { },
299
300
  requestResetPassword,
300
301
  resetPassword,
301
302
  reset,
@@ -0,0 +1,111 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { useOpenfortContext } from '../../core/context';
3
+ import { onError, onSuccess } from '../../lib/hookConsistency';
4
+ import { mapStatus } from '../../types/baseFlowState';
5
+ import { OpenfortError, OpenfortErrorType } from '../../types/openfortError';
6
+ export const useEmailAuthOtp = (hookOptions = {}) => {
7
+ const { client, _internal } = useOpenfortContext();
8
+ const [status, setStatus] = useState({
9
+ status: 'idle',
10
+ });
11
+ const signInEmailOtp = useCallback(async (options) => {
12
+ try {
13
+ setStatus({
14
+ status: 'loading',
15
+ });
16
+ if (!options.email || !options.otp) {
17
+ const error = new OpenfortError('Email and OTP are required', OpenfortErrorType.AUTHENTICATION_ERROR);
18
+ setStatus({
19
+ status: 'error',
20
+ error,
21
+ });
22
+ return onError({
23
+ hookOptions,
24
+ options,
25
+ error,
26
+ });
27
+ }
28
+ const result = await client.auth.logInWithEmailOtp({
29
+ email: options.email,
30
+ otp: options.otp,
31
+ });
32
+ setStatus({
33
+ status: 'success',
34
+ });
35
+ const user = result.user;
36
+ await _internal.refreshUserState(user);
37
+ return onSuccess({
38
+ data: { user },
39
+ hookOptions,
40
+ options,
41
+ });
42
+ }
43
+ catch (e) {
44
+ const error = new OpenfortError('Failed to login with email OTP', OpenfortErrorType.AUTHENTICATION_ERROR, {
45
+ error: e,
46
+ });
47
+ setStatus({
48
+ status: 'error',
49
+ error: error,
50
+ });
51
+ return onError({
52
+ hookOptions,
53
+ options,
54
+ error: error,
55
+ });
56
+ }
57
+ }, [client, setStatus, _internal, hookOptions]);
58
+ const requestEmailOtp = useCallback(async (options) => {
59
+ try {
60
+ setStatus({
61
+ status: 'loading',
62
+ });
63
+ if (!options.email) {
64
+ const error = new OpenfortError('Email is required', OpenfortErrorType.AUTHENTICATION_ERROR);
65
+ setStatus({
66
+ status: 'error',
67
+ error,
68
+ });
69
+ return onError({
70
+ hookOptions,
71
+ options,
72
+ error,
73
+ });
74
+ }
75
+ await client.auth.requestEmailOtp({
76
+ email: options.email,
77
+ });
78
+ setStatus({
79
+ status: 'success',
80
+ });
81
+ return onSuccess({
82
+ data: {},
83
+ hookOptions,
84
+ options,
85
+ });
86
+ }
87
+ catch (e) {
88
+ const error = new OpenfortError('Failed to request email OTP', OpenfortErrorType.AUTHENTICATION_ERROR, {
89
+ error: e,
90
+ });
91
+ setStatus({
92
+ status: 'error',
93
+ error: error,
94
+ });
95
+ return onError({
96
+ hookOptions,
97
+ options,
98
+ error: error,
99
+ });
100
+ }
101
+ }, [client, setStatus, hookOptions]);
102
+ const reset = () => {
103
+ setStatus({ status: 'idle' });
104
+ };
105
+ return {
106
+ requestEmailOtp,
107
+ signInEmailOtp,
108
+ reset,
109
+ ...mapStatus(status),
110
+ };
111
+ };
@@ -47,7 +47,7 @@ export const useGuestAuth = (hookOptions = {}) => {
47
47
  status: 'loading',
48
48
  });
49
49
  const result = await client.auth.signUpGuest();
50
- const user = result.player;
50
+ const user = result.user;
51
51
  await updateUser(user);
52
52
  // const { wallet } = await tryUseWallet({
53
53
  // logoutOnError: options.logoutOnError || hookOptions.logoutOnError,
@@ -50,29 +50,29 @@ export const useOAuth = (hookOptions = {}) => {
50
50
  const redirectUri = options.redirectTo || createOAuthRedirectUri('/oauth/callback');
51
51
  const result = await client.auth.initOAuth({
52
52
  provider: options.provider,
53
+ redirectTo: redirectUri,
53
54
  options: {
54
55
  skipBrowserRedirect: true,
55
- redirectTo: redirectUri,
56
56
  },
57
57
  });
58
58
  // Handle OAuth flow using native utilities
59
59
  setOAuthState({ status: 'awaiting-redirect' });
60
60
  // Check if we should use native Apple authentication
61
- if (options.provider === 'apple' && !options.isLegacyAppleIosBehaviorEnabled) {
61
+ if (options.provider === 'apple') {
62
62
  const isAppleAvailable = await isAppleSignInAvailable();
63
63
  if (isAppleAvailable) {
64
64
  try {
65
65
  const appleResult = await authenticateWithApple({
66
- state: result.key || '',
66
+ state: result || '',
67
67
  isLogin: true,
68
68
  });
69
69
  // Complete OAuth flow with Apple credentials
70
- const authResult = await client.auth.loginWithIdToken({
70
+ const authResult = await client.auth.logInWithIdToken({
71
71
  provider: OAuthProvider.APPLE,
72
72
  token: appleResult.identityToken,
73
73
  });
74
74
  setOAuthState({ status: 'done' });
75
- const user = authResult.player;
75
+ const user = authResult.user;
76
76
  // Refresh user state in provider
77
77
  await _internal.refreshUserState(user);
78
78
  return onSuccess({
@@ -98,7 +98,7 @@ export const useOAuth = (hookOptions = {}) => {
98
98
  }
99
99
  }
100
100
  // For other providers, use web-based OAuth
101
- const providerUrl = OAuthUtils.getProviderUrl(options.provider, result.url);
101
+ const providerUrl = OAuthUtils.getProviderUrl(options.provider, result);
102
102
  const oauthResult = await OAuthUtils.withTimeout(openOAuthSession({
103
103
  url: providerUrl,
104
104
  redirectUri,
@@ -106,14 +106,13 @@ export const useOAuth = (hookOptions = {}) => {
106
106
  );
107
107
  if (oauthResult.type === 'success' && oauthResult.url) {
108
108
  // Parse OAuth response from redirect URL
109
- const { access_token, refresh_token, player_id, error, errorDescription } = parseOAuthUrl(oauthResult.url);
110
- if (error) {
109
+ const { access_token, user_id, error, errorDescription } = parseOAuthUrl(oauthResult.url);
110
+ if (error || !user_id) {
111
111
  throw new Error(errorDescription || error);
112
112
  }
113
113
  await client.auth.storeCredentials({
114
- player: player_id,
115
- accessToken: access_token,
116
- refreshToken: refresh_token,
114
+ userId: user_id,
115
+ token: access_token,
117
116
  });
118
117
  setOAuthState({ status: 'done' });
119
118
  const user = await client.user.get();
@@ -177,30 +176,30 @@ export const useOAuth = (hookOptions = {}) => {
177
176
  const redirectUri = options.redirectTo || createOAuthRedirectUri('/oauth/callback');
178
177
  const result = await client.auth.initLinkOAuth({
179
178
  provider: options.provider,
180
- authToken: accessToken,
179
+ // authToken: accessToken,
180
+ redirectTo: redirectUri,
181
181
  options: {
182
182
  skipBrowserRedirect: true,
183
- redirectTo: redirectUri,
184
183
  },
185
184
  });
186
185
  // Handle OAuth linking flow using native utilities
187
186
  setOAuthState({ status: 'awaiting-redirect' });
188
187
  // Check if we should use native Apple authentication for linking
189
- if (options.provider === 'apple' && !options.isLegacyAppleIosBehaviorEnabled) {
188
+ if (options.provider === 'apple') {
190
189
  const isAppleAvailable = await isAppleSignInAvailable();
191
190
  if (isAppleAvailable) {
192
191
  try {
193
192
  const appleResult = await authenticateWithApple({
194
- state: result.key || '',
193
+ state: result || '',
195
194
  isLogin: false, // This is a linking operation
196
195
  });
197
196
  // Complete OAuth linking flow with Apple credentials
198
- const linkResult = await client.auth.loginWithIdToken({
197
+ const linkResult = await client.auth.logInWithIdToken({
199
198
  provider: OAuthProvider.APPLE,
200
199
  token: appleResult.identityToken,
201
200
  });
202
201
  setOAuthState({ status: 'done' });
203
- const user = linkResult.player;
202
+ const user = linkResult.user;
204
203
  // Refresh user state to reflect OAuth linking
205
204
  await _internal.refreshUserState();
206
205
  return onSuccess({
@@ -226,7 +225,7 @@ export const useOAuth = (hookOptions = {}) => {
226
225
  }
227
226
  }
228
227
  // For other providers, use web-based OAuth
229
- const providerUrl = OAuthUtils.getProviderUrl(options.provider, result.url);
228
+ const providerUrl = OAuthUtils.getProviderUrl(options.provider, result);
230
229
  const oauthResult = await OAuthUtils.withTimeout(openOAuthSession({
231
230
  url: providerUrl,
232
231
  redirectUri,
@@ -234,14 +233,13 @@ export const useOAuth = (hookOptions = {}) => {
234
233
  );
235
234
  if (oauthResult.type === 'success' && oauthResult.url) {
236
235
  // Parse OAuth response from redirect URL
237
- const { access_token, refresh_token, player_id, error, errorDescription } = parseOAuthUrl(oauthResult.url);
238
- if (error) {
236
+ const { access_token, user_id, error, errorDescription } = parseOAuthUrl(oauthResult.url);
237
+ if (error || !user_id) {
239
238
  throw new Error(errorDescription || error);
240
239
  }
241
240
  await client.auth.storeCredentials({
242
- player: player_id,
243
- accessToken: access_token,
244
- refreshToken: refresh_token,
241
+ userId: user_id,
242
+ token: access_token,
245
243
  });
246
244
  setOAuthState({ status: 'done' });
247
245
  const user = await client.user.get();
@@ -291,11 +289,9 @@ export const useOAuth = (hookOptions = {}) => {
291
289
  });
292
290
  }
293
291
  }, [client, setOAuthState, _internal]);
294
- const storeCredentials = () => { }; // TODO
295
292
  return {
296
293
  initOAuth,
297
294
  linkOauth,
298
- storeCredentials,
299
295
  ...mapOAuthStatus(oAuthState),
300
296
  };
301
297
  };
@@ -0,0 +1,111 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { useOpenfortContext } from '../../core/context';
3
+ import { onError, onSuccess } from '../../lib/hookConsistency';
4
+ import { mapStatus } from '../../types/baseFlowState';
5
+ import { OpenfortError, OpenfortErrorType } from '../../types/openfortError';
6
+ export const usePhoneAuthOtp = (hookOptions = {}) => {
7
+ const { client, _internal } = useOpenfortContext();
8
+ const [status, setStatus] = useState({
9
+ status: 'idle',
10
+ });
11
+ const signInPhoneOtp = useCallback(async (options) => {
12
+ try {
13
+ setStatus({
14
+ status: 'loading',
15
+ });
16
+ if (!options.phone || !options.otp) {
17
+ const error = new OpenfortError('Phone and OTP are required', OpenfortErrorType.AUTHENTICATION_ERROR);
18
+ setStatus({
19
+ status: 'error',
20
+ error,
21
+ });
22
+ return onError({
23
+ hookOptions,
24
+ options,
25
+ error,
26
+ });
27
+ }
28
+ const result = await client.auth.logInWithPhoneOtp({
29
+ phoneNumber: options.phone,
30
+ otp: options.otp,
31
+ });
32
+ setStatus({
33
+ status: 'success',
34
+ });
35
+ const user = result.user;
36
+ await _internal.refreshUserState(user);
37
+ return onSuccess({
38
+ data: { user },
39
+ hookOptions,
40
+ options,
41
+ });
42
+ }
43
+ catch (e) {
44
+ const error = new OpenfortError('Failed to login with phone OTP', OpenfortErrorType.AUTHENTICATION_ERROR, {
45
+ error: e,
46
+ });
47
+ setStatus({
48
+ status: 'error',
49
+ error: error,
50
+ });
51
+ return onError({
52
+ hookOptions,
53
+ options,
54
+ error: error,
55
+ });
56
+ }
57
+ }, [client, setStatus, _internal, hookOptions]);
58
+ const requestPhoneOtp = useCallback(async (options) => {
59
+ try {
60
+ setStatus({
61
+ status: 'loading',
62
+ });
63
+ if (!options.phone) {
64
+ const error = new OpenfortError('Phone is required', OpenfortErrorType.AUTHENTICATION_ERROR);
65
+ setStatus({
66
+ status: 'error',
67
+ error,
68
+ });
69
+ return onError({
70
+ hookOptions,
71
+ options,
72
+ error,
73
+ });
74
+ }
75
+ await client.auth.requestPhoneOtp({
76
+ phoneNumber: options.phone,
77
+ });
78
+ setStatus({
79
+ status: 'success',
80
+ });
81
+ return onSuccess({
82
+ data: {},
83
+ hookOptions,
84
+ options,
85
+ });
86
+ }
87
+ catch (e) {
88
+ const error = new OpenfortError('Failed to request phone OTP', OpenfortErrorType.AUTHENTICATION_ERROR, {
89
+ error: e,
90
+ });
91
+ setStatus({
92
+ status: 'error',
93
+ error: error,
94
+ });
95
+ return onError({
96
+ hookOptions,
97
+ options,
98
+ error: error,
99
+ });
100
+ }
101
+ }, [client, setStatus, hookOptions]);
102
+ const reset = () => {
103
+ setStatus({ status: 'idle' });
104
+ };
105
+ return {
106
+ requestPhoneOtp,
107
+ signInPhoneOtp,
108
+ reset,
109
+ ...mapStatus(status),
110
+ };
111
+ };
@@ -55,7 +55,7 @@ export function useSignOut(hookOptions = {}) {
55
55
  setStatus({ status: 'loading' });
56
56
  try {
57
57
  await client.auth.logout();
58
- await internalRef.current.refreshUserState(null);
58
+ await internalRef.current.refreshUserState(undefined);
59
59
  setStatus({ status: 'success' });
60
60
  return onSuccess({
61
61
  hookOptions: hookOptionsRef.current,
@@ -112,9 +112,10 @@ export function useWalletAuth(hookOptions) {
112
112
  message: message,
113
113
  walletClientType: 'unknown',
114
114
  connectorType: 'unknown',
115
+ address: opts.walletAddress,
115
116
  });
116
117
  setSiweState({ status: 'done' });
117
- const user = result.player;
118
+ const user = result.user;
118
119
  // Refresh user state to reflect SIWE linking
119
120
  await _internal.refreshUserState();
120
121
  return onSuccess({
@@ -150,9 +151,10 @@ export function useWalletAuth(hookOptions) {
150
151
  message: message,
151
152
  walletClientType: 'unknown',
152
153
  connectorType: 'unknown',
154
+ address: opts.walletAddress,
153
155
  });
154
156
  setSiweState({ status: 'done' });
155
- const user = result.player;
157
+ const user = result.user;
156
158
  // Refresh user state in provider
157
159
  await _internal.refreshUserState(user);
158
160
  return onSuccess({
@@ -106,12 +106,16 @@ export function useEmbeddedEthereumWallet(options = {}) {
106
106
  status: 'idle',
107
107
  });
108
108
  // Fetch Ethereum embedded accounts
109
- const fetchEmbeddedAccounts = useCallback(async () => {
109
+ const fetchEmbeddedAccounts = useCallback(async (options) => {
110
110
  if (!client || embeddedState === EmbeddedState.NONE || embeddedState === EmbeddedState.UNAUTHENTICATED) {
111
111
  setEmbeddedAccounts([]);
112
112
  return;
113
113
  }
114
114
  try {
115
+ // Only set fetching status if not called silently (e.g., during create/setActive)
116
+ if (!options?.silent) {
117
+ setStatus({ status: 'fetching-wallets' });
118
+ }
115
119
  const accounts = await client.embeddedWallet.list({
116
120
  limit: 100,
117
121
  chainType: ChainTypeEnum.EVM,
@@ -119,9 +123,15 @@ export function useEmbeddedEthereumWallet(options = {}) {
119
123
  });
120
124
  // Filter for Ethereum accounts only
121
125
  setEmbeddedAccounts(accounts);
126
+ if (!options?.silent) {
127
+ setStatus({ status: 'idle' });
128
+ }
122
129
  }
123
130
  catch {
124
131
  setEmbeddedAccounts([]);
132
+ if (!options?.silent) {
133
+ setStatus({ status: 'idle' });
134
+ }
125
135
  }
126
136
  }, [client, embeddedState, walletConfig]);
127
137
  useEffect(() => {
@@ -263,8 +273,8 @@ export function useEmbeddedEthereumWallet(options = {}) {
263
273
  // Get provider
264
274
  const ethProvider = await getEthereumProvider();
265
275
  setProvider(ethProvider);
266
- // Refresh accounts and set as active
267
- await fetchEmbeddedAccounts();
276
+ // Refresh accounts silently (don't override 'creating' status) and set as active
277
+ await fetchEmbeddedAccounts({ silent: true });
268
278
  setActiveWalletId(embeddedAccount.id);
269
279
  setActiveAccount(embeddedAccount);
270
280
  setStatus({ status: 'success' });
@@ -476,6 +486,9 @@ export function useEmbeddedEthereumWallet(options = {}) {
476
486
  exportPrivateKey: client.embeddedWallet.exportPrivateKey,
477
487
  };
478
488
  // Priority 1: Explicit action states (user-initiated operations)
489
+ if (status.status === 'fetching-wallets') {
490
+ return { ...baseActions, status: 'fetching-wallets', activeWallet: null };
491
+ }
479
492
  if (status.status === 'creating') {
480
493
  return { ...baseActions, status: 'creating', activeWallet: null };
481
494
  }
@@ -117,21 +117,31 @@ export function useEmbeddedSolanaWallet(options = {}) {
117
117
  status: 'idle',
118
118
  });
119
119
  // Fetch Solana embedded accounts
120
- const fetchEmbeddedAccounts = useCallback(async () => {
120
+ const fetchEmbeddedAccounts = useCallback(async (options) => {
121
121
  if (!client || embeddedState === EmbeddedState.NONE || embeddedState === EmbeddedState.UNAUTHENTICATED) {
122
122
  setEmbeddedAccounts([]);
123
123
  return;
124
124
  }
125
125
  try {
126
+ // Only set fetching status if not called silently (e.g., during create/setActive)
127
+ if (!options?.silent) {
128
+ setStatus({ status: 'fetching-wallets' });
129
+ }
126
130
  const accounts = await client.embeddedWallet.list({
127
131
  chainType: ChainTypeEnum.SVM,
128
132
  accountType: AccountTypeEnum.EOA,
129
133
  limit: 100,
130
134
  });
131
135
  setEmbeddedAccounts(accounts);
136
+ if (!options?.silent) {
137
+ setStatus({ status: 'idle' });
138
+ }
132
139
  }
133
140
  catch {
134
141
  setEmbeddedAccounts([]);
142
+ if (!options?.silent) {
143
+ setStatus({ status: 'idle' });
144
+ }
135
145
  }
136
146
  }, [client, embeddedState]);
137
147
  useEffect(() => {
@@ -280,8 +290,8 @@ export function useEmbeddedSolanaWallet(options = {}) {
280
290
  // Get provider
281
291
  const solProvider = await getSolanaProvider(embeddedAccount);
282
292
  setProvider(solProvider);
283
- // Refresh accounts and set as active
284
- await fetchEmbeddedAccounts();
293
+ // Refresh accounts silently (don't override 'creating' status) and set as active
294
+ await fetchEmbeddedAccounts({ silent: true });
285
295
  setActiveWalletId(embeddedAccount.id);
286
296
  setActiveAccount(embeddedAccount);
287
297
  setStatus({ status: 'success' });
@@ -426,6 +436,9 @@ export function useEmbeddedSolanaWallet(options = {}) {
426
436
  setActive,
427
437
  };
428
438
  // Priority 1: Explicit action states (user-initiated operations)
439
+ if (status.status === 'fetching-wallets') {
440
+ return { ...baseActions, status: 'fetching-wallets', activeWallet: null };
441
+ }
429
442
  if (status.status === 'creating') {
430
443
  return { ...baseActions, status: 'creating', activeWallet: null };
431
444
  }
@@ -129,8 +129,7 @@ export function parseOAuthUrl(url) {
129
129
  logger.info('Parsed OAuth URL', queryParams);
130
130
  return {
131
131
  access_token: queryParams?.access_token,
132
- refresh_token: queryParams?.refresh_token,
133
- player_id: queryParams?.player_id,
132
+ user_id: queryParams?.user_id,
134
133
  error: queryParams?.error,
135
134
  errorDescription: queryParams?.error_description,
136
135
  };
@@ -9,7 +9,7 @@ export interface OpenfortContextValue {
9
9
  /** The Openfort client instance. */
10
10
  client: OpenfortClient;
11
11
  /** The current authenticated user, or null when unauthenticated. */
12
- user: import('@openfort/openfort-js').AuthPlayerResponse | null;
12
+ user: import('@openfort/openfort-js').User | null;
13
13
  /** Whether the SDK has initialized and is ready for use. */
14
14
  isReady: boolean;
15
15
  /** Any error encountered during SDK initialization. */
@@ -38,7 +38,7 @@ export interface OpenfortContextValue {
38
38
  getAccessToken: () => Promise<string | null>;
39
39
  /** @internal Refreshes user state after authentication changes. */
40
40
  _internal: {
41
- refreshUserState: (user?: import('@openfort/openfort-js').AuthPlayerResponse | null) => Promise<import('@openfort/openfort-js').AuthPlayerResponse | null>;
41
+ refreshUserState: (user?: import('@openfort/openfort-js').User) => Promise<import('@openfort/openfort-js').AuthResponse | null>;
42
42
  };
43
43
  }
44
44
  /**
@@ -5,10 +5,14 @@
5
5
  */
6
6
  export type { EmailAuthResult, EmailVerificationResult, LinkEmailOptions, RequestResetPasswordOptions, ResetPasswordOptions, SignInEmailOptions, SignUpEmailOptions, UseEmailHookOptions, VerifyEmailOptions, } from './useEmailAuth';
7
7
  export { useEmailAuth } from './useEmailAuth';
8
+ export type { EmailOtpAuthResult, LoginWithEmailOtpOptions, RequestEmailOtpOptions, UseEmailOtpHookOptions, } from './useEmailAuthOtp';
9
+ export { useEmailAuthOtp } from './useEmailAuthOtp';
8
10
  export type { GuestHookOptions, GuestHookResult } from './useGuestAuth';
9
11
  export { useGuestAuth } from './useGuestAuth';
10
12
  export type { AuthHookOptions, InitializeOAuthOptions, InitOAuthReturnType, StoreCredentialsResult, } from './useOAuth';
11
13
  export { useOAuth } from './useOAuth';
14
+ export type { LoginWithPhoneOtpOptions, PhoneOtpAuthResult, RequestPhoneOtpOptions, UsePhoneOtpHookOptions, } from './usePhoneAuthOtp';
15
+ export { usePhoneAuthOtp } from './usePhoneAuthOtp';
12
16
  export { useSignOut } from './useSignOut';
13
17
  export type { WalletHookOptions, WalletHookResult } from './useWalletAuth';
14
18
  export { useWalletAuth } from './useWalletAuth';
@@ -1,4 +1,4 @@
1
- import type { AuthPlayerResponse as OpenfortUser } from '@openfort/openfort-js';
1
+ import type { User as OpenfortUser } from '@openfort/openfort-js';
2
2
  import type { OpenfortHookOptions } from '../../types/hookOption';
3
3
  import { OpenfortError } from '../../types/openfortError';
4
4
  export type EmailAuthResult = {
@@ -22,9 +22,8 @@ export type RequestResetPasswordOptions = {
22
22
  emailVerificationRedirectTo?: string;
23
23
  } & OpenfortHookOptions<EmailAuthResult>;
24
24
  export type ResetPasswordOptions = {
25
- email: string;
26
25
  password: string;
27
- state: string;
26
+ token: string;
28
27
  } & OpenfortHookOptions<EmailAuthResult>;
29
28
  export type LinkEmailOptions = {
30
29
  email: string;
@@ -32,8 +31,7 @@ export type LinkEmailOptions = {
32
31
  emailVerificationRedirectTo?: string;
33
32
  } & OpenfortHookOptions<EmailAuthResult>;
34
33
  export type VerifyEmailOptions = {
35
- email: string;
36
- state: string;
34
+ token: string;
37
35
  } & OpenfortHookOptions<EmailVerificationResult>;
38
36
  export type EmailVerificationResult = {
39
37
  email?: string;
@@ -90,7 +88,7 @@ export declare const useEmailAuth: (hookOptions?: UseEmailHookOptions) => {
90
88
  signInEmail: (options: SignInEmailOptions) => Promise<EmailAuthResult>;
91
89
  signUpEmail: (options: SignUpEmailOptions) => Promise<EmailAuthResult>;
92
90
  verifyEmail: (options: VerifyEmailOptions) => Promise<EmailVerificationResult>;
93
- linkEmail: (options: LinkEmailOptions) => Promise<EmailAuthResult>;
91
+ linkEmail: () => void;
94
92
  requestResetPassword: (options: RequestResetPasswordOptions) => Promise<EmailAuthResult>;
95
93
  resetPassword: (options: ResetPasswordOptions) => Promise<EmailAuthResult>;
96
94
  reset: () => void;
@@ -0,0 +1,24 @@
1
+ import type { User } from '@openfort/openfort-js';
2
+ import type { OpenfortHookOptions } from '../../types/hookOption';
3
+ import { OpenfortError } from '../../types/openfortError';
4
+ export type EmailOtpAuthResult = {
5
+ error?: OpenfortError;
6
+ user?: User;
7
+ };
8
+ export type LoginWithEmailOtpOptions = {
9
+ email: string;
10
+ otp: string;
11
+ } & OpenfortHookOptions<EmailOtpAuthResult>;
12
+ export type RequestEmailOtpOptions = {
13
+ email: string;
14
+ } & OpenfortHookOptions<EmailOtpAuthResult>;
15
+ export type UseEmailOtpHookOptions = OpenfortHookOptions<EmailOtpAuthResult>;
16
+ export declare const useEmailAuthOtp: (hookOptions?: UseEmailOtpHookOptions) => {
17
+ isLoading: boolean;
18
+ isError: boolean;
19
+ isSuccess: boolean;
20
+ error: OpenfortError | null | undefined;
21
+ requestEmailOtp: (options: RequestEmailOtpOptions) => Promise<EmailOtpAuthResult>;
22
+ signInEmailOtp: (options: LoginWithEmailOtpOptions) => Promise<EmailOtpAuthResult>;
23
+ reset: () => void;
24
+ };
@@ -1,4 +1,4 @@
1
- import type { AuthPlayerResponse as OpenfortUser } from '@openfort/openfort-js';
1
+ import type { User as OpenfortUser } from '@openfort/openfort-js';
2
2
  import type { OpenfortHookOptions } from '../../types/hookOption';
3
3
  import { OpenfortError } from '../../types/openfortError';
4
4
  export type GuestHookResult = {
@@ -1,10 +1,9 @@
1
- import { OAuthProvider, type AuthPlayerResponse as OpenfortUser } from '@openfort/openfort-js';
1
+ import { OAuthProvider, type User as OpenfortUser } from '@openfort/openfort-js';
2
2
  import type { OpenfortHookOptions } from '../../types/hookOption';
3
3
  import { OpenfortError } from '../../types/openfortError';
4
4
  export type InitializeOAuthOptions = {
5
5
  provider: OAuthProvider;
6
6
  redirectTo?: string;
7
- isLegacyAppleIosBehaviorEnabled?: boolean;
8
7
  } & OpenfortHookOptions<InitOAuthReturnType>;
9
8
  export type StoreCredentialsResult = {
10
9
  user?: OpenfortUser;
@@ -60,5 +59,4 @@ export declare const useOAuth: (hookOptions?: AuthHookOptions) => {
60
59
  error: Error | null | undefined;
61
60
  initOAuth: (options: InitializeOAuthOptions) => Promise<InitOAuthReturnType>;
62
61
  linkOauth: (options: InitializeOAuthOptions) => Promise<InitOAuthReturnType>;
63
- storeCredentials: () => void;
64
62
  };
@@ -0,0 +1,24 @@
1
+ import type { User } from '@openfort/openfort-js';
2
+ import type { OpenfortHookOptions } from '../../types/hookOption';
3
+ import { OpenfortError } from '../../types/openfortError';
4
+ export type PhoneOtpAuthResult = {
5
+ error?: OpenfortError;
6
+ user?: User;
7
+ };
8
+ export type LoginWithPhoneOtpOptions = {
9
+ phone: string;
10
+ otp: string;
11
+ } & OpenfortHookOptions<PhoneOtpAuthResult>;
12
+ export type RequestPhoneOtpOptions = {
13
+ phone: string;
14
+ } & OpenfortHookOptions<PhoneOtpAuthResult>;
15
+ export type UsePhoneOtpHookOptions = OpenfortHookOptions<PhoneOtpAuthResult>;
16
+ export declare const usePhoneAuthOtp: (hookOptions?: UsePhoneOtpHookOptions) => {
17
+ isLoading: boolean;
18
+ isError: boolean;
19
+ isSuccess: boolean;
20
+ error: OpenfortError | null | undefined;
21
+ requestPhoneOtp: (options: RequestPhoneOtpOptions) => Promise<PhoneOtpAuthResult>;
22
+ signInPhoneOtp: (options: LoginWithPhoneOtpOptions) => Promise<PhoneOtpAuthResult>;
23
+ reset: () => void;
24
+ };
@@ -1,4 +1,4 @@
1
- import type { AuthPlayerResponse as OpenfortUser } from '@openfort/openfort-js';
1
+ import type { User as OpenfortUser } from '@openfort/openfort-js';
2
2
  import type { OpenfortHookOptions } from '../../types/hookOption';
3
3
  import { OpenfortError } from '../../types/openfortError';
4
4
  export type WalletHookResult = {
@@ -27,7 +27,7 @@
27
27
  * ```
28
28
  */
29
29
  export declare function useUser(): {
30
- user: import("@openfort/openfort-js").AuthPlayerResponse | null;
30
+ user: import("@openfort/openfort-js").User | null;
31
31
  isAuthenticated: boolean;
32
32
  getAccessToken: () => Promise<string | null>;
33
33
  };
@@ -7,7 +7,7 @@
7
7
  * required to integrate Openfort authentication and embedded wallets into React Native and
8
8
  * Expo applications.
9
9
  */
10
- export { AccountTypeEnum, AuthInitPayload, AuthPlayerResponse, AuthResponse, ChainTypeEnum, EmbeddedAccount, EmbeddedState, OAuthProvider, Openfort as OpenfortClient, OpenfortConfiguration, OpenfortError, OpenfortEventMap, OpenfortEvents, openfortEvents, Provider, RecoveryMethod, RecoveryParams, ShieldConfiguration, SignedMessagePayload, } from '@openfort/openfort-js';
10
+ export { AccountTypeEnum, AuthInitPayload, AuthResponse, ChainTypeEnum, EmbeddedAccount, EmbeddedState, OAuthProvider, Openfort as OpenfortClient, OpenfortConfiguration, OpenfortError, OpenfortEventMap, OpenfortEvents, openfortEvents, Provider, RecoveryMethod, RecoveryParams, ShieldConfiguration, SignedMessagePayload, } from '@openfort/openfort-js';
11
11
  export * from './components';
12
12
  export * from './constants';
13
13
  export * from './core';
@@ -79,7 +79,7 @@ export declare function isAppleSignInAvailable(): Promise<boolean>;
79
79
  export declare function parseOAuthUrl(url: string): {
80
80
  access_token?: string;
81
81
  refresh_token?: string;
82
- player_id?: string;
82
+ user_id?: string;
83
83
  error?: string;
84
84
  errorDescription?: string;
85
85
  };
@@ -1,4 +1,4 @@
1
- import type { OpenfortError, AuthPlayerResponse as OpenfortUser } from '@openfort/openfort-js';
1
+ import type { OpenfortError, AuthResponse as OpenfortUser } from '@openfort/openfort-js';
2
2
  /**
3
3
  * Password authentication flow state
4
4
  */
@@ -32,5 +32,5 @@ export type LinkWithOAuthInput = {
32
32
  */
33
33
  export interface UseLoginWithOAuth {
34
34
  state: OAuthFlowState;
35
- login: (input: LoginWithOAuthInput) => Promise<import('@openfort/openfort-js').AuthPlayerResponse | undefined>;
35
+ login: (input: LoginWithOAuthInput) => Promise<import('@openfort/openfort-js').AuthResponse | undefined>;
36
36
  }
@@ -266,6 +266,9 @@ export interface SolanaWalletActions {
266
266
  export type EmbeddedEthereumWalletState = (EthereumWalletActions & {
267
267
  status: 'disconnected';
268
268
  activeWallet: null;
269
+ }) | (EthereumWalletActions & {
270
+ status: 'fetching-wallets';
271
+ activeWallet: null;
269
272
  }) | (EthereumWalletActions & {
270
273
  status: 'connecting';
271
274
  activeWallet: ConnectedEmbeddedEthereumWallet;
@@ -293,6 +296,9 @@ export type EmbeddedEthereumWalletState = (EthereumWalletActions & {
293
296
  export type EmbeddedSolanaWalletState = (SolanaWalletActions & {
294
297
  status: 'disconnected';
295
298
  activeWallet: null;
299
+ }) | (SolanaWalletActions & {
300
+ status: 'fetching-wallets';
301
+ activeWallet: null;
296
302
  }) | (SolanaWalletActions & {
297
303
  status: 'connecting';
298
304
  }) | (SolanaWalletActions & {
package/package.json CHANGED
@@ -1,9 +1,17 @@
1
1
  {
2
2
  "name": "@openfort/react-native",
3
3
  "main": "dist/index.js",
4
- "version": "0.1.25",
4
+ "version": "1.0.0",
5
5
  "license": "MIT",
6
6
  "description": "React Native SDK for Openfort platform integration",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/openfort-xyz/react-native.git"
10
+ },
11
+ "homepage": "https://github.com/openfort-xyz/react-native#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/openfort-xyz/react-native/issues"
14
+ },
7
15
  "types": "dist/types/index.d.ts",
8
16
  "files": [
9
17
  "dist"
@@ -16,7 +24,7 @@
16
24
  }
17
25
  },
18
26
  "dependencies": {
19
- "@openfort/openfort-js": "^0.10.39"
27
+ "@openfort/openfort-js": "^1.0.0"
20
28
  },
21
29
  "peerDependencies": {
22
30
  "expo-apple-authentication": "*",
@@ -33,6 +41,7 @@
33
41
  "@biomejs/biome": "^2.2.4",
34
42
  "@changesets/changelog-github": "^0.5.1",
35
43
  "@changesets/cli": "^2.29.7",
44
+ "@size-limit/preset-small-lib": "^11.2.0",
36
45
  "@types/react": "~18.3.12",
37
46
  "buffer": "^5.4.3",
38
47
  "expo-apple-authentication": "^7.2.4",
@@ -45,60 +54,32 @@
45
54
  "react": "^18.3.1",
46
55
  "react-native": "0.77.1",
47
56
  "react-native-webview": "^13.15.0",
57
+ "size-limit": "^11.2.0",
48
58
  "typedoc": "^0.28.14",
49
59
  "typescript": "^5.9.3"
50
60
  },
51
61
  "knip": {
62
+ "entry": [
63
+ "src/index.ts!"
64
+ ],
52
65
  "ignore": [
53
66
  ".github/**",
54
67
  ".husky/**",
55
- ".parcel-cache/**",
56
- "**/.next/**",
57
68
  "**/dist/**",
58
- "**/stats.html",
59
- "sdk/copyVersion.cjs",
60
- "sdk/scripts/**",
61
- "test/**",
62
- "**/next-env.d.ts",
63
- "**/vite-env.d.ts"
69
+ "test/**"
64
70
  ],
65
71
  "ignoreDependencies": [
66
72
  "buffer"
67
73
  ],
68
- "jest": {
69
- "entry": [
70
- "**/*.{test,spec}.{js,ts,tsx}"
71
- ]
72
- },
73
- "workspaces": {
74
- ".": {
75
- "entry": [
76
- "typedoc.json!"
77
- ]
78
- },
79
- "sdk": {
80
- "entry": [
81
- "src/index.ts!"
82
- ],
83
- "ignore": [
84
- "version.ts"
85
- ],
86
- "ignoreDependencies": [
87
- "axios-retry"
88
- ]
89
- },
90
- "packages/internal/openapi-clients": {
91
- "entry": [
92
- "src/backend-api-clients.ts!",
93
- "src/backend/index.ts!"
94
- ],
95
- "ignore": [
96
- "src/backend-openapi.json"
97
- ]
98
- }
99
- }
74
+ "ignoreExportsUsedInFile": true
100
75
  },
101
76
  "private": false,
77
+ "size-limit": [
78
+ {
79
+ "path": "dist/index.js",
80
+ "limit": "250 KB"
81
+ }
82
+ ],
102
83
  "scripts": {
103
84
  "build": "tsc",
104
85
  "check": "biome check",
@@ -111,6 +92,8 @@
111
92
  "lint": "tsc -b && biome lint .",
112
93
  "link": "pnpm link",
113
94
  "unlink": "pnpm unlink",
114
- "docs:build": "typedoc"
95
+ "docs:build": "typedoc",
96
+ "size": "size-limit",
97
+ "size:check": "size-limit --ci"
115
98
  }
116
99
  }