@umituz/react-native-firebase 1.13.47 → 1.13.49

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-firebase",
3
- "version": "1.13.47",
3
+ "version": "1.13.49",
4
4
  "description": "Unified Firebase package for React Native apps - Auth and Firestore services using Firebase JS SDK (no native modules).",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -1,16 +1,5 @@
1
1
  /**
2
2
  * Firebase Auth Client - Infrastructure Layer
3
- *
4
- * Domain-Driven Design: Infrastructure implementation of Firebase Auth client
5
- * Singleton pattern for managing Firebase Auth instance
6
- *
7
- * IMPORTANT: This package requires Firebase App to be initialized first.
8
- * Use @umituz/react-native-firebase to initialize Firebase App.
9
- *
10
- * SOLID Principles:
11
- * - Single Responsibility: Only manages Auth initialization
12
- * - Open/Closed: Extensible through configuration, closed for modification
13
- * - Dependency Inversion: Depends on Firebase App from @umituz/react-native-firebase
14
3
  */
15
4
 
16
5
  import type { Auth } from 'firebase/auth';
@@ -20,190 +9,46 @@ import type { FirebaseAuthConfig } from '../../domain/value-objects/FirebaseAuth
20
9
 
21
10
  declare const __DEV__: boolean;
22
11
 
23
- /**
24
- * Firebase Auth Client Singleton
25
- * Manages Firebase Auth initialization
26
- */
27
12
  class FirebaseAuthClientSingleton {
28
13
  private static instance: FirebaseAuthClientSingleton | null = null;
29
14
  private auth: Auth | null = null;
30
15
  private initializationError: string | null = null;
31
16
 
32
- private constructor() {
33
- // Private constructor to enforce singleton pattern
34
- }
35
-
36
- /**
37
- * Get singleton instance
38
- */
39
17
  static getInstance(): FirebaseAuthClientSingleton {
40
- if (!FirebaseAuthClientSingleton.instance) {
41
- FirebaseAuthClientSingleton.instance = new FirebaseAuthClientSingleton();
42
- }
43
- return FirebaseAuthClientSingleton.instance;
18
+ if (!this.instance) this.instance = new FirebaseAuthClientSingleton();
19
+ return this.instance;
44
20
  }
45
21
 
46
- /**
47
- * Initialize Firebase Auth
48
- * Requires Firebase App to be initialized first via @umituz/react-native-firebase
49
- *
50
- * @param config - Optional Firebase Auth configuration
51
- * @returns Firebase Auth instance or null if initialization fails
52
- */
53
22
  initialize(config?: FirebaseAuthConfig): Auth | null {
54
- // Return existing instance if already initialized
55
- if (this.auth) {
56
- if (__DEV__) {
57
- console.log('[FirebaseAuth] Already initialized, returning existing instance');
58
- }
59
- return this.auth;
60
- }
61
-
62
- // Don't retry if initialization already failed
63
- if (this.initializationError) {
64
- if (__DEV__) {
65
- console.warn('[FirebaseAuth] Previous initialization failed:', this.initializationError);
66
- }
67
- return null;
68
- }
23
+ if (this.auth) return this.auth;
24
+ if (this.initializationError) return null;
69
25
 
70
26
  try {
71
- // Get Firebase App instance (must be initialized first)
72
27
  const app = getFirebaseApp();
73
-
74
- // Return null if Firebase App is not available (offline mode)
75
- if (!app) {
76
- if (__DEV__) {
77
- console.log('[FirebaseAuth] Firebase App not available - skipping Auth initialization');
78
- }
79
- // Don't set initializationError - this is normal offline mode
80
- return null;
81
- }
82
-
83
- if (__DEV__) {
84
- console.log('[FirebaseAuth] Initializing Firebase Auth...');
85
- }
86
-
87
- // Initialize Auth
28
+ if (!app) return null;
88
29
  this.auth = FirebaseAuthInitializer.initialize(app, config);
89
-
90
- if (this.auth) {
91
- if (__DEV__) {
92
- console.log('[FirebaseAuth] ✅ Successfully initialized');
93
- }
94
- } else {
95
- if (__DEV__) {
96
- console.warn('[FirebaseAuth] ⚠️ Initialization returned null');
97
- }
98
- }
99
-
100
30
  return this.auth;
101
- } catch (error) {
102
- // Only set error if it's a real initialization failure, not just missing config
103
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
104
- if (__DEV__) {
105
- console.error('[FirebaseAuth] ❌ Initialization error:', errorMessage);
106
- }
107
- if (!errorMessage.includes('not initialized') && !errorMessage.includes('not available')) {
108
- this.initializationError = errorMessage;
109
- }
31
+ } catch (error: any) {
32
+ if (__DEV__) console.error('[FirebaseAuth] Init error:', error.message);
33
+ this.initializationError = error.message;
110
34
  return null;
111
35
  }
112
36
  }
113
37
 
114
- /**
115
- * Get the Firebase Auth instance
116
- * Auto-initializes if Firebase App is available
117
- * Returns null if config is not available (offline mode - no error)
118
- * @returns Firebase Auth instance or null if not initialized
119
- */
120
38
  getAuth(): Auth | null {
121
- // Auto-initialize if not already initialized
122
- if (!this.auth && !this.initializationError) {
123
- try {
124
- // Try to get Firebase App (will auto-initialize if config is available)
125
- const app = getFirebaseApp();
126
- if (app) {
127
- this.initialize();
128
- }
129
- } catch {
130
- // Firebase App not available, return null (offline mode)
131
- return null;
132
- }
133
- }
134
-
135
- // Return null if not initialized (offline mode - no error)
136
- return this.auth || null;
39
+ if (!this.auth && !this.initializationError && getFirebaseApp()) this.initialize();
40
+ return this.auth;
137
41
  }
138
42
 
139
- /**
140
- * Check if Auth is initialized
141
- */
142
- isInitialized(): boolean {
143
- return this.auth !== null;
144
- }
145
-
146
- /**
147
- * Get initialization error if any
148
- */
149
- getInitializationError(): string | null {
150
- return this.initializationError;
151
- }
152
-
153
- /**
154
- * Reset the Auth client instance
155
- * Useful for testing
156
- */
157
- reset(): void {
158
- this.auth = null;
159
- this.initializationError = null;
160
- }
43
+ reset(): void { this.auth = null; this.initializationError = null; }
161
44
  }
