@umituz/react-native-firebase 1.13.161 → 1.13.162
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 +1 -1
- package/src/auth/infrastructure/services/account-deletion.service.ts +9 -2
- package/src/auth/infrastructure/services/apple-auth.service.ts +2 -1
- package/src/auth/infrastructure/services/auth-listener.service.ts +1 -1
- package/src/auth/infrastructure/services/email-auth.service.ts +10 -4
- package/src/auth/infrastructure/services/google-oauth.service.ts +5 -47
- package/src/auth/presentation/hooks/useGoogleOAuth.ts +23 -3
- package/src/firestore/index.ts +0 -3
- package/src/firestore/infrastructure/repositories/BaseRepository.ts +0 -23
- package/src/index.ts +0 -1
- package/src/firestore/utils/path-resolver.ts +0 -44
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-firebase",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.162",
|
|
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",
|
|
@@ -50,7 +50,11 @@ export async function deleteCurrentUser(
|
|
|
50
50
|
const code = authErr?.code ?? "auth/failed";
|
|
51
51
|
const message = authErr?.message ?? "Account deletion failed";
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
// Determine if we should attempt reauthentication
|
|
54
|
+
const hasCredentials = !!(options.password || options.googleIdToken);
|
|
55
|
+
const shouldReauth = options.autoReauthenticate === true || hasCredentials;
|
|
56
|
+
|
|
57
|
+
if (code === "auth/requires-recent-login" && shouldReauth) {
|
|
54
58
|
const reauth = await attemptReauth(user, options);
|
|
55
59
|
if (reauth) return reauth;
|
|
56
60
|
}
|
|
@@ -93,7 +97,10 @@ async function attemptReauth(user: User, options: AccountDeletionOptions): Promi
|
|
|
93
97
|
|
|
94
98
|
if (res.success) {
|
|
95
99
|
try {
|
|
96
|
-
|
|
100
|
+
// After reauthentication, get fresh user reference from auth
|
|
101
|
+
const auth = getFirebaseAuth();
|
|
102
|
+
const currentUser = auth?.currentUser || user;
|
|
103
|
+
await deleteUser(currentUser);
|
|
97
104
|
return successResult();
|
|
98
105
|
} catch (err: unknown) {
|
|
99
106
|
const authErr = err instanceof Error ? (err as { code?: string; message?: string }) : null;
|
|
@@ -22,9 +22,10 @@ let AppleAuthentication: any = null;
|
|
|
22
22
|
let isAppleAuthAvailable = false;
|
|
23
23
|
|
|
24
24
|
try {
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
25
26
|
AppleAuthentication = require("expo-apple-authentication");
|
|
26
27
|
isAppleAuthAvailable = true;
|
|
27
|
-
} catch
|
|
28
|
+
} catch {
|
|
28
29
|
// expo-apple-authentication not available - this is fine if not using Apple auth
|
|
29
30
|
console.info("expo-apple-authentication is not installed. Apple authentication will not be available.");
|
|
30
31
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { onIdTokenChanged, type User } from "firebase/auth";
|
|
7
7
|
import { getFirebaseAuth } from "../config/FirebaseAuthClient";
|
|
8
8
|
import type { Result } from "../../../domain/utils";
|
|
9
|
-
import {
|
|
9
|
+
import { failureResultFrom } from "../../../domain/utils";
|
|
10
10
|
|
|
11
11
|
export interface AuthListenerConfig {
|
|
12
12
|
/**
|
|
@@ -82,11 +82,17 @@ export async function signUpWithEmail(
|
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
// Update display name if provided
|
|
85
|
+
// Update display name if provided (non-critical operation)
|
|
86
86
|
if (credentials.displayName && userCredential.user) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
try {
|
|
88
|
+
await updateProfile(userCredential.user, {
|
|
89
|
+
displayName: credentials.displayName.trim(),
|
|
90
|
+
});
|
|
91
|
+
} catch (profileError) {
|
|
92
|
+
// Profile update failed but account was created successfully
|
|
93
|
+
// Log the error but don't fail the signup
|
|
94
|
+
console.warn("Profile update failed after account creation:", profileError);
|
|
95
|
+
}
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
return { success: true, data: userCredential.user };
|
|
@@ -8,13 +8,12 @@ import type { Auth } from "firebase/auth";
|
|
|
8
8
|
import type { GoogleAuthResult } from "./google-auth.types";
|
|
9
9
|
import { googleAuthService } from "./google-auth.service";
|
|
10
10
|
|
|
11
|
-
// Conditional
|
|
12
|
-
let ExpoAuthSession: any = null;
|
|
11
|
+
// Conditional import - expo-web-browser is optional
|
|
13
12
|
let ExpoWebBrowser: any = null;
|
|
14
13
|
let isExpoAuthAvailable = false;
|
|
15
14
|
|
|
16
15
|
try {
|
|
17
|
-
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
18
17
|
ExpoWebBrowser = require("expo-web-browser");
|
|
19
18
|
isExpoAuthAvailable = true;
|
|
20
19
|
|
|
@@ -22,9 +21,9 @@ try {
|
|
|
22
21
|
if (ExpoWebBrowser?.maybeCompleteAuthSession) {
|
|
23
22
|
ExpoWebBrowser.maybeCompleteAuthSession();
|
|
24
23
|
}
|
|
25
|
-
} catch
|
|
26
|
-
// expo-
|
|
27
|
-
console.info("expo-
|
|
24
|
+
} catch {
|
|
25
|
+
// expo-web-browser not available - this is fine if not using Google OAuth
|
|
26
|
+
console.info("expo-web-browser is not installed. Google OAuth will not be available.");
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
export interface GoogleOAuthConfig {
|
|
@@ -33,12 +32,6 @@ export interface GoogleOAuthConfig {
|
|
|
33
32
|
androidClientId?: string;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
export interface GoogleOAuthHookResult {
|
|
37
|
-
request: any;
|
|
38
|
-
response: any;
|
|
39
|
-
promptAsync: (() => Promise<any>) | null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
35
|
const PLACEHOLDER_CLIENT_ID = "000000000000-placeholder.apps.googleusercontent.com";
|
|
43
36
|
|
|
44
37
|
function validateGoogleConfig(config?: GoogleOAuthConfig): boolean {
|
|
@@ -69,41 +62,6 @@ export class GoogleOAuthService {
|
|
|
69
62
|
return validateGoogleConfig(config);
|
|
70
63
|
}
|
|
71
64
|
|
|
72
|
-
/**
|
|
73
|
-
* Create auth request hook
|
|
74
|
-
* This mimics the behavior of expo-auth-session's useAuthRequest hook
|
|
75
|
-
*/
|
|
76
|
-
createAuthRequest(config?: GoogleOAuthConfig): GoogleOAuthHookResult {
|
|
77
|
-
if (!isExpoAuthAvailable || !ExpoAuthSession?.useAuthRequest) {
|
|
78
|
-
return {
|
|
79
|
-
request: null,
|
|
80
|
-
response: null,
|
|
81
|
-
promptAsync: null,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
const [request, response, promptAsync] = ExpoAuthSession.useAuthRequest({
|
|
87
|
-
iosClientId: config?.iosClientId || PLACEHOLDER_CLIENT_ID,
|
|
88
|
-
webClientId: config?.webClientId || PLACEHOLDER_CLIENT_ID,
|
|
89
|
-
androidClientId: config?.androidClientId || PLACEHOLDER_CLIENT_ID,
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
request: request ?? null,
|
|
94
|
-
response: response ?? null,
|
|
95
|
-
promptAsync: promptAsync ?? null,
|
|
96
|
-
};
|
|
97
|
-
} catch (error) {
|
|
98
|
-
console.error("Failed to create Google auth request:", error);
|
|
99
|
-
return {
|
|
100
|
-
request: null,
|
|
101
|
-
response: null,
|
|
102
|
-
promptAsync: null,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
65
|
/**
|
|
108
66
|
* Sign in with Google using OAuth flow
|
|
109
67
|
*/
|
|
@@ -9,6 +9,18 @@ import { googleOAuthService } from "../../infrastructure/services/google-oauth.s
|
|
|
9
9
|
import { getFirebaseAuth } from "../../infrastructure/config/FirebaseAuthClient";
|
|
10
10
|
import type { GoogleOAuthConfig } from "../../infrastructure/services/google-oauth.service";
|
|
11
11
|
|
|
12
|
+
// Conditional import for expo-auth-session
|
|
13
|
+
let ExpoAuthSession: any = null;
|
|
14
|
+
let isExpoAuthAvailable = false;
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
18
|
+
ExpoAuthSession = require("expo-auth-session/providers/google");
|
|
19
|
+
isExpoAuthAvailable = true;
|
|
20
|
+
} catch {
|
|
21
|
+
// expo-auth-session not available - hook will return unavailable state
|
|
22
|
+
}
|
|
23
|
+
|
|
12
24
|
export interface UseGoogleOAuthResult {
|
|
13
25
|
signInWithGoogle: () => Promise<SocialAuthResult>;
|
|
14
26
|
googleLoading: boolean;
|
|
@@ -23,6 +35,8 @@ export interface SocialAuthResult {
|
|
|
23
35
|
error?: string;
|
|
24
36
|
}
|
|
25
37
|
|
|
38
|
+
const PLACEHOLDER_CLIENT_ID = "000000000000-placeholder.apps.googleusercontent.com";
|
|
39
|
+
|
|
26
40
|
/**
|
|
27
41
|
* Hook for Google OAuth authentication
|
|
28
42
|
* Requires expo-auth-session and expo-web-browser to be installed
|
|
@@ -34,9 +48,15 @@ export function useGoogleOAuth(config?: GoogleOAuthConfig): UseGoogleOAuthResult
|
|
|
34
48
|
const googleAvailable = googleOAuthService.isAvailable();
|
|
35
49
|
const googleConfigured = googleOAuthService.isConfigured(config);
|
|
36
50
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
const
|
|
51
|
+
// Call the Hook directly (only valid in React component)
|
|
52
|
+
// If expo-auth-session is not available, these will be null
|
|
53
|
+
const [request, response, promptAsync] = isExpoAuthAvailable && ExpoAuthSession?.useAuthRequest
|
|
54
|
+
? ExpoAuthSession.useAuthRequest({
|
|
55
|
+
iosClientId: config?.iosClientId || PLACEHOLDER_CLIENT_ID,
|
|
56
|
+
webClientId: config?.webClientId || PLACEHOLDER_CLIENT_ID,
|
|
57
|
+
androidClientId: config?.androidClientId || PLACEHOLDER_CLIENT_ID,
|
|
58
|
+
})
|
|
59
|
+
: [null, null, null];
|
|
40
60
|
|
|
41
61
|
// Handle OAuth response
|
|
42
62
|
useEffect(() => {
|
package/src/firestore/index.ts
CHANGED
|
@@ -84,13 +84,11 @@ export abstract class BaseRepository {
|
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* Execute operation with error handling
|
|
87
|
-
* @param collection - Collection name for tracking
|
|
88
87
|
* @param operation - Operation to execute
|
|
89
88
|
* @returns Operation result
|
|
90
89
|
* @throws Error if operation fails
|
|
91
90
|
*/
|
|
92
91
|
protected async executeOperation<T>(
|
|
93
|
-
collection: string,
|
|
94
92
|
operation: () => Promise<T>
|
|
95
93
|
): Promise<T> {
|
|
96
94
|
if (this.state === RepositoryState.DESTROYED) {
|
|
@@ -111,27 +109,6 @@ export abstract class BaseRepository {
|
|
|
111
109
|
}
|
|
112
110
|
}
|
|
113
111
|
|
|
114
|
-
/**
|
|
115
|
-
* Track read operation
|
|
116
|
-
*/
|
|
117
|
-
protected trackRead(_collection: string, _count: number = 1): void {
|
|
118
|
-
// Quota tracking delegated to middleware
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Track write operation
|
|
123
|
-
*/
|
|
124
|
-
protected trackWrite(_collection: string, _count: number = 1): void {
|
|
125
|
-
// Quota tracking delegated to middleware
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Track delete operation
|
|
130
|
-
*/
|
|
131
|
-
protected trackDelete(_collection: string, _count: number = 1): void {
|
|
132
|
-
// Quota tracking delegated to middleware
|
|
133
|
-
}
|
|
134
|
-
|
|
135
112
|
/**
|
|
136
113
|
* Destroy the repository
|
|
137
114
|
* Prevents further operations
|
package/src/index.ts
CHANGED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { Firestore, CollectionReference, DocumentReference, DocumentData } from "firebase/firestore";
|
|
2
|
-
import { collection, doc } from "firebase/firestore";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Resolves Firestore paths for user collections
|
|
6
|
-
* Standard pattern: users/{userId}/{collectionName}
|
|
7
|
-
*
|
|
8
|
-
* Stateless design: db is passed to methods, not stored in constructor
|
|
9
|
-
* This eliminates initialization timing issues and makes testing easier
|
|
10
|
-
* This class is designed to be used across hundreds of apps.
|
|
11
|
-
* All user data MUST be under users/{userId}/ for consistency.
|
|
12
|
-
*/
|
|
13
|
-
export class FirestorePathResolver {
|
|
14
|
-
constructor(
|
|
15
|
-
private readonly collectionName: string,
|
|
16
|
-
) { }
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Get collection reference for a user
|
|
20
|
-
* Pattern: users/{userId}/{collectionName}
|
|
21
|
-
*
|
|
22
|
-
* @param db Firestore instance
|
|
23
|
-
* @param userId User identifier
|
|
24
|
-
* @returns CollectionReference or null if db not initialized
|
|
25
|
-
*/
|
|
26
|
-
getUserCollection(db: Firestore | null, userId: string): CollectionReference<DocumentData> | null {
|
|
27
|
-
if (!db) return null;
|
|
28
|
-
return collection(db, "users", userId, this.collectionName);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Get document reference for a specific item
|
|
33
|
-
* Pattern: users/{userId}/{collectionName}/{documentId}
|
|
34
|
-
*
|
|
35
|
-
* @param db Firestore instance
|
|
36
|
-
* @param userId User identifier
|
|
37
|
-
* @param documentId Document identifier
|
|
38
|
-
* @returns DocumentReference or null if db not initialized
|
|
39
|
-
*/
|
|
40
|
-
getDocRef(db: Firestore | null, userId: string, documentId: string): DocumentReference<DocumentData> | null {
|
|
41
|
-
if (!db) return null;
|
|
42
|
-
return doc(db, "users", userId, this.collectionName, documentId);
|
|
43
|
-
}
|
|
44
|
-
}
|