@umituz/react-native-firebase 1.13.140 → 1.13.142

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 (26) hide show
  1. package/package.json +1 -1
  2. package/src/auth/infrastructure/config/FirebaseAuthClient.ts +11 -2
  3. package/src/auth/infrastructure/services/account-deletion.service.ts +40 -20
  4. package/src/auth/infrastructure/services/anonymous-auth.service.ts +7 -11
  5. package/src/auth/infrastructure/services/apple-auth.service.ts +44 -18
  6. package/src/auth/infrastructure/services/base/base-auth.service.ts +9 -40
  7. package/src/auth/infrastructure/services/firestore-utils.service.ts +2 -2
  8. package/src/auth/infrastructure/services/google-auth.service.ts +27 -23
  9. package/src/auth/infrastructure/services/password.service.ts +6 -21
  10. package/src/auth/infrastructure/services/reauthentication.service.ts +19 -29
  11. package/src/auth/presentation/hooks/shared/hook-utils.util.ts +222 -0
  12. package/src/auth/presentation/hooks/useSocialAuth.ts +10 -1
  13. package/src/domain/utils/async-executor.util.ts +176 -0
  14. package/src/domain/utils/credential.util.ts +102 -0
  15. package/src/domain/utils/index.ts +101 -0
  16. package/src/domain/utils/result.util.ts +129 -0
  17. package/src/domain/utils/service-config.util.ts +99 -0
  18. package/src/domain/utils/validation.util.ts +78 -0
  19. package/src/firestore/infrastructure/middleware/QueryDeduplicationMiddleware.ts +3 -5
  20. package/src/firestore/infrastructure/repositories/BaseQueryRepository.ts +3 -3
  21. package/src/firestore/utils/dateUtils.ts +21 -5
  22. package/src/firestore/utils/deduplication/pending-query-manager.util.ts +3 -7
  23. package/src/firestore/utils/deduplication/query-key-generator.util.ts +12 -3
  24. package/src/firestore/utils/deduplication/timer-manager.util.ts +8 -2
  25. package/src/infrastructure/config/FirebaseClient.ts +36 -4
  26. package/src/init/createFirebaseInitModule.ts +18 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-firebase",
3
- "version": "1.13.140",
3
+ "version": "1.13.142",
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",
@@ -52,8 +52,17 @@ class FirebaseAuthClientSingleton extends ServiceClientSingleton<Auth, FirebaseA
52
52
  getAuth(): Auth | null {
53
53
  // Attempt initialization if not already initialized
54
54
  if (!this.isInitialized() && !this.getInitializationError()) {
55
- const app = getFirebaseApp();
56
- if (app) this.initialize();
55
+ try {
56
+ const app = getFirebaseApp();
57
+ if (app) {
58
+ this.initialize();
59
+ }
60
+ } catch (error) {
61
+ // Silently handle auto-initialization errors
62
+ // The error will be stored in state for later retrieval
63
+ const errorMessage = error instanceof Error ? error.message : 'Auto-initialization failed';
64
+ this.setError(errorMessage);
65
+ }
57
66
  }
58
67
  return this.getInstance();
59
68
  }
@@ -11,10 +11,14 @@ import {
11
11
  reauthenticateWithPassword,
12
12
  reauthenticateWithGoogle,
13
13
  } from "./reauthentication.service";
14
- import { toAuthErrorInfo } from "../../../domain/utils/error-handler.util";
15
- import type { AccountDeletionResult, AccountDeletionOptions } from "./reauthentication.types";
14
+ import { successResult, type Result } from "../../../domain/utils";
15
+ import type { AccountDeletionOptions } from "./reauthentication.types";
16
16
 
17
- export type { AccountDeletionResult, AccountDeletionOptions } from "./reauthentication.types";
17
+ export interface AccountDeletionResult extends Result<void> {
18
+ requiresReauth?: boolean;
19
+ }
20
+
21
+ export type { AccountDeletionOptions } from "./reauthentication.types";
18
22
 