162
45
 
163
- /**
164
- * Singleton instance
165
- */
166
46
  export const firebaseAuthClient = FirebaseAuthClientSingleton.getInstance();
47
+ export const initializeFirebaseAuth = (c?: FirebaseAuthConfig) => firebaseAuthClient.initialize(c);
48
+ export const getFirebaseAuth = () => firebaseAuthClient.getAuth();
49
+ export const isFirebaseAuthInitialized = () => firebaseAuthClient.getAuth() !== null;
50
+ export const getFirebaseAuthInitializationError = () => firebaseAuthClient.initialize() ? null : "Not initialized";
51
+ export const resetFirebaseAuthClient = () => firebaseAuthClient.reset();
167
52
 
168
- export function initializeFirebaseAuth(
169
- config?: FirebaseAuthConfig
170
- ): Auth | null {
171
- return firebaseAuthClient.initialize(config);
172
- }
173
-
174
- /**
175
- * Get Firebase Auth instance
176
- * Auto-initializes if Firebase App is available
177
- * Returns null if config is not available (offline mode - no error)
178
- * @returns Firebase Auth instance or null if not initialized
179
- */
180
- export function getFirebaseAuth(): Auth | null {
181
- return firebaseAuthClient.getAuth();
182
- }
183
-
184
- /**
185
- * Check if Firebase Auth is initialized
186
- */
187
- export function isFirebaseAuthInitialized(): boolean {
188
- return firebaseAuthClient.isInitialized();
189
- }
190
-
191
- /**
192
- * Get initialization error if any
193
- */
194
- export function getFirebaseAuthInitializationError(): string | null {
195
- return firebaseAuthClient.getInitializationError();
196
- }
197
-
198
- /**
199
- * Reset Firebase Auth client instance
200
- * Useful for testing
201
- */
202
- export function resetFirebaseAuthClient(): void {
203
- firebaseAuthClient.reset();
204
- }
205
-
206
- // Export types
207
53
  export type { Auth } from 'firebase/auth';
208
54
  export type { FirebaseAuthConfig } from '../../domain/value-objects/FirebaseAuthConfig';
209
-
@@ -1,8 +1,5 @@
1
1
  /**
2
2
  * Account Deletion Service
3
- * Handles Firebase Auth account deletion operations with automatic reauthentication
4
- *
5
- * SOLID: Single Responsibility - Only handles account deletion
6
3
  */
7
4
 
8
5
  import { deleteUser, type User } from "firebase/auth";
@@ -11,381 +8,74 @@ import {
11
8
  getUserAuthProvider,
12
9
  reauthenticateWithApple,
13
10
  reauthenticateWithPassword,
11
+ reauthenticateWithGoogle,
14
12
  } from "./reauthentication.service";
