@openfort/react-native 0.0.3 → 0.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.
Files changed (91) hide show
  1. package/README.md +0 -85
  2. package/dist/components/AuthBoundary.js +83 -0
  3. package/dist/components/index.js +10 -0
  4. package/dist/constants/config.js +9 -0
  5. package/dist/constants/index.js +1 -0
  6. package/dist/core/client.js +78 -0
  7. package/dist/core/context.js +37 -0
  8. package/dist/core/index.js +10 -0
  9. package/dist/core/provider.js +224 -0
  10. package/dist/core/storage.js +141 -0
  11. package/dist/hooks/auth/index.js +13 -0
  12. package/dist/hooks/auth/useAuthCallback.js +1 -0
  13. package/dist/hooks/auth/useCreateWalletPostAuth.js +22 -0
  14. package/dist/hooks/auth/useEmailAuth.js +169 -0
  15. package/dist/hooks/auth/useGuestAuth.js +52 -0
  16. package/dist/hooks/auth/useOAuth.js +292 -0
  17. package/dist/hooks/auth/useSignOut.js +48 -0
  18. package/dist/hooks/auth/useUser.js +1 -0
  19. package/dist/hooks/auth/useWalletAuth.js +122 -0
  20. package/dist/hooks/core/index.js +10 -0
  21. package/dist/hooks/core/useOpenfort.js +50 -0
  22. package/dist/hooks/core/useOpenfortClient.js +29 -0
  23. package/dist/hooks/core/useSignOut.js +7 -0
  24. package/dist/hooks/core/useUser.js +10 -0
  25. package/dist/hooks/index.js +15 -0
  26. package/dist/hooks/wallet/index.js +7 -0
  27. package/dist/hooks/wallet/useWallets.js +389 -0
  28. package/dist/index.js +24 -1
  29. package/dist/lib/hookConsistency.js +16 -0
  30. package/dist/native/index.js +6 -0
  31. package/dist/native/oauth.js +183 -0
  32. package/dist/native/storage.js +178 -0
  33. package/dist/native/webview.js +157 -0
  34. package/dist/types/auth.js +1 -0
  35. package/dist/types/baseFlowState.js +8 -0
  36. package/dist/types/components/AuthBoundary.d.ts +85 -0
  37. package/dist/types/components/index.d.ts +10 -0
  38. package/dist/types/config.js +1 -0
  39. package/dist/types/constants/config.d.ts +9 -0
  40. package/dist/types/constants/index.d.ts +1 -0
  41. package/dist/types/core/client.d.ts +24 -0
  42. package/dist/types/core/context.d.ts +61 -0
  43. package/dist/types/core/index.d.ts +8 -0
  44. package/dist/types/core/provider.d.ts +126 -0
  45. package/dist/types/core/storage.d.ts +34 -0
  46. package/dist/types/hex.js +1 -0
  47. package/dist/types/hookOption.js +1 -0
  48. package/dist/types/hooks/auth/index.d.ts +9 -0
  49. package/dist/types/hooks/auth/useAuthCallback.d.ts +0 -0
  50. package/dist/types/hooks/auth/useCreateWalletPostAuth.d.ts +6 -0
  51. package/dist/types/hooks/auth/useEmailAuth.d.ts +54 -0
  52. package/dist/types/hooks/auth/useGuestAuth.d.ts +39 -0
  53. package/dist/types/hooks/auth/useOAuth.d.ts +62 -0
  54. package/dist/types/hooks/auth/useSignOut.d.ts +9 -0
  55. package/dist/types/hooks/auth/useUser.d.ts +0 -0
  56. package/dist/types/hooks/auth/useWalletAuth.d.ts +42 -0
  57. package/dist/types/hooks/core/index.d.ts +9 -0
  58. package/dist/types/hooks/core/useOpenfort.d.ts +38 -0
  59. package/dist/types/hooks/core/useOpenfortClient.d.ts +29 -0
  60. package/dist/types/hooks/core/useSignOut.d.ts +3 -0
  61. package/dist/types/hooks/core/useUser.d.ts +5 -0
  62. package/dist/types/hooks/index.d.ts +12 -0
  63. package/dist/types/hooks/wallet/index.d.ts +6 -0
  64. package/dist/types/hooks/wallet/useWallets.d.ts +74 -0
  65. package/dist/types/index.d.ts +18 -1
  66. package/dist/types/index.js +2 -0
  67. package/dist/types/lib/hookConsistency.d.ts +14 -0
  68. package/dist/types/native/index.d.ts +5 -0
  69. package/dist/types/native/oauth.d.ts +91 -0
  70. package/dist/types/native/storage.d.ts +50 -0
  71. package/dist/types/native/webview.d.ts +50 -0
  72. package/dist/types/oauth.js +8 -0
  73. package/dist/types/openfortError.js +27 -0
  74. package/dist/types/predicates.js +101 -0
  75. package/dist/types/state.js +1 -0
  76. package/dist/types/types/auth.d.ts +168 -0
  77. package/dist/types/types/baseFlowState.d.ts +14 -0
  78. package/dist/types/types/config.d.ts +71 -0
  79. package/dist/types/types/hex.d.ts +1 -0
  80. package/dist/types/types/hookOption.d.ts +9 -0
  81. package/dist/types/types/index.d.ts +38 -0
  82. package/dist/types/types/oauth.d.ts +74 -0
  83. package/dist/types/types/openfortError.d.ts +13 -0
  84. package/dist/types/types/predicates.d.ts +64 -0
  85. package/dist/types/types/state.d.ts +0 -0
  86. package/dist/types/types/wallet.d.ts +262 -0
  87. package/dist/types/wallet.js +1 -0
  88. package/package.json +33 -19
  89. package/dist/Iframe.js +0 -84
  90. package/dist/types/Iframe.d.ts +0 -6
  91. package/polyfills/index.ts +0 -89
