@umituz/react-native-auth 3.4.30 → 3.4.32

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.
@@ -0,0 +1,386 @@
1
+ # useAccountManagement
2
+
3
+ Hook for account management operations (logout, delete account).
4
+
5
+ ## Features
6
+
7
+ - Sign out functionality
8
+ - Account deletion with reauthentication
9
+ - Reauthentication callback support
10
+ - Loading state management
11
+
12
+ ## Usage
13
+
14
+ ```typescript
15
+ import { useAccountManagement } from '@umituz/react-native-auth';
16
+
17
+ function AccountSettings() {
18
+ const { logout, deleteAccount, isLoading, isDeletingAccount } = useAccountManagement({
19
+ onReauthRequired: async () => {
20
+ // Show Google/Apple sign-in UI
21
+ const result = await reauthenticateWithGoogle();
22
+ return result.success;
23
+ },
24
+ onPasswordRequired: async () => {
25
+ // Show password prompt
26
+ const password = await showPasswordPrompt();
27
+ return password;
28
+ },
29
+ });
30
+
31
+ return (
32
+ <View>
33
+ <Button onPress={logout}>Sign Out</Button>
34
+ <Button onPress={deleteAccount}>Delete Account</Button>
35
+ </View>
36
+ );
37
+ }
38
+ ```
39
+
40
+ ## API
41
+
42
+ ### Parameters
43
+
44
+ | Param | Type | Required | Description |
45
+ |-------|------|----------|-------------|
46
+ | `onReauthRequired` | `() => Promise<boolean>` | No | Callback for Google/Apple reauthentication |
47
+ | `onPasswordRequired` | `() => Promise<string \| null>` | No | Callback for password reauthentication |
48
+
49
+ ### Return Value
50
+
51
+ | Prop | Type | Description |
52
+ |------|------|-------------|
53
+ | `logout` | `() => Promise<void>` | Sign out function |
54
+ | `deleteAccount` | `() => Promise<void>` | Delete account function |
55
+ | `isLoading` | `boolean` | General loading state |
56
+ | `isDeletingAccount` | `boolean` | Account deletion loading state |
57
+
58
+ ## Examples
59
+
60
+ ### Simple Account Settings
61
+
62
+ ```typescript
63
+ function AccountSettingsScreen() {
64
+ const { logout, deleteAccount, isDeletingAccount } = useAccountManagement();
65
+
66
+ return (
67
+ <ScrollView style={styles.container}>
68
+ <Section title="Session">
69
+ <MenuItem
70
+ title="Sign Out"
71
+ icon="log-out"
72
+ onPress={logout}
73
+ />
74
+ </Section>
75
+
76
+ <Section title="Danger Zone">
77
+ <MenuItem
78
+ title="Delete Account"
79
+ icon="trash"
80
+ onPress={deleteAccount}
81
+ destructive
82
+ disabled={isDeletingAccount}
83
+ />
84
+ {isDeletingAccount && <ActivityIndicator />}
85
+ </Section>
86
+ </ScrollView>
87
+ );
88
+ }
89
+ ```
90
+
91
+ ### With Reauthentication
92
+
93
+ ```typescript
94
+ function AccountSettingsScreen() {
95
+ const { logout, deleteAccount } = useAccountManagement({
96
+ onReauthRequired: async () => {
97
+ try {
98
+ // Reauthenticate with Google
99
+ const result = await signInWithGooglePopup();
100
+
101
+ if (result.user) {
102
+ Alert.alert('Success', 'Please continue with account deletion');
103
+ return true;
104
+ }
105
+
106
+ return false;
107
+ } catch (error) {
108
+ Alert.alert('Error', 'Reauthentication failed');
109
+ return false;
110
+ }
111
+ },
112
+ onPasswordRequired: async () => {
113
+ return new Promise((resolve) => {
114
+ // Show password prompt
115
+ Alert.prompt(
116
+ 'Enter Password',
117
+ 'Please enter your password to delete your account',
118
+ [
119
+ {
120
+ text: 'Cancel',
121
+ onPress: () => resolve(null),
122
+ style: 'cancel',
123
+ },
124
+ {
125
+ text: 'OK',
126
+ onPress: (password) => resolve(password || null),
127
+ },
128
+ ],
129
+ 'secure-text'
130
+ );
131
+ });
132
+ },
133
+ });
134
+
135
+ // ...
136
+ }
137
+ ```
138
+
139
+ ### Custom Reauthentication UI
140
+
141
+ ```typescript
142
+ function DeleteAccountScreen() {
143
+ const [showReauth, setShowReauth] = useState(false);
144
+
145
+ const { deleteAccount, isDeletingAccount } = useAccountManagement({
146
+ onReauthRequired: async () => {
147
+ setShowReauth(true);
148
+ return new Promise((resolve) => {
149
+ // Custom reauthentication UI
150
+ const handleResult = (success: boolean) => {
151
+ setShowReauth(false);
152
+ resolve(success);
153
+ };
154
+
155
+ showCustomReauthUI(handleResult);
156
+ });
157
+ },
158
+ onPasswordRequired: async () => {
159
+ setShowReauth(true);
160
+ return new Promise((resolve) => {
161
+ // Custom password prompt
162
+ showPasswordPrompt((password) => {
163
+ setShowReauth(false);
164
+ resolve(password);
165
+ });
166
+ });
167
+ },
168
+ });
169
+
170
+ const handleDelete = async () => {
171
+ try {
172
+ await deleteAccount();
173
+ Alert.alert('Success', 'Account deleted');
174
+ } catch (error) {
175
+ Alert.alert('Error', error.message);
176
+ }
177
+ };
178
+
179
+ return (
180
+ <View>
181
+ <Button onPress={handleDelete} disabled={isDeletingAccount}>
182
+ Delete Account
183
+ </Button>
184
+
185
+ {showReauth && (
186
+ <ReauthenticationModal
187
+ onComplete={() => {
188
+ // Reauthentication successful, deleteAccount continues
189
+ }}
190
+ />
191
+ )}
192
+ </View>
193
+ );
194
+ }
195
+ ```
196
+
197
+ ### Delete Account Confirmation
198
+
199
+ ```typescript
200
+ function DeleteAccountConfirmation() {
201
+ const { deleteAccount, isDeletingAccount } = useAccountManagement();
202
+ const [agreed, setAgreed] = useState(false);
203
+
204
+ const handleDelete = async () => {
205
+ if (!agreed) {
206
+ Alert.alert('Warning', 'Please accept the terms');
207
+ return;
208
+ }
209
+
210
+ Alert.alert(
211
+ 'Delete Account',
212
+ 'This action cannot be undone. Are you sure?',
213
+ [
214
+ { text: 'Cancel', style: 'cancel' },
215
+ {
216
+ text: 'Delete',
217
+ style: 'destructive',
218
+ onPress: deleteAccount,
219
+ },
220
+ ]
221
+ );
222
+ };
223
+
224
+ return (
225
+ <View>
226
+ <Text style={styles.warning}>
227
+ By deleting your account:
228
+ </Text>
229
+ <Text>• All your data will be permanently deleted</Text>
230
+ <Text>• This action cannot be undone</Text>
231
+ <Text>• You won't be able to sign in with the same account</Text>
232
+
233
+ <CheckBox
234
+ value={agreed}
235
+ onValueChange={setAgreed}
236
+ label="I accept the account deletion terms"
237
+ />
238
+
239
+ <Button
240
+ onPress={handleDelete}
241
+ disabled={!agreed || isDeletingAccount}
242
+ style={{ backgroundColor: 'red' }}
243
+ >
244
+ {isDeletingAccount ? 'Deleting...' : 'Permanently Delete Account'}
245
+ </Button>
246
+ </View>
247
+ );
248
+ }
249
+ ```
250
+
251
+ ### Anonymous User Handling
252
+
253
+ ```typescript
254
+ function AccountActionsAnonymous() {
255
+ const { isAnonymous } = useAuth();
256
+
257
+ if (isAnonymous) {
258
+ return (
259
+ <Button onPress={() => navigation.navigate('Register')}>
260
+ Create Account
261
+ </Button>
262
+ );
263
+ }
264
+
265
+ const config = {
266
+ logoutText: 'Sign Out',
267
+ deleteAccountText: 'Delete Account',
268
+ logoutConfirmTitle: 'Sign Out',
269
+ logoutConfirmMessage: 'Are you sure you want to sign out?',
270
+ deleteConfirmTitle: 'Delete Account',
271
+ deleteConfirmMessage: 'Are you sure you want to delete your account?',
272
+ onLogout: logout,
273
+ onDeleteAccount: deleteAccount,
274
+ };
275
+
276
+ return <AccountActions config={config} />;
277
+ }
278
+ ```
279
+
280
+ ## Error Handling
281
+
282
+ ```typescript
283
+ function AccountSettingsWithErrorHandling() {
284
+ const { logout, deleteAccount } = useAccountManagement();
285
+ const navigation = useNavigation();
286
+
287
+ const handleLogout = async () => {
288
+ try {
289
+ await logout();
290
+ navigation.replace('Login');
291
+ } catch (error) {
292
+ if (error.code === 'auth/network-request-failed') {
293
+ Alert.alert('Connection Error', 'Check your internet connection');
294
+ } else {
295
+ Alert.alert('Error', 'Failed to sign out');
296
+ }
297
+ }
298
+ };
299
+
300
+ const handleDeleteAccount = async () => {
301
+ try {
302
+ await deleteAccount();
303
+ Alert.alert('Success', 'Account deleted');
304
+ navigation.replace('Login');
305
+ } catch (error) {
306
+ if (error.code === 'auth/requires-recent-login') {
307
+ Alert.alert(
308
+ 'Authentication Required',
309
+ 'Please sign in again to delete your account'
310
+ );
311
+ } else if (error.code === 'auth/too-many-requests') {
312
+ Alert.alert(
313
+ 'Too Many Attempts',
314
+ 'Too many failed attempts. Please try again later'
315
+ );
316
+ } else {
317
+ Alert.alert('Error', 'Failed to delete account');
318
+ }
319
+ }
320
+ };
321
+
322
+ return (
323
+ <View>
324
+ <Button onPress={handleLogout}>Sign Out</Button>
325
+ <Button onPress={handleDeleteAccount}>Delete Account</Button>
326
+ </View>
327
+ );
328
+ }
329
+ ```
330
+
331
+ ## Important Notes
332
+
333
+ 1. **Reauthentication Required**: Firebase requires recent sign-in for account deletion
334
+ 2. **Anonymous Users**: Anonymous accounts cannot be deleted
335
+ 3. **Irreversible**: Account deletion is permanent
336
+ 4. **Callbacks**: If `onReauthRequired` and `onPasswordRequired` are not provided, errors will be thrown
337
+
338
+ ## Reauthentication
339
+
340
+ Account deletion is a sensitive operation, so Firebase requires the user to have signed in recently. This hook provides reauthentication callbacks:
341
+
342
+ ### onReauthRequired
343
+
344
+ For Google or Apple sign-in users:
345
+
346
+ ```typescript
347
+ const { deleteAccount } = useAccountManagement({
348
+ onReauthRequired: async () => {
349
+ try {
350
+ // Reauthenticate with Google
351
+ const result = await signInWithGooglePopup();
352
+ return result.user ? true : false;
353
+ } catch (error) {
354
+ return false;
355
+ }
356
+ },
357
+ });
358
+ ```
359
+
360
+ ### onPasswordRequired
361
+
362
+ For email/password users:
363
+
364
+ ```typescript
365
+ const { deleteAccount } = useAccountManagement({
366
+ onPasswordRequired: async () => {
367
+ return new Promise((resolve) => {
368
+ Alert.prompt(
369
+ 'Enter Password',
370
+ 'Please enter your password',
371
+ [
372
+ { text: 'Cancel', onPress: () => resolve(null), style: 'cancel' },
373
+ { text: 'OK', onPress: (password) => resolve(password || null) },
374
+ ],
375
+ 'secure-text'
376
+ );
377
+ });
378
+ },
379
+ });
380
+ ```
381
+
382
+ ## Related Hooks
383
+
384
+ - [`useAuth`](./useAuth.md) - Main auth state management
385
+ - [`useSignOut`](./useAuth.md) - Sign out function
386
+ - [`useUserProfile`](./useUserProfile.md) - Profile information
@@ -0,0 +1,255 @@
1
+ # useAuth
2
+
3
+ Primary authentication hook for managing auth state and operations.
4
+
5
+ ## Features
6
+
7
+ - Centralized Zustand store for auth state
8
+ - Email/password authentication
9
+ - Anonymous mode support
10
+ - Type-safe API
11
+ - Automatic error handling
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { useAuth } from '@umituz/react-native-auth';
17
+
18
+ function MyComponent() {
19
+ const {
20
+ user,
21
+ userId,
22
+ userType,
23
+ loading,
24
+ isAuthReady,
25
+ isAnonymous,
26
+ isAuthenticated,
27
+ error,
28
+ signIn,
29
+ signUp,
30
+ signOut,
31
+ continueAnonymously,
32
+ setError,
33
+ } = useAuth();
34
+
35
+ // Handle loading state
36
+ if (loading) return <LoadingSpinner />;
37
+
38
+ // Redirect to login if not authenticated
39
+ if (!isAuthenticated) {
40
+ return <LoginScreen />;
41
+ }
42
+
43
+ return (
44
+ <View>
45
+ <Text>Welcome, {user?.email}</Text>
46
+ <Button onPress={signOut}>Sign Out</Button>
47
+ </View>
48
+ );
49
+ }
50
+ ```
51
+
52
+ ## API
53
+
54
+ ### Return Value
55
+
56
+ | Prop | Type | Description |
57
+ |------|------|-------------|
58
+ | `user` | `AuthUser \| null` | Current authenticated user |
59
+ | `userId` | `string \| null` | Current user ID (uid) |
60
+ | `userType` | `UserType` | Current user type |
61
+ | `loading` | `boolean` | Whether auth state is loading |
62
+ | `isAuthReady` | `boolean` | Whether auth is ready (initialized and not loading) |
63
+ | `isAnonymous` | `boolean` | Whether user is anonymous |
64
+ | `isAuthenticated` | `boolean` | Whether user is authenticated (not anonymous) |
65
+ | `error` | `string \| null` | Current error message |
66
+ | `signIn` | `(email, password) => Promise<void>` | Sign in function |
67
+ | `signUp` | `(email, password, displayName?) => Promise<void>` | Sign up function |
68
+ | `signOut` | `() => Promise<void>` | Sign out function |
69
+ | `continueAnonymously` | `() => Promise<void>` | Continue anonymously function |
70
+ | `setError` | `(error: string \| null) => void` | Set error manually |
71
+
72
+ ## Examples
73
+
74
+ ### Login Form
75
+
76
+ ```typescript
77
+ function LoginForm() {
78
+ const { signIn, loading, error } = useAuth();
79
+ const [email, setEmail] = useState('');
80
+ const [password, setPassword] = useState('');
81
+
82
+ const handleLogin = async () => {
83
+ try {
84
+ await signIn(email, password);
85
+ // Navigate to home on success
86
+ } catch (err) {
87
+ // Error is automatically set in error state
88
+ }
89
+ };
90
+
91
+ return (
92
+ <View>
93
+ <TextInput
94
+ value={email}
95
+ onChangeText={setEmail}
96
+ placeholder="Email"
97
+ autoCapitalize="none"
98
+ keyboardType="email-address"
99
+ />
100
+ <TextInput
101
+ value={password}
102
+ onChangeText={setPassword}
103
+ placeholder="Password"
104
+ secureTextEntry
105
+ />
106
+ {error && <Text style={{ color: 'red' }}>{error}</Text>}
107
+ <Button onPress={handleLogin} disabled={loading}>
108
+ {loading ? 'Signing in...' : 'Sign In'}
109
+ </Button>
110
+ </View>
111
+ );
112
+ }
113
+ ```
114
+
115
+ ### Registration Form
116
+
117
+ ```typescript
118
+ function RegisterForm() {
119
+ const { signUp, loading, error } = useAuth();
120
+ const [email, setEmail] = useState('');
121
+ const [password, setPassword] = useState('');
122
+ const [displayName, setDisplayName] = useState('');
123
+
124
+ const handleRegister = async () => {
125
+ try {
126
+ await signUp(email, password, displayName);
127
+ // Success - user is automatically signed in
128
+ } catch (err) {
129
+ // Error handling
130
+ }
131
+ };
132
+
133
+ return (
134
+ <View>
135
+ <TextInput
136
+ value={displayName}
137
+ onChangeText={setDisplayName}
138
+ placeholder="Full Name"
139
+ />
140
+ <TextInput
141
+ value={email}
142
+ onChangeText={setEmail}
143
+ placeholder="Email"
144
+ keyboardType="email-address"
145
+ />
146
+ <TextInput
147
+ value={password}
148
+ onChangeText={setPassword}
149
+ placeholder="Password"
150
+ secureTextEntry
151
+ />
152
+ {error && <Text style={{ color: 'red' }}>{error}</Text>}
153
+ <Button onPress={handleRegister} disabled={loading}>
154
+ {loading ? 'Creating account...' : 'Sign Up'}
155
+ </Button>
156
+ </View>
157
+ );
158
+ }
159
+ ```
160
+
161
+ ### Anonymous Mode
162
+
163
+ ```typescript
164
+ function AnonymousPrompt() {
165
+ const { continueAnonymously, loading } = useAuth();
166
+
167
+ return (
168
+ <View>
169
+ <Text>Continue without an account?</Text>
170
+ <Button onPress={continueAnonymously} disabled={loading}>
171
+ {loading ? 'Starting...' : 'Continue as Guest'}
172
+ </Button>
173
+ </View>
174
+ );
175
+ }
176
+ ```
177
+
178
+ ### Auth State Checking
179
+
180
+ ```typescript
181
+ function ProtectedContent() {
182
+ const { isAuthenticated, isAuthReady, user } = useAuth();
183
+
184
+ // Show loading while auth initializes
185
+ if (!isAuthReady) {
186
+ return <LoadingScreen />;
187
+ }
188
+
189
+ // Redirect if not authenticated
190
+ if (!isAuthenticated) {
191
+ return <LoginScreen />;
192
+ }
193
+
194
+ // Show protected content
195
+ return (
196
+ <View>
197
+ <Text>Welcome, {user?.email}</Text>
198
+ <ProtectedContent />
199
+ </View>
200
+ );
201
+ }
202
+ ```
203
+
204
+ ### Sign Out
205
+
206
+ ```typescript
207
+ function ProfileScreen() {
208
+ const { user, signOut } = useAuth();
209
+ const navigation = useNavigation();
210
+
211
+ const handleSignOut = async () => {
212
+ Alert.alert(
213
+ 'Sign Out',
214
+ 'Are you sure you want to sign out?',
215
+ [
216
+ { text: 'Cancel', style: 'cancel' },
217
+ {
218
+ text: 'Sign Out',
219
+ onPress: async () => {
220
+ try {
221
+ await signOut();
222
+ navigation.reset({
223
+ index: 0,
224
+ routes: [{ name: 'Login' }],
225
+ });
226
+ } catch (error) {
227
+ Alert.alert('Error', 'Failed to sign out');
228
+ }
229
+ },
230
+ },
231
+ ]
232
+ );
233
+ };
234
+
235
+ return (
236
+ <View>
237
+ <Text>{user?.email}</Text>
238
+ <Button onPress={handleSignOut}>Sign Out</Button>
239
+ </View>
240
+ );
241
+ }
242
+ ```
243
+
244
+ ## Important Notes
245
+
246
+ 1. **initializeAuthListener()**: Must be called once in app root
247
+ 2. **Centralized State**: All components share the same state via Zustand
248
+ 3. **Error Handling**: Errors are automatically set in the `error` state
249
+ 4. **Loading States**: `loading` is true during operations
250
+
251
+ ## Related Hooks
252
+
253
+ - [`useAuthRequired`](./useAuthRequired.md) - For components requiring auth
254
+ - [`useRequireAuth`](./useAuthRequired.md#userequireauth) - Route protection
255
+ - [`useUserProfile`](./useUserProfile.md) - User profile data