19
23
  export async function deleteCurrentUser(
20
24
  options: AccountDeletionOptions = { autoReauthenticate: true }
@@ -25,29 +29,36 @@ export async function deleteCurrentUser(
25
29
  if (!auth || !user) {
26
30
  return {
27
31
  success: false,
28
- error: { code: "auth/not-ready", message: "Auth not ready", requiresReauth: false }
32
+ error: { code: "auth/not-ready", message: "Auth not ready" },
33
+ requiresReauth: false
29
34
  };
30
35
  }
31
36
 
32
37
  if (user.isAnonymous) {
33
38
  return {
34
39
  success: false,
35
- error: { code: "auth/anonymous", message: "Cannot delete anonymous", requiresReauth: false }
40
+ error: { code: "auth/anonymous", message: "Cannot delete anonymous" },
41
+ requiresReauth: false
36
42
  };
37
43
  }
38
44
 
39
45
  try {
40
46
  await deleteUser(user);
41
- return { success: true };
47
+ return successResult();
42
48
  } catch (error: unknown) {
43
- const authErr = toAuthErrorInfo(error);
44
- if (authErr.code === "auth/requires-recent-login" && (options.autoReauthenticate || options.password || options.googleIdToken)) {
49
+ const authErr = error instanceof Error ? (error as { code?: string; message?: string }) : null;
50
+ const code = authErr?.code ?? "auth/failed";
51
+ const message = authErr?.message ?? "Account deletion failed";
52
+
53
+ if (code === "auth/requires-recent-login" && (options.autoReauthenticate || options.password || options.googleIdToken)) {
45
54
  const reauth = await attemptReauth(user, options);
46
55
  if (reauth) return reauth;
47
56
  }
57
+
48
58
  return {
49
59
  success: false,
50
- error: { ...authErr, requiresReauth: authErr.code === "auth/requires-recent-login" }
60
+ error: { code, message },
61
+ requiresReauth: code === "auth/requires-recent-login"
51
62
  };
52
63
  }
53
64
  }
@@ -62,7 +73,8 @@ async function attemptReauth(user: User, options: AccountDeletionOptions): Promi
62
73
  if (!options.googleIdToken) {
63
74
  return {
64
75
  success: false,
65
- error: { code: "auth/google-reauth", message: "Google reauth required", requiresReauth: true }
76
+ error: { code: "auth/google-reauth", message: "Google reauth required" },
77
+ requiresReauth: true
66
78
  };
67
79
  }
68
80
  res = await reauthenticateWithGoogle(user, options.googleIdToken);
@@ -70,7 +82,8 @@ async function attemptReauth(user: User, options: AccountDeletionOptions): Promi
70
82
  if (!options.password) {
71
83
  return {
72
84
  success: false,
73
- error: { code: "auth/password-reauth", message: "Password required", requiresReauth: true }
85
+ error: { code: "auth/password-reauth", message: "Password required" },
86
+ requiresReauth: true
74
87
  };
75
88
  }
76
89
  res = await reauthenticateWithPassword(user, options.password);
@@ -81,10 +94,14 @@ async function attemptReauth(user: User, options: AccountDeletionOptions): Promi
81
94
  if (res.success) {
82
95
  try {
83
96
  await deleteUser(user);
84
- return { success: true };
97
+ return successResult();
85
98
  } catch (err: unknown) {
86
- const authErr = toAuthErrorInfo(err);
87
- return { success: false, error: { ...authErr, requiresReauth: false } };
99
+ const authErr = err instanceof Error ? (err as { code?: string; message?: string }) : null;
100
+ return {
101
+ success: false,
102
+ error: { code: authErr?.code ?? "auth/failed", message: authErr?.message ?? "Deletion failed" },
103
+ requiresReauth: false
104
+ };
88
105
  }
89
106
  }
90
107
 
@@ -93,8 +110,8 @@ async function attemptReauth(user: User, options: AccountDeletionOptions): Promi
93
110
  error: {
94
111
  code: res.error?.code || "auth/reauth-failed",
95
112
  message: res.error?.message || "Reauth failed",
96
- requiresReauth: true
97
- }
113
+ },
114
+ requiresReauth: true
98
115
  };
99
116
  }
100
117
 
@@ -102,18 +119,21 @@ export async function deleteUserAccount(user: User | null): Promise<AccountDelet
102
119
  if (!user || user.isAnonymous) {
103
120
  return {
104
121
  success: false,
105
- error: { code: "auth/invalid", message: "Invalid user", requiresReauth: false }
122
+ error: { code: "auth/invalid", message: "Invalid user" },
123
+ requiresReauth: false
106
124
  };
107
125
  }