13
+ import type { AccountDeletionResult, AccountDeletionOptions } from "./reauthentication.types";
15
14
 
16
- export interface AccountDeletionResult {
17
- success: boolean;
18
- error?: {
19
- code: string;
20
- message: string;
21
- requiresReauth: boolean;
22
- };
23
- }
15
+ export type { AccountDeletionResult, AccountDeletionOptions } from "./reauthentication.types";
24
16
 
25
- export interface AccountDeletionOptions {
26
- /**
27
- * Google ID token for reauthentication (required if user signed in with Google)
28
- * This must be provided by the calling code after prompting user for Google sign-in
29
- */
30
- googleIdToken?: string;
31
- /**
32
- * Password for reauthentication (required if user signed in with email/password)
33
- * This must be provided by the calling code after prompting user for password
34
- */
35
- password?: string;
36
- /**
37
- * If true, will attempt to reauthenticate with Apple automatically
38
- * (shows Apple sign-in prompt to user)
39
- */
40
- autoReauthenticate?: boolean;
41
- }
17
+ declare const __DEV__: boolean;
42
18
 
43
- /**
44
- * Delete the current user's Firebase Auth account
45
- * Now with automatic reauthentication support for Apple Sign-In
46
- * Note: This is irreversible and also signs out the user
47
- */
48
19
  export async function deleteCurrentUser(
49
20
  options: AccountDeletionOptions = { autoReauthenticate: true }
50
21
  ): Promise<AccountDeletionResult> {
51
- if (__DEV__) {
52
- console.log("[deleteCurrentUser] Starting with options:", options);
53
- }
54
-
55
22
  const auth = getFirebaseAuth();
23
+ const user = auth?.currentUser;
56
24
 
57
- if (!auth) {
58
- if (__DEV__) {
59
- console.log("[deleteCurrentUser] Firebase Auth not initialized");
60
- }
61
- return {
62
- success: false,
63
- error: {
64
- code: "auth/not-initialized",
65
- message: "Firebase Auth is not initialized",
66
- requiresReauth: false,
67
- },
68
- };
69
- }
70
-
71
- const user = auth.currentUser;
72
-
73
- if (!user) {
74
- if (__DEV__) {
75
- console.log("[deleteCurrentUser] ❌ No user signed in");
76
- }
77
- return {
78
- success: false,
79
- error: {
80
- code: "auth/no-user",
81
- message: "No user is currently signed in",
82
- requiresReauth: false,
83
- },
84
- };
85
- }
86
-
87
- if (user.isAnonymous) {
88
- if (__DEV__) {
89
- console.log("[deleteCurrentUser] ❌ Cannot delete anonymous user");
90
- }
91
- return {
92
- success: false,
93
- error: {
94
- code: "auth/anonymous-user",
95
- message: "Cannot delete anonymous account",
96
- requiresReauth: false,
97
- },
98
- };
99
- }
100
-
101
- if (__DEV__) {
102
- const provider = getUserAuthProvider(user);
103
- console.log("[deleteCurrentUser] User info:", {
104
- uid: user.uid,
105
- provider,
106
- providerData: user.providerData?.map(p => p.providerId),
107
- });
108
- }
25
+ if (!auth || !user) return {
26
+ success: false,
27
+ error: { code: "auth/not-ready", message: "Auth not ready", requiresReauth: false }
28
+ };
29
+ if (user.isAnonymous) return {
30
+ success: false,
31
+ error: { code: "auth/anonymous", message: "Cannot delete anonymous", requiresReauth: false }
32
+ };
109
33
 
110
- // First attempt to delete
111
34
  try {
112
- if (__DEV__) {
113
- console.log("[deleteCurrentUser] Attempting to delete user...");
114
- }
115
35
  await deleteUser(user);
116
- if (__DEV__) {
117
- console.log("[deleteCurrentUser] ✅ User deleted successfully");
118
- }
119
36
  return { success: true };
120
- } catch (error) {
121
- const firebaseError = error as { code?: string; message?: string };
122
- const requiresReauth = firebaseError.code === "auth/requires-recent-login";
123
-
124
- if (__DEV__) {
125
- console.log("[deleteCurrentUser] First delete attempt failed:", {
126
- code: firebaseError.code,
127
- message: firebaseError.message,
128
- requiresReauth,
129
- });
130
- }
131
-
132
- // If reauthentication is required and autoReauthenticate is enabled OR password is provided
133
- if (requiresReauth && (options.autoReauthenticate || options.password)) {
134
- if (__DEV__) {
135
- console.log("[deleteCurrentUser] Attempting auto-reauthentication...");
136
- }
137
- const reauthResult = await attemptReauthenticationAndDelete(user, options);
138
- if (reauthResult) {
139
- if (__DEV__) {
140
- console.log("[deleteCurrentUser] Auto-reauth result:", reauthResult);
141
- }
142
- return reauthResult;
143
- }
37
+ } catch (error: any) {
38
+ if (error.code === "auth/requires-recent-login" && (options.autoReauthenticate || options.password || options.googleIdToken)) {
39
+ const reauth = await attemptReauth(user, options);
40
+ if (reauth) return reauth;
144
41
  }
145
-
146
42
  return {
147
43
  success: false,
148
- error: {
149
- code: firebaseError.code || "auth/unknown",
150
- message: requiresReauth
151
- ? "Please sign in again before deleting your account"
152
- : firebaseError.message || "Failed to delete account",
153
- requiresReauth,
154
- },
44
+ error: { code: error.code || "auth/failed", message: error.message, requiresReauth: error.code === "auth/requires-recent-login" }
155
45
  };
156
46
  }
157
47
  }
158
48
 
159
- /**
160
- * Attempt to reauthenticate based on provider and then delete the account
161
- */
162
- async function attemptReauthenticationAndDelete(
163
- user: User,
164
- options: AccountDeletionOptions
165
- ): Promise<AccountDeletionResult | null> {
49
+ async function attemptReauth(user: User, options: AccountDeletionOptions): Promise<AccountDeletionResult | null> {
166
50
  const provider = getUserAuthProvider(user);
167
-
168
- if (__DEV__) {
169
- console.log("[attemptReauthenticationAndDelete] Provider:", provider);
170
- }
171
-
172
- // Handle Apple reauthentication
173
- if (provider === "apple.com") {
174
- if (__DEV__) {
175
- console.log("[attemptReauthenticationAndDelete] Attempting Apple reauthentication...");
176
- }
177
-
178
- const reauthResult = await reauthenticateWithApple(user);
179
-
180
- if (__DEV__) {
181
- console.log("[attemptReauthenticationAndDelete] Apple reauth result:", reauthResult);
182
- }
183
-
184
- if (reauthResult.success) {
185
- // Retry deletion after successful reauthentication
186
- try {
187
- if (__DEV__) {
188
- console.log("[attemptReauthenticationAndDelete] Deleting user after Apple reauth...");
189
- }
190
- await deleteUser(user);
191
- if (__DEV__) {
192
- console.log("[attemptReauthenticationAndDelete] ✅ User deleted after Apple reauth");
193
- }
194
- return { success: true };
195
- } catch (deleteError) {
196
- const firebaseError = deleteError as { code?: string; message?: string };
197
- if (__DEV__) {
198
- console.log("[attemptReauthenticationAndDelete] ❌ Delete failed after Apple reauth:", firebaseError);
199
- }
200
- return {
201
- success: false,
202
- error: {
203
- code: firebaseError.code || "auth/deletion-failed-after-reauth",
204
- message: firebaseError.message || "Failed to delete account after reauthentication",
205
- requiresReauth: false,
206
- },
207
- };
208
- }
209
- } else {
210
- // Reauthentication failed
211
- if (__DEV__) {
212
- console.log("[attemptReauthenticationAndDelete] ❌ Apple reauth failed");
213
- }
214
- return {
215
- success: false,
216
- error: {
217
- code: reauthResult.error?.code || "auth/reauthentication-failed",
218
- message: reauthResult.error?.message || "Reauthentication failed",
219
- requiresReauth: true,
220
- },
221
- };
222
- }
223
- }
224
-
225
- // Handle Google reauthentication (requires ID token from caller)
226
- if (provider === "google.com") {
227
- // For Google, we need the caller to provide the ID token
228
- // This is because we need to trigger Google Sign-In UI which must be done at the presentation layer
229
- if (!options.googleIdToken) {
230
- return {
231
- success: false,
232
- error: {
233
- code: "auth/google-reauth-required",
234
- message: "Please sign in with Google again to delete your account",
235
- requiresReauth: true,
236
- },
237
- };
238
- }
239
-
240
- // If we have a Google ID token, reauthenticate with it
241
- const { reauthenticateWithGoogle } = await import("./reauthentication.service");
242
- const reauthResult = await reauthenticateWithGoogle(user, options.googleIdToken);
243
-
244
- if (reauthResult.success) {
245
- try {
246
- await deleteUser(user);
247
- return { success: true };
248
- } catch (deleteError) {
249
- const firebaseError = deleteError as { code?: string; message?: string };
250
- return {
251
- success: false,
252
- error: {
253
- code: firebaseError.code || "auth/deletion-failed-after-reauth",
254
- message: firebaseError.message || "Failed to delete account after reauthentication",
255
- requiresReauth: false,
256
- },
257
- };
258
- }
259
- } else {
260
- return {
261
- success: false,
262
- error: {
263
- code: reauthResult.error?.code || "auth/reauthentication-failed",
264
- message: reauthResult.error?.message || "Google reauthentication failed",
265
- requiresReauth: true,
266
- },
267
- };
268
- }
269
- }
270
-
271
- // Handle Password reauthentication (requires password from caller)
272
- if (provider === "password") {
273
- if (__DEV__) {
274
- console.log("[attemptReauthenticationAndDelete] Password provider detected");
275
- }
276
-
277
- // For password, we need the caller to provide the password
278
- if (!options.password) {
279
- if (__DEV__) {
280
- console.log("[attemptReauthenticationAndDelete] No password provided, requesting reauth");
281
- }
282
- return {
283
- success: false,
284
- error: {
285
- code: "auth/password-reauth-required",
286
- message: "Please enter your password to delete your account",
287
- requiresReauth: true,
288
- },
289
- };
290
- }
291
-
292
- if (__DEV__) {
293
- console.log("[attemptReauthenticationAndDelete] Attempting password reauthentication...");
294
- }
295
-
296
- const reauthResult = await reauthenticateWithPassword(user, options.password);
297
-
298
- if (__DEV__) {
299
- console.log("[attemptReauthenticationAndDelete] Password reauth result:", reauthResult);
300
- }
301
-
302
- if (reauthResult.success) {
303
- try {
304
- if (__DEV__) {
305
- console.log("[attemptReauthenticationAndDelete] Deleting user after password reauth...");
306
- }
307
- await deleteUser(user);
308
- if (__DEV__) {
309
- console.log("[attemptReauthenticationAndDelete] ✅ User deleted after password reauth");
310
- }
311
- return { success: true };
312
- } catch (deleteError) {
313
- const firebaseError = deleteError as { code?: string; message?: string };
314
- if (__DEV__) {
315
- console.log("[attemptReauthenticationAndDelete] ❌ Delete failed after password reauth:", firebaseError);
316
- }
317
- return {
318
- success: false,
319
- error: {
320
- code: firebaseError.code || "auth/deletion-failed-after-reauth",
321
- message: firebaseError.message || "Failed to delete account after reauthentication",
322
- requiresReauth: false,
323
- },
324
- };
325
- }
326
- } else {
327
- if (__DEV__) {
328
- console.log("[attemptReauthenticationAndDelete] ❌ Password reauth failed");
329
- }
330
- return {
331
- success: false,
332
- error: {
333
- code: reauthResult.error?.code || "auth/reauthentication-failed",
334
- message: reauthResult.error?.message || "Password reauthentication failed",
335
- requiresReauth: true,
336
- },
337
- };
51
+ let res: { success: boolean, error?: any };
52
+
53
+ if (provider === "apple.com") res = await reauthenticateWithApple(user);
54
+ else if (provider === "google.com") {
55
+ if (!options.googleIdToken) return { success: false, error: { code: "auth/google-reauth", message: "Google reauth required", requiresReauth: true } };
56
+ res = await reauthenticateWithGoogle(user, options.googleIdToken);
57
+ } else if (provider === "password") {
58
+ if (!options.password) return { success: false, error: { code: "auth/password-reauth", message: "Password required", requiresReauth: true } };
59
+ res = await reauthenticateWithPassword(user, options.password);
60
+ } else return null;
61
+
62
+ if (res.success) {
63
+ try {
64
+ await deleteUser(user);
65
+ return { success: true };
66
+ } catch (err: any) {
67
+ return { success: false, error: { code: err.code || "auth/post-reauth-failed", message: err.message, requiresReauth: false } };
338
68
  }
339
69
  }
340
-
341
- // For other providers, return null to use the default error handling
342
- return null;
70
+ return { success: false, error: { code: res.error?.code || "auth/reauth-failed", message: res.error?.message, requiresReauth: true } };
343
71
  }
344
72
 
345
- /**
346
- * Delete a specific user (must be the current user)
347
- */
348
- export async function deleteUserAccount(
349
- user: User
350
- ): Promise<AccountDeletionResult> {
351
- if (!user) {
352
- return {
353
- success: false,
354
- error: {
355
- code: "auth/no-user",
356
- message: "No user provided",
357
- requiresReauth: false,
358
- },
359
- };
360
- }
361
-
362
- if (user.isAnonymous) {
363
- return {
364
- success: false,
365
- error: {
366
- code: "auth/anonymous-user",
367
- message: "Cannot delete anonymous account",
368
- requiresReauth: false,
369
- },
370
- };
371
- }
372
-
73
+ export async function deleteUserAccount(user: User): Promise<AccountDeletionResult> {
74
+ if (!user || user.isAnonymous) return { success: false, error: { code: "auth/invalid", message: "Invalid user", requiresReauth: false } };
373
75
  try {
374
76
  await deleteUser(user);
375
77
  return { success: true };
376
- } catch (error) {
377
- const firebaseError = error as { code?: string; message?: string };
378
- const requiresReauth = firebaseError.code === "auth/requires-recent-login";
379
-
380
- return {
381
- success: false,
382
- error: {
383
- code: firebaseError.code || "auth/unknown",
384
- message: requiresReauth
385
- ? "Please sign in again before deleting your account"
386
- : firebaseError.message || "Failed to delete account",
387
- requiresReauth,
388
- },
389
- };
78
+ } catch (error: any) {
79
+ return { success: false, error: { code: error.code || "auth/failed", message: error.message, requiresReauth: error.code === "auth/requires-recent-login" } };
390
80
  }
391
81
  }
@@ -1,8 +1,6 @@
1
1
  /**
2
2
  * Reauthentication Service
3
3
  * Handles Firebase Auth reauthentication for sensitive operations
4
- *
5
- * SOLID: Single Responsibility - Only handles reauthentication
6
4
  */
7
5
 
8
6
  import {
@@ -11,243 +9,85 @@ import {
11
9
  OAuthProvider,
12
10
  EmailAuthProvider,
13
11
  type User,
14
- type AuthCredential,
15
12
  } from "firebase/auth";
16
13
  import * as AppleAuthentication from "expo-apple-authentication";
17
14
  import * as Crypto from "expo-crypto";
18
15
  import { Platform } from "react-native";
16
+ import type {
17
+ ReauthenticationResult,
18
+ AuthProviderType,
19
+ ReauthCredentialResult
20
+ } from "./reauthentication.types";
21
+
22
+ export type {
23
+ ReauthenticationResult,
24
+ AuthProviderType,
25
+ ReauthCredentialResult
26
+ } from "./reauthentication.types";
19
27
 
20
- export interface ReauthenticationResult {
21
- success: boolean;
22
- error?: {
23
- code: string;
24
- message: string;
25
- };
26
- }
27
-
28
- export type AuthProviderType = "google.com" | "apple.com" | "password" | "anonymous" | "unknown";
29
-
30
- /**
31
- * Get the primary auth provider for a user
32
- */
33
28
  export function getUserAuthProvider(user: User): AuthProviderType {
34
- if (user.isAnonymous) {
35
- return "anonymous";
36
- }
37
-
38
- const providerData = user.providerData;
39
- if (!providerData || providerData.length === 0) {
40
- return "unknown";
41
- }
42
-
43
- // Check for Google
44
- const googleProvider = providerData.find((p) => p.providerId === "google.com");
45
- if (googleProvider) {
46
- return "google.com";
47
- }
48
-
49
- // Check for Apple
50
- const appleProvider = providerData.find((p) => p.providerId === "apple.com");
51
- if (appleProvider) {
52
- return "apple.com";
53
- }
54
-
55
- // Check for password
56
- const passwordProvider = providerData.find((p) => p.providerId === "password");
57
- if (passwordProvider) {
58
- return "password";
59
- }
60
-
29
+ if (user.isAnonymous) return "anonymous";
30
+ const data = user.providerData;
31
+ if (!data?.length) return "unknown";
32
+ if (data.find(p => p.providerId === "google.com")) return "google.com";
33
+ if (data.find(p => p.providerId === "apple.com")) return "apple.com";
34
+ if (data.find(p => p.providerId === "password")) return "password";
61
35
  return "unknown";
62
36
  }
63
37
 
64
- /**
65
- * Reauthenticate with Google
66
- */
67
- export async function reauthenticateWithGoogle(
68
- user: User,
69
- idToken: string
70
- ): Promise<ReauthenticationResult> {
38
+ export async function reauthenticateWithGoogle(user: User, idToken: string): Promise<ReauthenticationResult> {
71
39
  try {
72
- const credential = GoogleAuthProvider.credential(idToken);
73
- await reauthenticateWithCredential(user, credential);
40
+ await reauthenticateWithCredential(user, GoogleAuthProvider.credential(idToken));
74
41
  return { success: true };
75
- } catch (error) {
76
- const firebaseError = error as { code?: string; message?: string };
77
- return {
78
- success: false,
79
- error: {
80
- code: firebaseError.code || "auth/reauthentication-failed",
81
- message: firebaseError.message || "Google reauthentication failed",
82
- },
83
- };
42
+ } catch (error: any) {
43
+ return { success: false, error: { code: error.code || "auth/failed", message: error.message } };
84
44
  }
85
45
  }
86
46
 
87
- /**
88
- * Reauthenticate with Email/Password
89
- */
90
- export async function reauthenticateWithPassword(
91
- user: User,
92
- password: string
93
- ): Promise<ReauthenticationResult> {
47
+ export async function reauthenticateWithPassword(user: User, pass: string): Promise<ReauthenticationResult> {
48
+ if (!user.email) return { success: false, error: { code: "auth/no-email", message: "User has no email" } };
94
49
  try {
95
- if (!user.email) {
96
- return {
97
- success: false,
98
- error: {
99
- code: "auth/no-email",
100
- message: "User has no email address",
101
- },
102
- };
103
- }
104
-
105
- const credential = EmailAuthProvider.credential(user.email, password);
106
- await reauthenticateWithCredential(user, credential);
50
+ await reauthenticateWithCredential(user, EmailAuthProvider.credential(user.email, pass));
107
51
  return { success: true };
108
- } catch (error) {
109
- const firebaseError = error as { code?: string; message?: string };
110
- return {
111
- success: false,
112
- error: {
113
- code: firebaseError.code || "auth/reauthentication-failed",
114
- message: firebaseError.message || "Password reauthentication failed",
115
- },
116
- };
52
+ } catch (error: any) {
53
+ return { success: false, error: { code: error.code || "auth/failed", message: error.message } };
117
54
  }
118
55
  }
119
56
 
120
- /**
121
- * Generate a random nonce for Apple Sign-In security
122
- */
123
- async function generateNonce(length: number = 32): Promise<string> {
124
- const randomBytes = await Crypto.getRandomBytesAsync(length);
57
+ async function generateNonce(len: number = 32): Promise<string> {
58
+ const bytes = await Crypto.getRandomBytesAsync(len);
125
59
  const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
126
- let result = "";
127
-
128
- for (let i = 0; i < randomBytes.length; i++) {
129
- const byte = randomBytes[i];
130
- if (byte !== undefined) {
131
- result += chars.charAt(byte % chars.length);
132
- }
133
- }
134
-
135
- return result;
60
+ return Array.from(bytes).map(b => chars.charAt(b % chars.length)).join("");
136
61
  }
137
62
 
138
- /**
139
- * Reauthenticate with Apple
140
- * Returns the credential that can be used for reauthentication
141
- */
142
- export async function getAppleReauthCredential(): Promise<{
143
- success: boolean;
144
- credential?: AuthCredential;
145
- error?: { code: string; message: string };
146
- }> {
147
- if (Platform.OS !== "ios") {
148
- return {
149
- success: false,
150
- error: {
151
- code: "auth/platform-not-supported",
152
- message: "Apple Sign-In is only available on iOS",
153
- },
154
- };
155
- }
156
-
63
+ export async function getAppleReauthCredential(): Promise<ReauthCredentialResult> {
64
+ if (Platform.OS !== "ios") return { success: false, error: { code: "auth/ios-only", message: "iOS only" } };
157
65
  try {
158
- const isAvailable = await AppleAuthentication.isAvailableAsync();
159
- if (!isAvailable) {
160
- return {
161
- success: false,
162
- error: {
163
- code: "auth/apple-signin-unavailable",
164
- message: "Apple Sign-In is not available on this device",
165
- },
166
- };
167
- }
66
+ if (!(await AppleAuthentication.isAvailableAsync()))
67
+ return { success: false, error: { code: "auth/unavailable", message: "Unavailable" } };
168
68
 
169
- // Generate nonce
170
69
  const nonce = await generateNonce();
171
- const hashedNonce = await Crypto.digestStringAsync(
172
- Crypto.CryptoDigestAlgorithm.SHA256,
173
- nonce
174
- );
175
-
176
- // Request Apple Sign-In
177
- const appleCredential = await AppleAuthentication.signInAsync({
178
- requestedScopes: [
179
- AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
180
- AppleAuthentication.AppleAuthenticationScope.EMAIL,
181
- ],
182
- nonce: hashedNonce,
70
+ const hashed = await Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256, nonce);
71
+ const apple = await AppleAuthentication.signInAsync({
72
+ requestedScopes: [AppleAuthentication.AppleAuthenticationScope.FULL_NAME, AppleAuthentication.AppleAuthenticationScope.EMAIL],
73
+ nonce: hashed,
183
74
  });
184
75
 
185
- if (!appleCredential.identityToken) {
186
- return {
187
- success: false,
188
- error: {
189
- code: "auth/no-identity-token",
190
- message: "No identity token received from Apple",
191
- },
192
- };
193
- }
194
-
195
- // Create Firebase credential
196
- const provider = new OAuthProvider("apple.com");
197
- const credential = provider.credential({
198
- idToken: appleCredential.identityToken,
199
- rawNonce: nonce,
200
- });
201
-
202
- return { success: true, credential };
203
- } catch (error) {
204
- if (error instanceof Error && error.message.includes("ERR_CANCELED")) {
205
- return {
206
- success: false,
207
- error: {
208
- code: "auth/cancelled",
209
- message: "Apple Sign-In was cancelled",
210
- },
211
- };
212
- }
213
-
214
- return {
215
- success: false,
216
- error: {
217
- code: "auth/apple-reauthentication-failed",
218
- message: error instanceof Error ? error.message : "Apple reauthentication failed",
219
- },
220
- };
76
+ if (!apple.identityToken) return { success: false, error: { code: "auth/no-token", message: "No token" } };
77
+ return { success: true, credential: new OAuthProvider("apple.com").credential({ idToken: apple.identityToken, rawNonce: nonce }) };
78
+ } catch (error: any) {
79
+ const code = error.message?.includes("ERR_CANCELED") ? "auth/cancelled" : "auth/failed";
80
+ return { success: false, error: { code, message: error.message } };
221
81
  }
222
82
  }
223
83
 
224
- /**
225
- * Reauthenticate with Apple
226
- */
227
84
  export async function reauthenticateWithApple(user: User): Promise<ReauthenticationResult> {
228
- const result = await getAppleReauthCredential();
229
-
230
- if (!result.success || !result.credential) {
231
- return {
232
- success: false,
233
- error: result.error || {
234
- code: "auth/no-credential",
235
- message: "Failed to get Apple credential",
236
- },
237
- };
238
- }
239
-
85
+ const res = await getAppleReauthCredential();
86
+ if (!res.success || !res.credential) return { success: false, error: res.error };
240
87
  try {
241
- await reauthenticateWithCredential(user, result.credential);
88
+ await reauthenticateWithCredential(user, res.credential);
242
89
  return { success: true };
243
- } catch (error) {
244
- const firebaseError = error as { code?: string; message?: string };
245
- return {
246
- success: false,
247
- error: {
248
- code: firebaseError.code || "auth/reauthentication-failed",
249
- message: firebaseError.message || "Apple reauthentication failed",
250
- },
251
- };
90
+ } catch (error: any) {
91
+ return { success: false, error: { code: error.code || "auth/failed", message: error.message } };
252
92
  }
253
93
  }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Reauthentication Types
3
+ */
4
+
5
+ import type { AuthCredential } from "firebase/auth";
6
+
7
+ export interface ReauthenticationResult {
8
+ success: boolean;
9
+ error?: {
10
+ code: string;
11
+ message: string;
12
+ };
13
+ }
14
+
15
+ export type AuthProviderType = "google.com" | "apple.com" | "password" | "anonymous" | "unknown";
16
+
17
+ export interface ReauthCredentialResult {
18
+ success: boolean;
19
+ credential?: AuthCredential;
20
+ error?: { code: string; message: string };
21
+ }
22
+
23
+ export interface AccountDeletionResult {
24
+ success: boolean;
25
+ error?: {
26
+ code: string;
27
+ message: string;
28
+ requiresReauth: boolean;
29
+ };
30
+ }
31
+
32
+ export interface AccountDeletionOptions {
33
+ /** Google ID token for reauthentication */
34
+ googleIdToken?: string;
35
+ /** Password for reauthentication */
36
+ password?: string;
37
+ /** Attempt Apple reauth automatically */
38
+ autoReauthenticate?: boolean;
39
+ }