@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.
- package/README.md +0 -85
- package/dist/components/AuthBoundary.js +83 -0
- package/dist/components/index.js +10 -0
- package/dist/constants/config.js +9 -0
- package/dist/constants/index.js +1 -0
- package/dist/core/client.js +78 -0
- package/dist/core/context.js +37 -0
- package/dist/core/index.js +10 -0
- package/dist/core/provider.js +224 -0
- package/dist/core/storage.js +141 -0
- package/dist/hooks/auth/index.js +13 -0
- package/dist/hooks/auth/useAuthCallback.js +1 -0
- package/dist/hooks/auth/useCreateWalletPostAuth.js +22 -0
- package/dist/hooks/auth/useEmailAuth.js +169 -0
- package/dist/hooks/auth/useGuestAuth.js +52 -0
- package/dist/hooks/auth/useOAuth.js +292 -0
- package/dist/hooks/auth/useSignOut.js +48 -0
- package/dist/hooks/auth/useUser.js +1 -0
- package/dist/hooks/auth/useWalletAuth.js +122 -0
- package/dist/hooks/core/index.js +10 -0
- package/dist/hooks/core/useOpenfort.js +50 -0
- package/dist/hooks/core/useOpenfortClient.js +29 -0
- package/dist/hooks/core/useSignOut.js +7 -0
- package/dist/hooks/core/useUser.js +10 -0
- package/dist/hooks/index.js +15 -0
- package/dist/hooks/wallet/index.js +7 -0
- package/dist/hooks/wallet/useWallets.js +389 -0
- package/dist/index.js +24 -1
- package/dist/lib/hookConsistency.js +16 -0
- package/dist/native/index.js +6 -0
- package/dist/native/oauth.js +183 -0
- package/dist/native/storage.js +178 -0
- package/dist/native/webview.js +157 -0
- package/dist/types/auth.js +1 -0
- package/dist/types/baseFlowState.js +8 -0
- package/dist/types/components/AuthBoundary.d.ts +85 -0
- package/dist/types/components/index.d.ts +10 -0
- package/dist/types/config.js +1 -0
- package/dist/types/constants/config.d.ts +9 -0
- package/dist/types/constants/index.d.ts +1 -0
- package/dist/types/core/client.d.ts +24 -0
- package/dist/types/core/context.d.ts +61 -0
- package/dist/types/core/index.d.ts +8 -0
- package/dist/types/core/provider.d.ts +126 -0
- package/dist/types/core/storage.d.ts +34 -0
- package/dist/types/hex.js +1 -0
- package/dist/types/hookOption.js +1 -0
- package/dist/types/hooks/auth/index.d.ts +9 -0
- package/dist/types/hooks/auth/useAuthCallback.d.ts +0 -0
- package/dist/types/hooks/auth/useCreateWalletPostAuth.d.ts +6 -0
- package/dist/types/hooks/auth/useEmailAuth.d.ts +54 -0
- package/dist/types/hooks/auth/useGuestAuth.d.ts +39 -0
- package/dist/types/hooks/auth/useOAuth.d.ts +62 -0
- package/dist/types/hooks/auth/useSignOut.d.ts +9 -0
- package/dist/types/hooks/auth/useUser.d.ts +0 -0
- package/dist/types/hooks/auth/useWalletAuth.d.ts +42 -0
- package/dist/types/hooks/core/index.d.ts +9 -0
- package/dist/types/hooks/core/useOpenfort.d.ts +38 -0
- package/dist/types/hooks/core/useOpenfortClient.d.ts +29 -0
- package/dist/types/hooks/core/useSignOut.d.ts +3 -0
- package/dist/types/hooks/core/useUser.d.ts +5 -0
- package/dist/types/hooks/index.d.ts +12 -0
- package/dist/types/hooks/wallet/index.d.ts +6 -0
- package/dist/types/hooks/wallet/useWallets.d.ts +74 -0
- package/dist/types/index.d.ts +18 -1
- package/dist/types/index.js +2 -0
- package/dist/types/lib/hookConsistency.d.ts +14 -0
- package/dist/types/native/index.d.ts +5 -0
- package/dist/types/native/oauth.d.ts +91 -0
- package/dist/types/native/storage.d.ts +50 -0
- package/dist/types/native/webview.d.ts +50 -0
- package/dist/types/oauth.js +8 -0
- package/dist/types/openfortError.js +27 -0
- package/dist/types/predicates.js +101 -0
- package/dist/types/state.js +1 -0
- package/dist/types/types/auth.d.ts +168 -0
- package/dist/types/types/baseFlowState.d.ts +14 -0
- package/dist/types/types/config.d.ts +71 -0
- package/dist/types/types/hex.d.ts +1 -0
- package/dist/types/types/hookOption.d.ts +9 -0
- package/dist/types/types/index.d.ts +38 -0
- package/dist/types/types/oauth.d.ts +74 -0
- package/dist/types/types/openfortError.d.ts +13 -0
- package/dist/types/types/predicates.d.ts +64 -0
- package/dist/types/types/state.d.ts +0 -0
- package/dist/types/types/wallet.d.ts +262 -0
- package/dist/types/wallet.js +1 -0
- package/package.json +33 -19
- package/dist/Iframe.js +0 -84
- package/dist/types/Iframe.d.ts +0 -6
- package/polyfills/index.ts +0 -89
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication hooks index
|
|
3
|
+
*
|
|
4
|
+
* This module re-exports all authentication-related hooks for convenient importing.
|
|
5
|
+
*/
|
|
6
|
+
// Email authentication
|
|
7
|
+
export { useEmailAuth } from './useEmailAuth';
|
|
8
|
+
// OAuth authentication
|
|
9
|
+
export { useOAuth } from './useOAuth';
|
|
10
|
+
// Wallet-based authentication (SIWE)
|
|
11
|
+
export { useWalletAuth } from './useWalletAuth';
|
|
12
|
+
// Guest accounts
|
|
13
|
+
export { useGuestAuth } from './useGuestAuth';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
// this hook is used to create a wallet after the user has authenticated
|
|
3
|
+
export const useCreateWalletPostAuth = () => {
|
|
4
|
+
// This would connect to the wallet and set it as active
|
|
5
|
+
// eslint-disable-next-line no-empty-pattern
|
|
6
|
+
const tryUseWallet = useCallback(async ({}) => {
|
|
7
|
+
// if (!walletConfig || walletConfig.recoveryMethod !== RecoveryMethod.AUTOMATIC || !automaticRecovery) {
|
|
8
|
+
// return {};
|
|
9
|
+
// }
|
|
10
|
+
// const wallet = await setActiveWallet({
|
|
11
|
+
// connector: embeddedWalletId,
|
|
12
|
+
// });
|
|
13
|
+
// if (wallet.error && signOutOnError) {
|
|
14
|
+
// // If there was an error and we should log out, we can call the logout function
|
|
15
|
+
// await signOut();
|
|
16
|
+
// }
|
|
17
|
+
return { wallet: undefined };
|
|
18
|
+
}, [ /* walletConfig, setActiveWallet, signOut */]);
|
|
19
|
+
return {
|
|
20
|
+
tryUseWallet,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { useOpenfortContext } from '../../core/context';
|
|
3
|
+
import { onError, onSuccess } from '../../lib/hookConsistency';
|
|
4
|
+
import { OpenfortError, OpenfortErrorType } from '../../types/openfortError';
|
|
5
|
+
export const useEmailAuth = (hookOptions = {}) => {
|
|
6
|
+
const { client, setPasswordState, _internal } = useOpenfortContext();
|
|
7
|
+
const signInEmail = useCallback(async (options) => {
|
|
8
|
+
try {
|
|
9
|
+
setPasswordState({ status: 'sending-verification-code' });
|
|
10
|
+
// Login with email and password
|
|
11
|
+
const result = await client.auth.logInWithEmailPassword({
|
|
12
|
+
email: options.email,
|
|
13
|
+
password: options.password,
|
|
14
|
+
});
|
|
15
|
+
// Check if action is required (email verification)
|
|
16
|
+
if ('action' in result) {
|
|
17
|
+
setPasswordState({
|
|
18
|
+
status: 'awaiting-code-input',
|
|
19
|
+
});
|
|
20
|
+
// Return undefined as email verification is required
|
|
21
|
+
return onSuccess({
|
|
22
|
+
hookOptions,
|
|
23
|
+
options,
|
|
24
|
+
data: { requiresEmailVerification: true },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// Login successful
|
|
29
|
+
setPasswordState({ status: 'done' });
|
|
30
|
+
const user = result.player;
|
|
31
|
+
// Refresh user state in provider
|
|
32
|
+
await _internal.refreshUserState(user);
|
|
33
|
+
return onSuccess({
|
|
34
|
+
hookOptions,
|
|
35
|
+
options,
|
|
36
|
+
data: { user },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
const error = new OpenfortError('Failed to login with email and password', OpenfortErrorType.AUTHENTICATION_ERROR, { error: e });
|
|
42
|
+
setPasswordState({
|
|
43
|
+
status: 'error',
|
|
44
|
+
error
|
|
45
|
+
});
|
|
46
|
+
return onError({
|
|
47
|
+
hookOptions,
|
|
48
|
+
options,
|
|
49
|
+
error
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}, [client, setPasswordState, _internal, hookOptions]);
|
|
53
|
+
const signUpEmail = useCallback(async (options) => {
|
|
54
|
+
try {
|
|
55
|
+
setPasswordState({ status: 'sending-verification-code' });
|
|
56
|
+
// Sign up with email and password
|
|
57
|
+
const result = await client.auth.signUpWithEmailPassword({
|
|
58
|
+
email: options.email,
|
|
59
|
+
password: options.password,
|
|
60
|
+
...(options.name && { name: options.name }),
|
|
61
|
+
});
|
|
62
|
+
// Check if action is required (email verification)
|
|
63
|
+
if ('action' in result) {
|
|
64
|
+
setPasswordState({
|
|
65
|
+
status: 'awaiting-code-input',
|
|
66
|
+
});
|
|
67
|
+
// Return undefined as email verification is required
|
|
68
|
+
return onSuccess({
|
|
69
|
+
hookOptions,
|
|
70
|
+
options,
|
|
71
|
+
data: { requiresEmailVerification: true },
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Signup successful
|
|
76
|
+
setPasswordState({ status: 'done' });
|
|
77
|
+
const user = result.player;
|
|
78
|
+
// Refresh user state in provider
|
|
79
|
+
await _internal.refreshUserState(user);
|
|
80
|
+
return onSuccess({
|
|
81
|
+
hookOptions,
|
|
82
|
+
options,
|
|
83
|
+
data: { user },
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
const error = new OpenfortError('Failed to signup with email and password', OpenfortErrorType.AUTHENTICATION_ERROR, { error: e });
|
|
89
|
+
setPasswordState({
|
|
90
|
+
status: 'error',
|
|
91
|
+
error
|
|
92
|
+
});
|
|
93
|
+
return onError({
|
|
94
|
+
hookOptions,
|
|
95
|
+
options,
|
|
96
|
+
error
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}, [client, setPasswordState, _internal, hookOptions]);
|
|
100
|
+
const linkEmail = useCallback(async (options) => {
|
|
101
|
+
try {
|
|
102
|
+
setPasswordState({ status: 'sending-verification-code' });
|
|
103
|
+
// Get current user access token
|
|
104
|
+
const accessToken = await client.getAccessToken();
|
|
105
|
+
if (!accessToken) {
|
|
106
|
+
throw new Error('User must be authenticated to link email');
|
|
107
|
+
}
|
|
108
|
+
// Link email account
|
|
109
|
+
const result = await client.auth.linkEmailPassword({
|
|
110
|
+
email: options.email,
|
|
111
|
+
password: options.password,
|
|
112
|
+
authToken: accessToken,
|
|
113
|
+
});
|
|
114
|
+
// Check if action is required (email verification)
|
|
115
|
+
if ('action' in result) {
|
|
116
|
+
setPasswordState({
|
|
117
|
+
status: 'awaiting-code-input',
|
|
118
|
+
});
|
|
119
|
+
// Return undefined as email verification is required
|
|
120
|
+
return onSuccess({
|
|
121
|
+
hookOptions,
|
|
122
|
+
options,
|
|
123
|
+
data: { requiresEmailVerification: true },
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// Link successful
|
|
128
|
+
setPasswordState({ status: 'done' });
|
|
129
|
+
// Refresh user state to reflect email linking
|
|
130
|
+
await _internal.refreshUserState();
|
|
131
|
+
return onSuccess({
|
|
132
|
+
hookOptions,
|
|
133
|
+
options,
|
|
134
|
+
data: { user: result },
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
const error = new OpenfortError('Failed to link email and password', OpenfortErrorType.AUTHENTICATION_ERROR, { error: e });
|
|
140
|
+
setPasswordState({
|
|
141
|
+
status: 'error',
|
|
142
|
+
error
|
|
143
|
+
});
|
|
144
|
+
return onError({
|
|
145
|
+
hookOptions,
|
|
146
|
+
options,
|
|
147
|
+
error
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}, [client, setPasswordState, _internal, hookOptions]);
|
|
151
|
+
const verifyEmail = () => { }; // TODO
|
|
152
|
+
const resetPassword = () => { }; // TODO
|
|
153
|
+
const requestResetPassword = () => { }; // TODO
|
|
154
|
+
const reset = () => {
|
|
155
|
+
setPasswordState({ status: 'initial' });
|
|
156
|
+
};
|
|
157
|
+
return {
|
|
158
|
+
signInEmail,
|
|
159
|
+
signUpEmail,
|
|
160
|
+
verifyEmail,
|
|
161
|
+
linkEmail,
|
|
162
|
+
requestResetPassword,
|
|
163
|
+
resetPassword,
|
|
164
|
+
reset,
|
|
165
|
+
// ...mapStatus(passwordState),
|
|
166
|
+
// requiresEmailVerification,
|
|
167
|
+
// isAwaitingInput: status.status === 'awaiting-input',
|
|
168
|
+
};
|
|
169
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
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 useGuestAuth = (hookOptions = {}) => {
|
|
7
|
+
const { client, _internal } = useOpenfortContext();
|
|
8
|
+
const { refreshUserState: updateUser } = _internal;
|
|
9
|
+
const [status, setStatus] = useState({
|
|
10
|
+
status: "idle",
|
|
11
|
+
});
|
|
12
|
+
// const { tryUseWallet } = useCreateWalletPostAuth();
|
|
13
|
+
const signUpGuest = useCallback(async (options = {}) => {
|
|
14
|
+
try {
|
|
15
|
+
setStatus({
|
|
16
|
+
status: 'loading',
|
|
17
|
+
});
|
|
18
|
+
const result = await client.auth.signUpGuest();
|
|
19
|
+
const user = result.player;
|
|
20
|
+
await updateUser(user);
|
|
21
|
+
// const { wallet } = await tryUseWallet({
|
|
22
|
+
// logoutOnError: options.logoutOnError || hookOptions.logoutOnError,
|
|
23
|
+
// automaticRecovery: options.automaticRecovery || hookOptions.automaticRecovery,
|
|
24
|
+
// });
|
|
25
|
+
setStatus({
|
|
26
|
+
status: 'success',
|
|
27
|
+
});
|
|
28
|
+
onSuccess({
|
|
29
|
+
hookOptions,
|
|
30
|
+
options,
|
|
31
|
+
data: { user },
|
|
32
|
+
});
|
|
33
|
+
return { user, /* wallet */ };
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const openfortError = new OpenfortError("Failed to signup guest", OpenfortErrorType.AUTHENTICATION_ERROR, { error });
|
|
37
|
+
setStatus({
|
|
38
|
+
status: 'error',
|
|
39
|
+
error: openfortError,
|
|
40
|
+
});
|
|
41
|
+
return onError({
|
|
42
|
+
hookOptions,
|
|
43
|
+
options,
|
|
44
|
+
error: openfortError,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}, [client, setStatus, updateUser, hookOptions]);
|
|
48
|
+
return {
|
|
49
|
+
signUpGuest,
|
|
50
|
+
...mapStatus(status),
|
|
51
|
+
};
|
|
52
|
+
};
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for OAuth-based login functionality
|
|
3
|
+
*/
|
|
4
|
+
import { OAuthProvider } from '@openfort/openfort-js';
|
|
5
|
+
import { useCallback } from 'react';
|
|
6
|
+
import { useOpenfortContext } from '../../core/context';
|
|
7
|
+
import { authenticateWithApple, createOAuthRedirectUri, isAppleSignInAvailable, OAuthUtils, openOAuthSession, parseOAuthUrl, } from '../../native';
|
|
8
|
+
import { mapOAuthStatus } from "../../types/oauth";
|
|
9
|
+
import { OpenfortError, OpenfortErrorType } from '../../types/openfortError';
|
|
10
|
+
import { onError, onSuccess } from '../../lib/hookConsistency';
|
|
11
|
+
/**
|
|
12
|
+
* Hook for OAuth-based authentication with supported providers
|
|
13
|
+
*
|
|
14
|
+
* This hook provides OAuth authentication flow for various providers (Google, Apple, Discord, etc.).
|
|
15
|
+
* It opens the provider's web authentication page and handles the OAuth flow automatically.
|
|
16
|
+
*
|
|
17
|
+
* @param opts - Configuration options including success/error callbacks
|
|
18
|
+
* @returns Object with login function and current OAuth flow state
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* const { login, state } = useLoginWithOAuth({
|
|
23
|
+
* onSuccess: (user) => console.log('OAuth login successful:', user),
|
|
24
|
+
* onError: (error) => console.error('OAuth login failed:', error),
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* // Login with Google
|
|
28
|
+
* const user = await login({ provider: 'google' });
|
|
29
|
+
*
|
|
30
|
+
* // Login with Apple (using legacy web flow on iOS if needed)
|
|
31
|
+
* const user = await login({
|
|
32
|
+
* provider: 'apple',
|
|
33
|
+
* isLegacyAppleIosBehaviorEnabled: true
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // Other supported providers
|
|
37
|
+
* await login({ provider: 'discord' });
|
|
38
|
+
* await login({ provider: 'twitter' });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export const useOAuth = (hookOptions = {}) => {
|
|
42
|
+
const { client, oAuthState, setOAuthState, _internal } = useOpenfortContext();
|
|
43
|
+
const initOAuth = useCallback(async (options) => {
|
|
44
|
+
try {
|
|
45
|
+
setOAuthState({ status: 'loading' });
|
|
46
|
+
// Initialize OAuth flow
|
|
47
|
+
const redirectUri = options.redirectTo || createOAuthRedirectUri('/oauth/callback');
|
|
48
|
+
const result = await client.auth.initOAuth({
|
|
49
|
+
provider: options.provider,
|
|
50
|
+
options: {
|
|
51
|
+
skipBrowserRedirect: true,
|
|
52
|
+
redirectTo: redirectUri,
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
// Handle OAuth flow using native utilities
|
|
56
|
+
setOAuthState({ status: 'awaiting-redirect' });
|
|
57
|
+
// Check if we should use native Apple authentication
|
|
58
|
+
if (options.provider === 'apple' && !options.isLegacyAppleIosBehaviorEnabled) {
|
|
59
|
+
const isAppleAvailable = await isAppleSignInAvailable();
|
|
60
|
+
if (isAppleAvailable) {
|
|
61
|
+
try {
|
|
62
|
+
const appleResult = await authenticateWithApple({
|
|
63
|
+
state: result.key || '',
|
|
64
|
+
isLogin: true,
|
|
65
|
+
});
|
|
66
|
+
// Complete OAuth flow with Apple credentials
|
|
67
|
+
const authResult = await client.auth.loginWithIdToken({
|
|
68
|
+
provider: OAuthProvider.APPLE,
|
|
69
|
+
token: appleResult.identityToken,
|
|
70
|
+
});
|
|
71
|
+
setOAuthState({ status: 'done' });
|
|
72
|
+
const user = authResult.player;
|
|
73
|
+
// Refresh user state in provider
|
|
74
|
+
await _internal.refreshUserState(user);
|
|
75
|
+
return onSuccess({
|
|
76
|
+
options,
|
|
77
|
+
hookOptions,
|
|
78
|
+
data: { user },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
const error = new OpenfortError('Apple authentication failed', OpenfortErrorType.AUTHENTICATION_ERROR, { error: e });
|
|
83
|
+
setOAuthState({
|
|
84
|
+
status: 'error',
|
|
85
|
+
error,
|
|
86
|
+
});
|
|
87
|
+
return onError({
|
|
88
|
+
options,
|
|
89
|
+
hookOptions,
|
|
90
|
+
error,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// For other providers, use web-based OAuth
|
|
96
|
+
const providerUrl = OAuthUtils.getProviderUrl(options.provider, result.url);
|
|
97
|
+
const oauthResult = await OAuthUtils.withTimeout(openOAuthSession({
|
|
98
|
+
url: providerUrl,
|
|
99
|
+
redirectUri,
|
|
100
|
+
}), 120000 // 2 minute timeout
|
|
101
|
+
);
|
|
102
|
+
if (oauthResult.type === 'success' && oauthResult.url) {
|
|
103
|
+
// Parse OAuth response from redirect URL
|
|
104
|
+
const { access_token, refresh_token, player_id, error, errorDescription } = parseOAuthUrl(oauthResult.url);
|
|
105
|
+
if (error) {
|
|
106
|
+
throw new Error(errorDescription || error);
|
|
107
|
+
}
|
|
108
|
+
await client.auth.storeCredentials({
|
|
109
|
+
player: player_id,
|
|
110
|
+
accessToken: access_token,
|
|
111
|
+
refreshToken: refresh_token,
|
|
112
|
+
});
|
|
113
|
+
setOAuthState({ status: 'done' });
|
|
114
|
+
const user = await client.user.get();
|
|
115
|
+
// Refresh user state in provider
|
|
116
|
+
await _internal.refreshUserState(user);
|
|
117
|
+
return onSuccess({
|
|
118
|
+
options,
|
|
119
|
+
hookOptions,
|
|
120
|
+
data: { user },
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
else if (oauthResult.type === 'cancel') {
|
|
124
|
+
const error = new OpenfortError('OAuth authentication was cancelled by user', OpenfortErrorType.AUTHENTICATION_ERROR);
|
|
125
|
+
setOAuthState({
|
|
126
|
+
status: 'error',
|
|
127
|
+
error,
|
|
128
|
+
});
|
|
129
|
+
return onError({
|
|
130
|
+
options,
|
|
131
|
+
hookOptions,
|
|
132
|
+
error,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
const error = new OpenfortError(oauthResult.error || 'OAuth authentication failed', OpenfortErrorType.AUTHENTICATION_ERROR);
|
|
137
|
+
setOAuthState({
|
|
138
|
+
status: 'error',
|
|
139
|
+
error,
|
|
140
|
+
});
|
|
141
|
+
return onError({
|
|
142
|
+
options,
|
|
143
|
+
hookOptions,
|
|
144
|
+
error,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
const error = new OpenfortError('OAuth initialization failed', OpenfortErrorType.AUTHENTICATION_ERROR, { error: e });
|
|
150
|
+
setOAuthState({
|
|
151
|
+
status: 'error',
|
|
152
|
+
error
|
|
153
|
+
});
|
|
154
|
+
return onError({
|
|
155
|
+
options,
|
|
156
|
+
hookOptions,
|
|
157
|
+
error,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}, [client, setOAuthState, _internal]);
|
|
161
|
+
const link = useCallback(async (options) => {
|
|
162
|
+
try {
|
|
163
|
+
setOAuthState({ status: 'loading' });
|
|
164
|
+
// Get current user access token for linking
|
|
165
|
+
const accessToken = await client.getAccessToken();
|
|
166
|
+
if (!accessToken) {
|
|
167
|
+
throw new Error('User must be authenticated to link OAuth account');
|
|
168
|
+
}
|
|
169
|
+
// Initialize OAuth linking flow
|
|
170
|
+
const redirectUri = options.redirectTo || createOAuthRedirectUri('/oauth/callback');
|
|
171
|
+
const result = await client.auth.initLinkOAuth({
|
|
172
|
+
provider: options.provider,
|
|
173
|
+
authToken: accessToken,
|
|
174
|
+
options: {
|
|
175
|
+
skipBrowserRedirect: true,
|
|
176
|
+
redirectTo: redirectUri,
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
// Handle OAuth linking flow using native utilities
|
|
180
|
+
setOAuthState({ status: 'awaiting-redirect' });
|
|
181
|
+
// Check if we should use native Apple authentication for linking
|
|
182
|
+
if (options.provider === 'apple' && !options.isLegacyAppleIosBehaviorEnabled) {
|
|
183
|
+
const isAppleAvailable = await isAppleSignInAvailable();
|
|
184
|
+
if (isAppleAvailable) {
|
|
185
|
+
try {
|
|
186
|
+
const appleResult = await authenticateWithApple({
|
|
187
|
+
state: result.key || '',
|
|
188
|
+
isLogin: false, // This is a linking operation
|
|
189
|
+
});
|
|
190
|
+
// Complete OAuth linking flow with Apple credentials
|
|
191
|
+
const linkResult = await client.auth.loginWithIdToken({
|
|
192
|
+
provider: OAuthProvider.APPLE,
|
|
193
|
+
token: appleResult.identityToken
|
|
194
|
+
});
|
|
195
|
+
setOAuthState({ status: 'done' });
|
|
196
|
+
const user = linkResult.player;
|
|
197
|
+
// Refresh user state to reflect OAuth linking
|
|
198
|
+
await _internal.refreshUserState();
|
|
199
|
+
return onSuccess({
|
|
200
|
+
options,
|
|
201
|
+
hookOptions,
|
|
202
|
+
data: { user },
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch (e) {
|
|
206
|
+
const error = new OpenfortError('Apple linking failed', OpenfortErrorType.AUTHENTICATION_ERROR, { error: e });
|
|
207
|
+
setOAuthState({
|
|
208
|
+
status: 'error',
|
|
209
|
+
error,
|
|
210
|
+
});
|
|
211
|
+
return onError({
|
|
212
|
+
options,
|
|
213
|
+
hookOptions,
|
|
214
|
+
error,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// For other providers, use web-based OAuth
|
|
220
|
+
const providerUrl = OAuthUtils.getProviderUrl(options.provider, result.url);
|
|
221
|
+
const oauthResult = await OAuthUtils.withTimeout(openOAuthSession({
|
|
222
|
+
url: providerUrl,
|
|
223
|
+
redirectUri,
|
|
224
|
+
}), 120000 // 2 minute timeout
|
|
225
|
+
);
|
|
226
|
+
if (oauthResult.type === 'success' && oauthResult.url) {
|
|
227
|
+
// Parse OAuth response from redirect URL
|
|
228
|
+
const { access_token, refresh_token, player_id, error, errorDescription } = parseOAuthUrl(oauthResult.url);
|
|
229
|
+
if (error) {
|
|
230
|
+
throw new Error(errorDescription || error);
|
|
231
|
+
}
|
|
232
|
+
await client.auth.storeCredentials({
|
|
233
|
+
player: player_id,
|
|
234
|
+
accessToken: access_token,
|
|
235
|
+
refreshToken: refresh_token,
|
|
236
|
+
});
|
|
237
|
+
setOAuthState({ status: 'done' });
|
|
238
|
+
const user = await client.user.get();
|
|
239
|
+
// Refresh user state in provider
|
|
240
|
+
await _internal.refreshUserState(user);
|
|
241
|
+
return onSuccess({
|
|
242
|
+
options,
|
|
243
|
+
hookOptions,
|
|
244
|
+
data: { user },
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
else if (oauthResult.type === 'cancel') {
|
|
248
|
+
const error = new OpenfortError('OAuth linking was cancelled by user', OpenfortErrorType.AUTHENTICATION_ERROR);
|
|
249
|
+
setOAuthState({
|
|
250
|
+
status: 'error',
|
|
251
|
+
error,
|
|
252
|
+
});
|
|
253
|
+
return onError({
|
|
254
|
+
options,
|
|
255
|
+
hookOptions,
|
|
256
|
+
error,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
const error = new OpenfortError(oauthResult.error || 'OAuth linking failed', OpenfortErrorType.AUTHENTICATION_ERROR);
|
|
261
|
+
setOAuthState({
|
|
262
|
+
status: 'error',
|
|
263
|
+
error,
|
|
264
|
+
});
|
|
265
|
+
return onError({
|
|
266
|
+
options,
|
|
267
|
+
hookOptions,
|
|
268
|
+
error,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (e) {
|
|
273
|
+
const error = new OpenfortError('OAuth linking failed', OpenfortErrorType.AUTHENTICATION_ERROR, { error: e });
|
|
274
|
+
setOAuthState({
|
|
275
|
+
status: 'error',
|
|
276
|
+
error
|
|
277
|
+
});
|
|
278
|
+
return onError({
|
|
279
|
+
options,
|
|
280
|
+
hookOptions,
|
|
281
|
+
error
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}, [client, setOAuthState, _internal]);
|
|
285
|
+
const storeCredentials = () => { }; // TODO
|
|
286
|
+
return {
|
|
287
|
+
initOAuth,
|
|
288
|
+
link,
|
|
289
|
+
storeCredentials,
|
|
290
|
+
...mapOAuthStatus(oAuthState),
|
|
291
|
+
};
|
|
292
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
import { useOpenfortContext } from "../../core";
|
|
3
|
+
import { onError, onSuccess } from "../../lib/hookConsistency";
|
|
4
|
+
import { mapStatus } from "../../types/baseFlowState";
|
|
5
|
+
import { OpenfortError, OpenfortErrorType } from "../../types/openfortError";
|
|
6
|
+
import { useOpenfortClient } from "../core";
|
|
7
|
+
export function useSignOut(hookOptions = {}) {
|
|
8
|
+
const client = useOpenfortClient();
|
|
9
|
+
const { _internal, user } = useOpenfortContext();
|
|
10
|
+
const [status, setStatus] = useState({
|
|
11
|
+
status: "idle",
|
|
12
|
+
});
|
|
13
|
+
const signOut = useCallback(async (options = {}) => {
|
|
14
|
+
if (!user)
|
|
15
|
+
return;
|
|
16
|
+
setStatus({
|
|
17
|
+
status: 'loading',
|
|
18
|
+
});
|
|
19
|
+
try {
|
|
20
|
+
await client.auth.logout();
|
|
21
|
+
_internal.refreshUserState();
|
|
22
|
+
setStatus({
|
|
23
|
+
status: 'success',
|
|
24
|
+
});
|
|
25
|
+
return onSuccess({
|
|
26
|
+
hookOptions,
|
|
27
|
+
options,
|
|
28
|
+
data: {},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
const error = new OpenfortError('Failed to sign out', OpenfortErrorType.AUTHENTICATION_ERROR, { error: e });
|
|
33
|
+
setStatus({
|
|
34
|
+
status: 'error',
|
|
35
|
+
error,
|
|
36
|
+
});
|
|
37
|
+
return onError({
|
|
38
|
+
hookOptions,
|
|
39
|
+
options,
|
|
40
|
+
error,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}, [client, user, _internal.refreshUserState, setStatus, hookOptions]);
|
|
44
|
+
return {
|
|
45
|
+
...mapStatus(status),
|
|
46
|
+
signOut,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|