108
126
 
109
127
  try {
110
128
  await deleteUser(user);
111
- return { success: true };
129
+ return successResult();
112
130
  } catch (error: unknown) {
113
- const authErr = toAuthErrorInfo(error);
131
+ const authErr = error instanceof Error ? (error as { code?: string; message?: string }) : null;
132
+ const code = authErr?.code ?? "auth/failed";
114
133
  return {
115
134
  success: false,
116
- error: { ...authErr, requiresReauth: authErr.code === "auth/requires-recent-login" }
135
+ error: { code, message: authErr?.message ?? "Deletion failed" },
136
+ requiresReauth: code === "auth/requires-recent-login"
117
137
  };
118
138
  }
119
139
  }
@@ -34,17 +34,13 @@ export class AnonymousAuthService implements AnonymousAuthServiceInterface {
34
34
  throw new Error("A non-anonymous user is already signed in. Sign out first before creating an anonymous session.");
35
35
  }
36
36
 
37
- try {
38
- const userCredential = await signInAnonymously(auth);
39
- const anonymousUser = toAnonymousUser(userCredential.user);
40
- return {
41
- user: userCredential.user,
42
- anonymousUser,
43
- wasAlreadySignedIn: false,
44
- };
45
- } catch (error) {
46
- throw error;
47
- }
37
+ const userCredential = await signInAnonymously(auth);
38
+ const anonymousUser = toAnonymousUser(userCredential.user);
39
+ return {
40
+ user: userCredential.user,
41
+ anonymousUser,
42
+ wasAlreadySignedIn: false,
43
+ };
48
44
  }
49
45
  }
50
46
 
@@ -13,10 +13,9 @@ import { Platform } from "react-native";
13
13
  import { generateNonce, hashNonce } from "./crypto.util";
14
14
  import type { AppleAuthResult } from "./apple-auth.types";
15
15
  import {
16
- createSuccessResult,
17
- createFailureResult,
18
16
  isCancellationError,
19
17
  } from "./base/base-auth.service";
18
+ import { executeAuthOperation, type Result } from "../../../domain/utils";
20
19
 