package/README.md CHANGED
@@ -1,85 +0,0 @@
1
- # Openfort React native SDK
2
-
3
- ## Install required packages:
4
-
5
- Using the package manager of your preference, install the openfort-js react native library, e.g. with yarn: `yarn add @openfort/react-native @openfort/openfort-js`.
6
-
7
- Since react native requires installing native dependencies directly, you also have to install these required dependencies
8
- ```
9
- yarn add buffer react-native-crypto react-native-get-random-values react-native-randombytes stream-browserify react-native-mmkv
10
- ```
11
-
12
- ## Setup your metro config
13
-
14
- If you do not already have a `metro.config.js`, create one with those required extra node modules:
15
- ```
16
- // sample metro.config.js
17
- const { getDefaultConfig } = require('expo/metro-config');
18
-
19
- module.exports = (() => {
20
- const config = getDefaultConfig(__dirname);
21
-
22
- // Add shims for Node.js modules like crypto and stream
23
- config.resolver.extraNodeModules = {
24
- crypto: require.resolve('react-native-crypto'),
25
- stream: require.resolve('stream-browserify'),
26
- buffer: require.resolve('buffer'),
27
- };
28
-
29
- return config;
30
- })();
31
-
32
- ```
33
- ## Import `Openfort` at the top of your app
34
- The first file loaded should be Openfort-js polyfills:
35
- ```
36
- import "@openfort/react-native/polyfills";
37
- ```
38
- This will ensure the correct modules are imported and will ensure `openfort-js` works properly.
39
-
40
- ## Configure `Openfort`
41
-
42
- Configure openfort, the same way that you would with openfort-js
43
- ```
44
- import Openfort from "@openfort/openfort-js";
45
-
46
- const openfort = new Openfort({
47
- baseConfiguration: {
48
- publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY",
49
- },
50
- shieldConfiguration: {
51
- shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",
52
- }
53
- });
54
- ```
55
- Check out the documentation [here](https://www.openfort.xyz/docs/guides/getting-started#4-import-openfort-into-your-app)
56
-
57
- ## Render secure WebView
58
-
59
- Openfort uses a `WebView` (from `react-native-webview`) to operate as a secure environment, managing the private key and executing wallet operations. [Learn more](https://www.openfort.xyz/docs/security#embedded-self-custodial-signer).
60
-
61
- This WebView needs to always be displayed, it is recommended to put it on top of your app. It is wrapped inside a view with `flex: 0`
62
-
63
- Simply import it from `@openfort/react-native`
64
-
65
- ```
66
- // Sample app/_layout.tsx using expo router
67
- import { Iframe } from '@openfort/react-native';
68
-
69
- export default function RootLayout() {
70
-
71
- return (
72
- <>
73
- <Iframe />
74
- <Slot />
75
- </>
76
- );
77
- }
78
- ```
79
- ## Notes
80
-
81
- Because we are using `mmkv` storage, expo-go will not work. To run your app use `expo run:ios` or `expo run:android`.
82
-
83
- # Sample
84
-
85
- You can check out the [React native auth sample](https://github.com/openfort-xyz/react-native-auth-sample) to get your app running.
@@ -0,0 +1,83 @@
1
+ import React from 'react';
2
+ import { useOpenfort } from '../hooks/core/useOpenfort';
3
+ /**
4
+ * Authentication boundary component that conditionally renders content based on
5
+ * the user's authentication status and SDK readiness.
6
+ *
7
+ * This component simplifies protecting routes and content based on authentication state.
8
+ * It handles three main states:
9
+ * 1. Loading - SDK is initializing
10
+ * 2. Error - SDK encountered an initialization error
11
+ * 3. Unauthenticated - User is not logged in
12
+ * 4. Authenticated - User is logged in and SDK is ready
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * import { AuthBoundary } from '@openfort/react-native';
17
+ * import { Text, ActivityIndicator } from 'react-native';
18
+ *
19
+ * function App() {
20
+ * return (
21
+ * <AuthBoundary
22
+ * loading={<ActivityIndicator size="large" />}
23
+ * unauthenticated={<LoginScreen />}
24
+ * error={(error) => <Text>Error: {error.message}</Text>}
25
+ * >
26
+ * <AuthenticatedApp />
27
+ * </AuthBoundary>
28
+ * );
29
+ * }
30
+ * ```
31
+ *
32
+ * @example
33
+ * // With React Navigation
34
+ * ```tsx
35
+ * import { AuthBoundary } from '@openfort/react-native';
36
+ * import { NavigationContainer } from '@react-navigation/native';
37
+ * import { createNativeStackNavigator } from '@react-navigation/native-stack';
38
+ *
39
+ * const Stack = createNativeStackNavigator();
40
+ *
41
+ * function App() {
42
+ * return (
43
+ * <NavigationContainer>
44
+ * <AuthBoundary
45
+ * loading={<SplashScreen />}
46
+ * unauthenticated={
47
+ * <Stack.Navigator>
48
+ * <Stack.Screen name="Login" component={LoginScreen} />
49
+ * <Stack.Screen name="Signup" component={SignupScreen} />
50
+ * </Stack.Navigator>
51
+ * }
52
+ * >
53
+ * <Stack.Navigator>
54
+ * <Stack.Screen name="Home" component={HomeScreen} />
55
+ * <Stack.Screen name="Profile" component={ProfileScreen} />
56
+ * </Stack.Navigator>
57
+ * </AuthBoundary>
58
+ * </NavigationContainer>
59
+ * );
60
+ * }
61
+ * ```
62
+ */
63
+ export const AuthBoundary = ({ loading, unauthenticated, error: errorComponent, children, }) => {
64
+ const { user, isReady, error } = useOpenfort();
65
+ // SDK encountered an error during initialization
66
+ if (error && errorComponent) {
67
+ if (typeof errorComponent === 'function') {
68
+ return React.createElement(React.Fragment, null, errorComponent(error));
69
+ }
70
+ return React.createElement(React.Fragment, null, errorComponent);
71
+ }
72
+ // SDK is still initializing
73
+ if (!isReady) {
74
+ return React.createElement(React.Fragment, null, loading);
75
+ }
76
+ // User is not authenticated
77
+ if (!user) {
78
+ return React.createElement(React.Fragment, null, unauthenticated);
79
+ }
80
+ // User is authenticated and SDK is ready
81
+ return React.createElement(React.Fragment, null, children);
82
+ };
83
+ export default AuthBoundary;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Openfort React Native SDK Components
3
+ *
4
+ * This module provides React components and hooks for building user interfaces
5
+ * that integrate with the Openfort platform in React Native applications.
6
+ *
7
+ * The components are organized into the following categories:
8
+ * - Authentication boundaries and guards
9
+ */
10
+ export { AuthBoundary } from './AuthBoundary';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * SDK package information
3
+ */
4
+ export const SDK_INFO = {
5
+ name: '@openfort/react-native',
6
+ version: '0.1.0',
7
+ description: 'React Native client for Openfort',
8
+ homepage: 'https://openfort.io/docs',
9
+ };
@@ -0,0 +1 @@
1
+ export * from './config';
@@ -0,0 +1,78 @@
1
+ import { Openfort as OpenfortClient } from '@openfort/openfort-js';
2
+ import { digest } from 'expo-crypto';
3
+ import { applicationId } from 'expo-application';
4
+ import { SecureStorageAdapter, createNormalizedStorage } from './storage';
5
+ /**
6
+ * Creates an instance of the Openfort client configured for Expo/React Native
7
+ *
8
+ * @param options Configuration options for the Openfort client
9
+ * @returns Configured Openfort client instance
10
+ *
11
+ * @example
12
+ * const client = createOpenfortClient({
13
+ * });
14
+ *
15
+ * const token = await client.getAccessToken();
16
+ */
17
+ export function createOpenfortClient({ baseConfiguration, overrides, shieldConfiguration, }) {
18
+ const nativeAppId = getNativeApplicationId();
19
+ console.log('Creating Openfort client with native app ID:', nativeAppId);
20
+ // appId,
21
+ // clientId,
22
+ // supportedChains,
23
+ // storage: createNormalizedStorage(storage),
24
+ // sdkVersion: `expo:${SDK_INFO.version}`,
25
+ // nativeAppIdentifier: nativeAppId,
26
+ // crypto: {
27
+ // digest,
28
+ // },
29
+ // baseUrl,
30
+ // logLevel,
31
+ return new OpenfortClient({
32
+ baseConfiguration,
33
+ overrides: {
34
+ ...overrides,
35
+ crypto: {
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ digest: digest,
38
+ },
39
+ storage: createNormalizedStorage(SecureStorageAdapter)
40
+ },
41
+ shieldConfiguration
42
+ });
43
+ }
44
+ /**
45
+ * Gets the native application identifier from Expo
46
+ * Throws an error if the identifier cannot be determined
47
+ */
48
+ function getNativeApplicationId() {
49
+ if (typeof applicationId !== 'string') {
50
+ throw new Error('Cannot determine native application ID. Please make sure `expo-application` is installed as a dependency and that `ios.bundleId` or `android.package` is set.');
51
+ }
52
+ return applicationId;
53
+ }
54
+ /**
55
+ * Default Openfort client instance - should only be used internally
56
+ * Applications should create their own client instances using createOpenfortClient
57
+ */
58
+ let defaultClient = null;
59
+ /**
60
+ * Gets or creates the default Openfort client instance
61
+ * @internal
62
+ */
63
+ export function getDefaultClient(options) {
64
+ if (!defaultClient && options) {
65
+ defaultClient = new OpenfortClient(options);
66
+ }
67
+ if (!defaultClient) {
68
+ throw new Error('Openfort client not initialized. Make sure to wrap your app with OpenfortProvider.');
69
+ }
70
+ return defaultClient;
71
+ }
72
+ /**
73
+ * Sets the default Openfort client instance
74
+ * @internal
75
+ */
76
+ export function setDefaultClient(client) {
77
+ defaultClient = client;
78
+ }
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ /**
3
+ * React context for sharing Openfort SDK state throughout the component tree
4
+ */
5
+ export const OpenfortContext = React.createContext(null);
6
+ /**
7
+ * Hook to access the Openfort context
8
+ * Throws an error if used outside of a OpenfortProvider
9
+ */
10
+ export function useOpenfortContext() {
11
+ const context = React.useContext(OpenfortContext);
12
+ if (!context) {
13
+ throw new Error('useOpenfortContext must be used within a OpenfortProvider. ' +
14
+ 'Make sure to wrap your app with <OpenfortProvider>.');
15
+ }
16
+ return context;
17
+ }
18
+ /**
19
+ * Hook to safely access the Openfort context
20
+ * Returns null if used outside of a OpenfortProvider
21
+ */
22
+ export function useOpenfortContextSafe() {
23
+ return React.useContext(OpenfortContext);
24
+ }
25
+ /**
26
+ * Type guard to check if a value is a valid OpenfortContextValue
27
+ */
28
+ export function isOpenfortContextValue(value) {
29
+ return (typeof value === 'object' &&
30
+ value !== null &&
31
+ 'client' in value &&
32
+ 'isReady' in value &&
33
+ 'logout' in value &&
34
+ 'getAccessToken' in value &&
35
+ 'embeddedState' in value &&
36
+ '_internal' in value);
37
+ }
@@ -0,0 +1,10 @@
1
+ // Client creation and configuration
2
+ export { createOpenfortClient, getDefaultClient, setDefaultClient } from './client';
3
+ // Re-export important types and enums from openfort-js
4
+ export { RecoveryMethod } from '@openfort/openfort-js';
5
+ // React context and hooks
6
+ export { OpenfortContext, useOpenfortContext, useOpenfortContextSafe, isOpenfortContextValue } from './context';
7
+ // Main provider component
8
+ export { OpenfortProvider } from './provider';
9
+ // Storage adapters
10
+ export { SecureStorageAdapter, createNormalizedStorage } from './storage';
@@ -0,0 +1,224 @@
1
+ import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
2
+ import { OpenfortConfiguration, ShieldConfiguration, EmbeddedState } from '@openfort/openfort-js';
3
+ import { OpenfortContext } from './context';
4
+ import { createOpenfortClient, setDefaultClient } from './client';
5
+ import { EmbeddedWalletWebView, WebViewUtils } from '../native';
6
+ /**
7
+ * Main provider component that wraps the entire application and provides
8
+ * Openfort SDK functionality through React context
9
+ */
10
+ export const OpenfortProvider = ({ children, publishableKey, customAuth, supportedChains, walletConfig, overrides, }) => {
11
+ // Prevent multiple OpenfortProvider instances
12
+ const existingContext = React.useContext(OpenfortContext);
13
+ if (existingContext) {
14
+ throw new Error('Found multiple instances of OpenfortProvider. Ensure there is only one mounted in your application tree.');
15
+ }
16
+ // Create or use provided client
17
+ const client = useMemo(() => {
18
+ const newClient = createOpenfortClient({
19
+ baseConfiguration: new OpenfortConfiguration({
20
+ publishableKey: publishableKey,
21
+ }),
22
+ shieldConfiguration: walletConfig ? new ShieldConfiguration({
23
+ shieldPublishableKey: walletConfig.shieldPublishableKey,
24
+ shieldEncryptionKey: 'shieldEncryptionKey' in walletConfig ? walletConfig.shieldEncryptionKey : undefined,
25
+ shieldDebug: walletConfig.debug,
26
+ }) : undefined,
27
+ overrides: {
28
+ ...overrides,
29
+ },
30
+ });
31
+ setDefaultClient(newClient);
32
+ return newClient;
33
+ }, [publishableKey, walletConfig, overrides]);
34
+ // Embedded state
35
+ const [embeddedState, setEmbeddedState] = useState(EmbeddedState.NONE);
36
+ const pollingRef = useRef(null);
37
+ const pollEmbeddedState = useCallback(async () => {
38
+ if (!client)
39
+ return;
40
+ try {
41
+ const state = await client.embeddedWallet.getEmbeddedState();
42
+ console.log('Current embedded state:', state);
43
+ setEmbeddedState(state);
44
+ }
45
+ catch (error) {
46
+ console.error('Error checking embedded state with Openfort:', error);
47
+ if (pollingRef.current)
48
+ clearInterval(pollingRef.current);
49
+ }
50
+ }, [client]);
51
+ const startPollingEmbeddedState = useCallback(() => {
52
+ if (pollingRef.current)
53
+ return;
54
+ pollingRef.current = setInterval(pollEmbeddedState, 1000);
55
+ }, [pollEmbeddedState]);
56
+ const stopPollingEmbeddedState = useCallback(() => {
57
+ clearInterval(pollingRef.current || undefined);
58
+ pollingRef.current = null;
59
+ }, []);
60
+ useEffect(() => {
61
+ if (!client)
62
+ return;
63
+ startPollingEmbeddedState();
64
+ return () => {
65
+ stopPollingEmbeddedState();
66
+ };
67
+ }, [client]);
68
+ // Core state
69
+ const [user, setUser] = useState(null);
70
+ const [isUserInitialized, setIsUserInitialized] = useState(false);
71
+ const [isClientReady, setIsClientReady] = useState(false);
72
+ const [error, setError] = useState(null);
73
+ // Flow states
74
+ const [passwordState, setPasswordState] = useState({ status: 'initial' });
75
+ const [oAuthState, setOAuthState] = useState({ status: 'initial' });
76
+ const [siweState, setSiweState] = useState({ status: 'initial' });
77
+ const [recoveryFlowState, setRecoveryFlowState] = useState({ status: 'initial' });
78
+ // User state management
79
+ const handleUserChange = useCallback((newUser) => {
80
+ console.log('User state changed:', newUser);
81
+ setUser(newUser);
82
+ if (newUser) {
83
+ setError(null);
84
+ }
85
+ }, []);
86
+ // Core methods
87
+ const logout = useCallback(async () => {
88
+ handleUserChange(null);
89
+ return client.auth.logout();
90
+ }, [client, handleUserChange]);
91
+ const getAccessToken = useCallback(async () => {
92
+ try {
93
+ return await client.getAccessToken();
94
+ }
95
+ catch (err) {
96
+ console.debug('Failed to get access token:', err);
97
+ return null;
98
+ }
99
+ }, [client]);
100
+ // Initialize client and user
101
+ useEffect(() => {
102
+ console.log('Initializing Openfort client and user state', isUserInitialized);
103
+ if (!isUserInitialized) {
104
+ (async () => {
105
+ console.log('Initializing Openfort client');
106
+ try {
107
+ // Openfort client doesn't need explicit initialization
108
+ setIsClientReady(true);
109
+ }
110
+ catch (err) {
111
+ setError(err instanceof Error ? err : new Error(String(err)));
112
+ }
113
+ try {
114
+ console.log('Refreshing user state on initial load');
115
+ await refreshUserState();
116
+ }
117
+ catch {
118
+ // User not logged in, which is fine
119
+ handleUserChange(null);
120
+ }
121
+ finally {
122
+ setIsUserInitialized(true);
123
+ }
124
+ })();
125
+ }
126
+ }, [client, isUserInitialized, handleUserChange]);
127
+ // Internal refresh function for auth hooks to use
128
+ const refreshUserState = useCallback(async (user) => {
129
+ try {
130
+ console.log('Refreshing user state', user);
131
+ // If user is provided, use it directly instead of fetching from API
132
+ if (user !== undefined) {
133
+ handleUserChange(user);
134
+ return user;
135
+ }
136
+ // Otherwise, fetch from API
137
+ const currentUser = await client.user.get();
138
+ console.log('Refreshed user state:', currentUser);
139
+ handleUserChange(currentUser);
140
+ return currentUser;
141
+ }
142
+ catch (err) {
143
+ console.log('Failed to refresh user state:', err);
144
+ handleUserChange(null);
145
+ return null;
146
+ }
147
+ }, [client, handleUserChange]);
148
+ // Custom auth state management
149
+ useEffect(() => {
150
+ if (customAuth?.enabled && isUserInitialized && isClientReady) {
151
+ (async () => {
152
+ try {
153
+ const { getCustomAccessToken, isLoading } = customAuth;
154
+ if (isLoading)
155
+ return;
156
+ const customToken = await getCustomAccessToken();
157
+ if (customToken) {
158
+ // Custom auth sync implementation would go here
159
+ // This would typically handle SIWE authentication with the custom token
160
+ console.debug('Custom token available for authentication sync');
161
+ }
162
+ }
163
+ catch (err) {
164
+ console.error('Custom auth sync failed:', err);
165
+ }
166
+ })();
167
+ }
168
+ }, [client, customAuth, isUserInitialized, isClientReady]);
169
+ // Determine if SDK is ready
170
+ const isReady = useMemo(() => {
171
+ const customAuthReady = !customAuth?.enabled || !customAuth.isLoading;
172
+ return isUserInitialized && isClientReady && customAuthReady;
173
+ }, [isUserInitialized, isClientReady, customAuth?.enabled, customAuth?.isLoading]);
174
+ // Context value
175
+ const contextValue = useMemo(() => ({
176
+ client,
177
+ user,
178
+ isReady,
179
+ error,
180
+ supportedChains,
181
+ embeddedWallet: walletConfig,
182
+ embeddedState,
183
+ // Flow states
184
+ passwordState,
185
+ oAuthState,
186
+ siweState,
187
+ recoveryFlowState,
188
+ // State setters
189
+ setPasswordState,
190
+ setOAuthState,
191
+ setSiweState,
192
+ setRecoveryFlowState,
193
+ // Core methods
194
+ logout,
195
+ getAccessToken,
196
+ // Internal methods
197
+ _internal: {
198
+ refreshUserState,
199
+ },
200
+ }), [
201
+ client,
202
+ user,
203
+ isReady,
204
+ error,
205
+ supportedChains,
206
+ walletConfig,
207
+ embeddedState,
208
+ passwordState,
209
+ oAuthState,
210
+ siweState,
211
+ recoveryFlowState,
212
+ logout,
213
+ getAccessToken,
214
+ refreshUserState,
215
+ ]);
216
+ return (React.createElement(OpenfortContext.Provider, { value: contextValue },
217
+ children,
218
+ client && isReady && WebViewUtils.isSupported() && (React.createElement(EmbeddedWalletWebView, { client: client, isClientReady: isReady, onProxyStatusChange: (status) => {
219
+ // Handle WebView status changes for debugging
220
+ if (process.env.NODE_ENV === 'development') {
221
+ console.debug('WebView status changed:', status);
222
+ }
223
+ } }))));
224
+ };
@@ -0,0 +1,141 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import * as SecureStore from 'expo-secure-store';
3
+ import { NativeStorageUtils } from '../native';
4
+ // Define the StorageKeys enum values that match the Openfort SDK
5
+ var StorageKeys;
6
+ (function (StorageKeys) {
7
+ StorageKeys["AUTHENTICATION"] = "openfort.authentication";
8
+ StorageKeys["SIGNER"] = "openfort.signer";
9
+ StorageKeys["CONFIGURATION"] = "openfort.configuration";
10
+ StorageKeys["ACCOUNT"] = "openfort.account";
11
+ StorageKeys["TEST"] = "openfort.test";
12
+ StorageKeys["RECOVERY"] = "openfort.recovery";
13
+ StorageKeys["SESSION"] = "openfort.session";
14
+ StorageKeys["PKCE_STATE"] = "openfort.pkce_state";
15
+ StorageKeys["PKCE_VERIFIER"] = "openfort.pkce_verifier";
16
+ })(StorageKeys || (StorageKeys = {}));
17
+ /**
18
+ * Storage adapter using `expo-secure-store` intended for
19
+ * use with `Openfort` class from `@openfort/openfort-js`.
20
+ */
21
+ export const SecureStorageAdapter = {
22
+ async get(key) {
23
+ try {
24
+ const normalizedKey = normalizeKey(key);
25
+ const result = await SecureStore.getItemAsync(normalizedKey, NativeStorageUtils.getStorageOptions());
26
+ // If result is a string (as expected), return it
27
+ if (typeof result === 'string' || result === null) {
28
+ return result;
29
+ }
30
+ // Handle unexpected Promise-like objects (shouldn't happen according to docs)
31
+ if (result && typeof result === 'object' && '_j' in result) {
32
+ console.warn('WARNING: SecureStore returned a Promise-like object instead of a string');
33
+ const actualValue = result._j;
34
+ return typeof actualValue === 'string' ? actualValue : null;
35
+ }
36
+ // If we get here, something is wrong
37
+ console.error('Unexpected result type from SecureStore:', result);
38
+ return null;
39
+ }
40
+ catch (error) {
41
+ console.warn('Failed to get item from secure store:', error);
42
+ return null;
43
+ }
44
+ },
45
+ async save(key, value) {
46
+ try {
47
+ const normalizedKey = normalizeKey(key);
48
+ await SecureStore.setItemAsync(normalizedKey, value, NativeStorageUtils.getStorageOptions());
49
+ }
50
+ catch (error) {
51
+ console.warn('Failed to set item in secure store:', error);
52
+ throw error;
53
+ }
54
+ },
55
+ async remove(key) {
56
+ try {
57
+ const normalizedKey = normalizeKey(key);
58
+ await SecureStore.deleteItemAsync(normalizedKey, NativeStorageUtils.getStorageOptions());
59
+ }
60
+ catch (error) {
61
+ console.warn('Failed to delete item from secure store:', error);
62
+ throw error;
63
+ }
64
+ },
65
+ flush() {
66
+ // SecureStore doesn't provide a way to list all keys
67
+ // This is a no-op for secure store
68
+ },
69
+ // Additional utility methods using native storage utilities
70
+ async keyExists(key) {
71
+ const normalizedKey = normalizeKey(key);
72
+ return await NativeStorageUtils.keyExists(normalizedKey);
73
+ },
74
+ async getStorageInfo() {
75
+ return await NativeStorageUtils.getStorageInfo();
76
+ },
77
+ };
78
+ /**
79
+ * Normalizes storage keys by replacing colons with hyphens
80
+ * to ensure compatibility with expo-secure-store
81
+ */
82
+ function normalizeKey(key) {
83
+ return key.replaceAll(':', '-');
84
+ }
85
+ /**
86
+ * Creates a type-safe storage adapter that bridges between the Openfort SDK's
87
+ * expected Storage interface and our React Native implementation
88
+ */
89
+ export function createNormalizedStorage(customStorage) {
90
+ const baseStorage = customStorage || SecureStorageAdapter;
91
+ return {
92
+ async get(key) {
93
+ // Convert the unknown key to our StorageKeys enum
94
+ const storageKey = keyToStorageKeys(key);
95
+ const result = await baseStorage.get(storageKey);
96
+ return result;
97
+ },
98
+ save(key, value) {
99
+ console.log('storage save:', key, value);
100
+ const storageKey = keyToStorageKeys(key);
101
+ // Fire and forget - don't await as the SDK expects synchronous behavior
102
+ baseStorage.save(storageKey, value).catch(error => {
103
+ console.error('Failed to save to storage:', error);
104
+ });
105
+ },
106
+ remove(key) {
107
+ console.log('storage remove:', key);
108
+ const storageKey = keyToStorageKeys(key);
109
+ // Fire and forget - don't await as the SDK expects synchronous behavior
110
+ baseStorage.remove(storageKey).catch(error => {
111
+ console.error('Failed to remove from storage:', error);
112
+ });
113
+ },
114
+ flush() {
115
+ console.log('storage flush');
116
+ baseStorage.flush();
117
+ },
118
+ };
119
+ }
120
+ /**
121
+ * Converts an unknown key (likely from the Openfort SDK) to our StorageKeys enum
122
+ */
123
+ function keyToStorageKeys(key) {
124
+ if (typeof key === 'string') {
125
+ // Check if the string matches one of our enum values
126
+ const storageKey = Object.values(StorageKeys).find(value => value === key);
127
+ if (storageKey) {
128
+ return storageKey;
129
+ }
130
+ }
131
+ // If it's an enum-like object, try to get its value
132
+ if (typeof key === 'object' && key !== null && 'toString' in key) {
133
+ const keyString = key.toString();
134
+ const storageKey = Object.values(StorageKeys).find(value => value === keyString);
135
+ if (storageKey) {
136
+ return storageKey;
137
+ }
138
+ }
139
+ // Fallback: throw an error for unknown keys
140
+ throw new Error(`Unknown storage key: ${key}. Expected one of: ${Object.values(StorageKeys).join(', ')}`);
141
+ }