21
20
  export class AppleAuthService {
22
21
  async isAvailable(): Promise<boolean> {
@@ -28,17 +27,32 @@ export class AppleAuthService {
28
27
  }
29
28
  }
30
29
 
30
+ private convertToAppleAuthResult(result: Result<{ userCredential: any; isNewUser: boolean }>): AppleAuthResult {
31
+ if (result.success && result.data) {
32
+ return {
33
+ success: true,
34
+ userCredential: result.data.userCredential,
35
+ isNewUser: result.data.isNewUser,
36
+ };
37
+ }
38
+ return {
39
+ success: false,
40
+ error: result.error?.message ?? "Apple sign-in failed",
41
+ code: result.error?.code,
42
+ };
43
+ }
44
+
31
45
  async signIn(auth: Auth): Promise<AppleAuthResult> {
32
- try {
33
- const isAvailable = await this.isAvailable();
34
- if (!isAvailable) {
35
- return {
36
- success: false,
37
- error: "Apple Sign-In is not available on this device",
38
- code: "unavailable"
39
- };
40
- }
46
+ const isAvailable = await this.isAvailable();
47
+ if (!isAvailable) {
48
+ return {
49
+ success: false,
50
+ error: "Apple Sign-In is not available on this device",
51
+ code: "unavailable"
52
+ };
53
+ }
41
54
 
55
+ const result = await executeAuthOperation(async () => {
42
56
  const nonce = await generateNonce();
43
57
  const hashedNonce = await hashNonce(nonce);
44
58
 
@@ -51,11 +65,7 @@ export class AppleAuthService {
51
65
  });
52
66
 
53
67
  if (!appleCredential.identityToken) {
54
- return {
55
- success: false,
56
- error: "No identity token received",
57
- code: "no_token"
58
- };
68
+ throw new Error("No identity token received");
59
69
  }
60
70
 
61
71
  const provider = new OAuthProvider("apple.com");
@@ -65,7 +75,22 @@ export class AppleAuthService {
65
75
  });
66
76
 
67
77
  const userCredential = await signInWithCredential(auth, credential);
68
- return createSuccessResult(userCredential);
78
+ return {
79
+ userCredential,
80
+ isNewUser: userCredential.user.metadata.creationTime ===
81
+ userCredential.user.metadata.lastSignInTime
82
+ };
83
+ });
84
+
85
+ return this.convertToAppleAuthResult(result);
86
+ }
87
+
88
+ /**
89
+ * Sign in with error handling for cancellation
90
+ */
91
+ async signInWithCancellationHandling(auth: Auth): Promise<AppleAuthResult> {
92
+ try {
93
+ return await this.signIn(auth);
69
94
  } catch (error) {
70
95
  if (isCancellationError(error)) {
71
96
  return {
@@ -75,7 +100,8 @@ export class AppleAuthService {
75
100
  };
76
101
  }
77
102
 
78
- return createFailureResult(error);
103
+ // Re-throw for executeAuthOperation to handle
104
+ throw error;
79
105
  }
80
106
  }
81
107
 
@@ -6,39 +6,20 @@
6
6
  */
7
7
 
8
8
  import type { UserCredential } from 'firebase/auth';
9
- import { toAuthErrorInfo } from '../../../../domain/utils/error-handler.util';
9
+ import { authErrorConverter, type Result } from '../../../../domain/utils';
10
10
 
11
11
  /**
12
- * Base authentication result interface
12
+ * Authentication result with user credential
13
13
  */
14
- export interface BaseAuthResult {
15
- readonly success: boolean;
16
- readonly error?: string;
17
- readonly code?: string;
18
- }
19
-
20
- /**
21
- * Successful authentication result
22
- */
23
- export interface AuthSuccessResult extends BaseAuthResult {
24
- readonly success: true;
14
+ export interface AuthSuccessData {
25
15
  readonly userCredential: UserCredential;
26
16
  readonly isNewUser: boolean;
27
17
  }
28
18
 
29
19
  /**
30
- * Failed authentication result
31
- */
32
- export interface AuthFailureResult extends BaseAuthResult {
33
- readonly success: false;
34
- readonly error: string;
35
- readonly code: string;
36
- }
37
-
38
- /**
39
- * Combined auth result type
20
+ * Auth result type that extends the base Result
40
21
  */
41
- export type AuthResult = AuthSuccessResult | AuthFailureResult;
22
+ export type AuthResult = Result<AuthSuccessData>;
42
23
 
43
24
  /**
44
25
  * Check if user is new based on metadata
@@ -50,17 +31,6 @@ export function checkIsNewUser(userCredential: UserCredential): boolean {
50
31
  );
51
32
  }
52
33
 
53
- /**
54
- * Extract error information from unknown error
55
- */
56
- export function extractAuthError(error: unknown): { code: string; message: string } {
57
- const errorInfo = toAuthErrorInfo(error);
58
- return {
59
- code: errorInfo.code,
60
- message: errorInfo.message,
61
- };
62
- }
63
-
64
34
  /**
65
35
  * Check if error is a cancellation error
66
36
  */
@@ -74,19 +44,18 @@ export function isCancellationError(error: unknown): boolean {
74
44
  /**
75
45
  * Create failure result from error
76
46
  */
77
- export function createFailureResult(error: unknown): AuthFailureResult {
78
- const { code, message } = extractAuthError(error);
47
+ export function createFailureResult(error: unknown): { success: false; error: { code: string; message: string } } {
48
+ const errorInfo = authErrorConverter(error);
79
49
  return {
80
50
  success: false,
81
- error: message,
82
- code,
51
+ error: errorInfo,
83
52
  };
84
53
  }
85
54
 
86
55
  /**
87
56
  * Create success result from user credential
88
57
  */
89
- export function createSuccessResult(userCredential: UserCredential): AuthSuccessResult {
58
+ export function createSuccessResult(userCredential: UserCredential): { success: true; userCredential: UserCredential; isNewUser: boolean } {
90
59
  return {
91
60
  success: true,
92
61
  userCredential,
@@ -55,7 +55,7 @@ export function shouldSkipFirestoreQuery(
55
55
  const {
56
56
  skipForGuest = true,
57
57
  skipIfNotAuthenticated = true,
58
- verifyUserId: shouldVerify = true,
58
+ verifyUserId: shouldVerifyUserId = true,
59
59
  userId,
60
60
  } = options;
61
61
 
@@ -80,7 +80,7 @@ export function shouldSkipFirestoreQuery(
80
80
  return createResult(true, authState, "is_guest");
81
81
  }
82
82
 
83
- if (shouldVerify && userId && !verifyUserId(auth, userId)) {
83
+ if (shouldVerifyUserId && userId && !verifyUserId(auth, userId)) {
84
84
  return createResult(true, authState, "user_id_mismatch");
85
85
  }
86
86
 
@@ -9,46 +9,50 @@ import {
9
9
  type Auth,
10
10
  } from "firebase/auth";
11
11
  import type { GoogleAuthConfig, GoogleAuthResult } from "./google-auth.types";
12
- import {
13
- createSuccessResult,
14
- createFailureResult,
15
- } from "./base/base-auth.service";
12
+ import { executeAuthOperation, type Result } from "../../../domain/utils";
13
+ import { ConfigurableService } from "../../../domain/utils/service-config.util";
16
14
 
17
15
  /**
18
16
  * Google Auth Service
19
17
  * Provides Google Sign-In functionality for Firebase
20
18
  */
21
- export class GoogleAuthService {
22
- private config: GoogleAuthConfig | null = null;
23
-
24
- configure(config: GoogleAuthConfig): void {
25
- this.config = config;
26
- }
27
-
28
- isConfigured(): boolean {
19
+ export class GoogleAuthService extends ConfigurableService<GoogleAuthConfig> {
20
+ protected isValidConfig(config: GoogleAuthConfig): boolean {
29
21
  return (
30
- this.config !== null &&
31
- (!!this.config.webClientId ||
32
- !!this.config.iosClientId ||
33
- !!this.config.androidClientId)
22
+ config !== null &&
23
+ (!!config.webClientId || !!config.iosClientId || !!config.androidClientId)
34
24
  );
35
25
  }
36
26
 
37
- getConfig(): GoogleAuthConfig | null {
38
- return this.config;
27
+ private convertToGoogleAuthResult(result: Result<{ userCredential: any; isNewUser: boolean }>): GoogleAuthResult {
28
+ if (result.success && result.data) {
29
+ return {
30
+ success: true,
31
+ userCredential: result.data.userCredential,
32
+ isNewUser: result.data.isNewUser,
33
+ };
34
+ }
35
+ return {
36
+ success: false,
37
+ error: result.error?.message ?? "Google sign-in failed",
38
+ code: result.error?.code,
39
+ };
39
40
  }
40
41
 
41
42
  async signInWithIdToken(
42
43
  auth: Auth,
43
44
  idToken: string,
44
45
  ): Promise<GoogleAuthResult> {
45
- try {
46
+ const result = await executeAuthOperation(async () => {
46
47
  const credential = GoogleAuthProvider.credential(idToken);
47
48
  const userCredential = await signInWithCredential(auth, credential);
48
- return createSuccessResult(userCredential);
49
- } catch (error) {
50
- return createFailureResult(error);
51
- }
49
+ return {
50
+ userCredential,
51
+ isNewUser: userCredential.user.metadata.creationTime ===
52
+ userCredential.user.metadata.lastSignInTime
53
+ };
54
+ });
55
+ return this.convertToGoogleAuthResult(result);
52
56
  }
53
57
  }
54
58
 
@@ -4,35 +4,20 @@
4
4
  */
5
5
 
6
6
  import { updatePassword, type User } from 'firebase/auth';
7
- import { toAuthErrorInfo } from '../../../domain/utils/error-handler.util';
7
+ import { executeAuthOperation, type Result } from '../../../domain/utils';
8
8
 
9
9
  /**
10
10
  * Result of a password update operation
11
+ * @deprecated Use Result<void> instead
11
12
  */
12
- export interface PasswordUpdateResult {
13
- success: boolean;
14
- error?: {
15
- code: string;
16
- message: string;
17
- };
18
- }
13
+ export type PasswordUpdateResult = Result<void>;
19
14
 
20
15
  /**
21
16
  * Update the current user's password
22
17
  * Note: Requires recent authentication. Re-authenticate before calling if needed.
23
18
  */
24
- export async function updateUserPassword(user: User, newPassword: string): Promise<PasswordUpdateResult> {
25
- try {
19
+ export async function updateUserPassword(user: User, newPassword: string): Promise<Result<void>> {
20
+ return executeAuthOperation(async () => {
26
21
  await updatePassword(user, newPassword);
27
- return { success: true };
28
- } catch (error: unknown) {
29
- const errorInfo = toAuthErrorInfo(error);
30
- return {
31
- success: false,
32
- error: {
33
- code: errorInfo.code,
34
- message: errorInfo.message,
35
- },
36
- };
37
- }
22
+ });
38
23
  }
@@ -13,7 +13,8 @@ import {
13
13
  import * as AppleAuthentication from "expo-apple-authentication";
14
14
  import { Platform } from "react-native";
15
15
  import { generateNonce, hashNonce } from "./crypto.util";
16
- import { toAuthErrorInfo, isCancelledError } from "../../../domain/utils/error-handler.util";
16
+ import { executeOperation, failureResultFrom } from "../../../domain/utils";
17
+ import { isCancelledError } from "../../../domain/utils/error-handler.util";
17
18
  import type {
18
19
  ReauthenticationResult,
19
20
  AuthProviderType,
@@ -37,30 +38,20 @@ export function getUserAuthProvider(user: User): AuthProviderType {
37
38
  }
38
39
 
39
40
  export async function reauthenticateWithGoogle(user: User, idToken: string): Promise<ReauthenticationResult> {
40
- try {
41
+ return executeOperation(async () => {
41
42
  await reauthenticateWithCredential(user, GoogleAuthProvider.credential(idToken));
42
- return { success: true };
43
- } catch (error: unknown) {
44
- const err = toAuthErrorInfo(error);
45
- return { success: false, error: err };
46
- }
43
+ });
47
44
  }
48
45
 
49
46
  export async function reauthenticateWithPassword(user: User, pass: string): Promise<ReauthenticationResult> {
50
- if (!user.email) {
51
- return {
52
- success: false,
53
- error: { code: "auth/no-email", message: "User has no email" }
54
- };
47
+ const email = user.email;
48
+ if (!email) {
49
+ return failureResultFrom("auth/no-email", "User has no email");
55
50
  }
56
51
 
57
- try {
58
- await reauthenticateWithCredential(user, EmailAuthProvider.credential(user.email, pass));
59
- return { success: true };
60
- } catch (error: unknown) {
61
- const err = toAuthErrorInfo(error);
62
- return { success: false, error: err };
63
- }
52
+ return executeOperation(async () => {
53
+ await reauthenticateWithCredential(user, EmailAuthProvider.credential(email, pass));
54
+ });
64
55
  }
65
56
 
66
57
  export async function getAppleReauthCredential(): Promise<ReauthCredentialResult> {
@@ -107,8 +98,8 @@ export async function getAppleReauthCredential(): Promise<ReauthCredentialResult
107
98
  credential
108
99
  };
109
100
  } catch (error: unknown) {
110
- const err = toAuthErrorInfo(error);
111
- const code = isCancelledError(err) ? "auth/cancelled" : err.code;
101
+ const err = error instanceof Error ? error : new Error(String(error));
102
+ const code = isCancelledError({ code: '', message: err.message }) ? "auth/cancelled" : "auth/failed";
112
103
  return {
113
104
  success: false,
114
105
  error: { code, message: err.message }
@@ -118,13 +109,12 @@ export async function getAppleReauthCredential(): Promise<ReauthCredentialResult
118
109
 
119
110
  export async function reauthenticateWithApple(user: User): Promise<ReauthenticationResult> {
120
111
  const res = await getAppleReauthCredential();
121
- if (!res.success || !res.credential) return { success: false, error: res.error };
122
-
123
- try {
124
- await reauthenticateWithCredential(user, res.credential);
125
- return { success: true };
126
- } catch (error: unknown) {
127
- const err = toAuthErrorInfo(error);
128
- return { success: false, error: err };
112
+ if (!res.success || !res.credential) {
113
+ return { success: false, error: res.error ?? { code: "auth/failed", message: "Failed to get credential" } };
129
114
  }
115
+
116
+ const credential = res.credential;
117
+ return executeOperation(async () => {
118
+ await reauthenticateWithCredential(user, credential);
119
+ });
130
120
  }