@umituz/react-native-auth 3.4.23 → 3.4.24

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-auth",
3
- "version": "3.4.23",
3
+ "version": "3.4.24",
4
4
  "description": "Authentication service for React Native apps - Secure, type-safe, and production-ready. Provider-agnostic design with dependency injection, configurable validation, and comprehensive error handling.",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -11,6 +11,12 @@ import type {
11
11
  UserDocumentConfig,
12
12
  UserDocumentExtras,
13
13
  } from "./UserDocument.types";
14
+ import {
15
+ getSignUpMethod,
16
+ buildBaseData,
17
+ buildCreateData,
18
+ buildUpdateData,
19
+ } from "../utils/userDocumentBuilder.util";
14
20
 
15
21
  export type {
16
22
  UserDocumentUser,
@@ -26,90 +32,6 @@ export function configureUserDocumentService(config: UserDocumentConfig): void {
26
32
  userDocumentConfig = { ...config };
27
33
  }
28
34
 
29
- function getSignUpMethod(user: UserDocumentUser): string | undefined {
30
- if (user.isAnonymous) return "anonymous";
31
- if (user.email) {
32
- const providerData = (
33
- user as unknown as { providerData?: { providerId: string }[] }
34
- ).providerData;
35
- if (providerData && providerData.length > 0) {
36
- const providerId = providerData[0].providerId;
37
- if (providerId === "google.com") return "google";
38
- if (providerId === "apple.com") return "apple";
39
- if (providerId === "password") return "email";
40
- }
41
- return "email";
42
- }
43
- return undefined;
44
- }
45
-
46
- function buildBaseData(
47
- user: UserDocumentUser,
48
- extras?: UserDocumentExtras,
49
- ): Record<string, unknown> {
50
- const data: Record<string, unknown> = {
51
- displayName: user.displayName,
52
- email: user.email,
53
- photoURL: user.photoURL,
54
- isAnonymous: user.isAnonymous,
55
- };
56
-
57
- const fields: (keyof UserDocumentExtras)[] = [
58
- 'deviceId', 'platform', 'deviceModel', 'deviceBrand',
59
- 'osVersion', 'appVersion', 'buildNumber', 'locale', 'timezone'
60
- ];
61
-
62
- fields.forEach(field => {
63
- const val = extras?.[field];
64
- if (val) data[field] = val;
65
- });
66
-
67
- return data;
68
- }
69
-
70
- function buildCreateData(
71
- baseData: Record<string, unknown>,
72
- extras?: UserDocumentExtras,
73
- ): Record<string, unknown> {
74
- const createData: Record<string, unknown> = {
75
- ...baseData,
76
- ...userDocumentConfig.extraFields,
77
- createdAt: serverTimestamp(),
78
- updatedAt: serverTimestamp(),
79
- lastLoginAt: serverTimestamp(),
80
- };
81
-
82
- if (extras?.previousAnonymousUserId) {
83
- createData.previousAnonymousUserId = extras.previousAnonymousUserId;
84
- createData.convertedFromAnonymous = true;
85
- createData.convertedAt = serverTimestamp();
86
- }
87
-
88
- if (extras?.signUpMethod) createData.signUpMethod = extras.signUpMethod;
89
-
90
- return createData;
91
- }
92
-
93
- function buildUpdateData(
94
- baseData: Record<string, unknown>,
95
- extras?: UserDocumentExtras,
96
- ): Record<string, unknown> {
97
- const updateData: Record<string, unknown> = {
98
- ...baseData,
99
- lastLoginAt: serverTimestamp(),
100
- updatedAt: serverTimestamp(),
101
- };
102
-
103
- if (extras?.previousAnonymousUserId) {
104
- updateData.previousAnonymousUserId = extras.previousAnonymousUserId;
105
- updateData.convertedFromAnonymous = true;
106
- updateData.convertedAt = serverTimestamp();
107
- if (extras?.signUpMethod) updateData.signUpMethod = extras.signUpMethod;
108
- }
109
-
110
- return updateData;
111
- }
112
-
113
35
  export async function ensureUserDocument(
114
36
  user: UserDocumentUser | User,
115
37
  extras?: UserDocumentExtras,
@@ -131,8 +53,8 @@ export async function ensureUserDocument(
131
53
  const userDoc = await getDoc(userRef);
132
54
  const baseData = buildBaseData(user, allExtras);
133
55
 
134
- const docData = !userDoc.exists()
135
- ? buildCreateData(baseData, allExtras)
56
+ const docData = !userDoc.exists()
57
+ ? buildCreateData(baseData, userDocumentConfig.extraFields, allExtras)
136
58
  : buildUpdateData(baseData, allExtras);
137
59
 
138
60
  await setDoc(userRef, docData, { merge: true });
@@ -0,0 +1,107 @@
1
+ /**
2
+ * User Document Builder Utility
3
+ * Builds user document data for Firestore
4
+ */
5
+
6
+ import { serverTimestamp } from "firebase/firestore";
7
+ import type {
8
+ UserDocumentUser,
9
+ UserDocumentExtras,
10
+ } from "../../infrastructure/services/UserDocument.types";
11
+
12
+ /**
13
+ * Gets the sign-up method from user provider data
14
+ */
15
+ export function getSignUpMethod(user: UserDocumentUser): string | undefined {
16
+ if (user.isAnonymous) return "anonymous";
17
+ if (user.email) {
18
+ const providerData = (
19
+ user as unknown as { providerData?: { providerId: string }[] }
20
+ ).providerData;
21
+ if (providerData && providerData.length > 0) {
22
+ const providerId = providerData[0].providerId;
23
+ if (providerId === "google.com") return "google";
24
+ if (providerId === "apple.com") return "apple";
25
+ if (providerId === "password") return "email";
26
+ }
27
+ return "email";
28
+ }
29
+ return undefined;
30
+ }
31
+
32
+ /**
33
+ * Builds base user data from user object and extras
34
+ */
35
+ export function buildBaseData(
36
+ user: UserDocumentUser,
37
+ extras?: UserDocumentExtras,
38
+ ): Record<string, unknown> {
39
+ const data: Record<string, unknown> = {
40
+ displayName: user.displayName,
41
+ email: user.email,
42
+ photoURL: user.photoURL,
43
+ isAnonymous: user.isAnonymous,
44
+ };
45
+
46
+ const fields: (keyof UserDocumentExtras)[] = [
47
+ 'deviceId', 'platform', 'deviceModel', 'deviceBrand',
48
+ 'osVersion', 'appVersion', 'buildNumber', 'locale', 'timezone'
49
+ ];
50
+
51
+ fields.forEach(field => {
52
+ const val = extras?.[field];
53
+ if (val) data[field] = val;
54
+ });
55
+
56
+ return data;
57
+ }
58
+
59
+ /**
60
+ * Builds user document data for creation
61
+ */
62
+ export function buildCreateData(
63
+ baseData: Record<string, unknown>,
64
+ extraFields: Record<string, unknown> | undefined,
65
+ extras?: UserDocumentExtras,
66
+ ): Record<string, unknown> {
67
+ const createData: Record<string, unknown> = {
68
+ ...baseData,
69
+ ...extraFields,
70
+ createdAt: serverTimestamp(),
71
+ updatedAt: serverTimestamp(),
72
+ lastLoginAt: serverTimestamp(),
73
+ };
74
+
75
+ if (extras?.previousAnonymousUserId) {
76
+ createData.previousAnonymousUserId = extras.previousAnonymousUserId;
77
+ createData.convertedFromAnonymous = true;
78
+ createData.convertedAt = serverTimestamp();
79
+ }
80
+
81
+ if (extras?.signUpMethod) createData.signUpMethod = extras.signUpMethod;
82
+
83
+ return createData;
84
+ }
85
+
86
+ /**
87
+ * Builds user document data for update
88
+ */
89
+ export function buildUpdateData(
90
+ baseData: Record<string, unknown>,
91
+ extras?: UserDocumentExtras,
92
+ ): Record<string, unknown> {
93
+ const updateData: Record<string, unknown> = {
94
+ ...baseData,
95
+ lastLoginAt: serverTimestamp(),
96
+ updatedAt: serverTimestamp(),
97
+ };
98
+
99
+ if (extras?.previousAnonymousUserId) {
100
+ updateData.previousAnonymousUserId = extras.previousAnonymousUserId;
101
+ updateData.convertedFromAnonymous = true;
102
+ updateData.convertedAt = serverTimestamp();
103
+ if (extras?.signUpMethod) updateData.signUpMethod = extras.signUpMethod;
104
+ }
105
+
106
+ return updateData;
107
+ }
@@ -4,10 +4,9 @@
4
4
  * Generic hook - reauthentication is handled via callback from calling app
5
5
  */
6
6
 
7
- /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument */
8
7
  import { useCallback, useState } from "react";
9
8
  import { useAuth } from "./useAuth";
10
- import { deleteCurrentUser } from "@umituz/react-native-firebase";
9
+ import { handleAccountDeletion } from "../utils/accountDeleteHandler.util";
11
10
 
12
11
  export interface UseAccountManagementOptions {
13
12
  /**
@@ -60,110 +59,7 @@ export const useAccountManagement = (
60
59
  setIsDeletingAccount(true);
61
60
 
62
61
  try {
63
- if (__DEV__) {
64
- console.log("[useAccountManagement] Calling deleteCurrentUser with autoReauthenticate: true");
65
- }
66
-
67
- const result = await deleteCurrentUser({ autoReauthenticate: true });
68
-
69
- if (__DEV__) {
70
- console.log("[useAccountManagement] First delete attempt result:", result);
71
- }
72
-
73
- if (result.success) {
74
- if (__DEV__) {
75
- console.log("[useAccountManagement] ✅ Delete successful on first attempt");
76
- }
77
- return;
78
- }
79
-
80
- // If reauthentication required
81
- if (result.error?.requiresReauth) {
82
- // Handle password-based reauth
83
- if (result.error.code === "auth/password-reauth-required" && onPasswordRequired) {
84
- if (__DEV__) {
85
- console.log("[useAccountManagement] Password reauth required, prompting user");
86
- }
87
-
88
- const password = await onPasswordRequired();
89
-
90
- if (password) {
91
- if (__DEV__) {
92
- console.log("[useAccountManagement] Password provided, retrying delete");
93
- }
94
-
95
- const retryResult = await deleteCurrentUser({
96
- autoReauthenticate: false,
97
- password,
98
- });
99
-
100
- if (__DEV__) {
101
- console.log("[useAccountManagement] Retry delete with password result:", retryResult);
102
- }
103
-
104
- if (retryResult.success) {
105
- if (__DEV__) {
106
- console.log("[useAccountManagement] ✅ Delete successful after password reauth");
107
- }
108
- return;
109
- }
110
-
111
- if (retryResult.error) {
112
- throw new Error(retryResult.error.message);
113
- }
114
- } else {
115
- if (__DEV__) {
116
- console.log("[useAccountManagement] Password prompt cancelled");
117
- }
118
- throw new Error("Password required to delete account");
119
- }
120
- }
121
-
122
- // Handle Google/Apple reauth
123
- if (onReauthRequired) {
124
- if (__DEV__) {
125
- console.log("[useAccountManagement] Reauth required, calling onReauthRequired callback");
126
- }
127
-
128
- const reauthSuccess = await onReauthRequired();
129
-
130
- if (__DEV__) {
131
- console.log("[useAccountManagement] onReauthRequired result:", reauthSuccess);
132
- }
133
-
134
- if (reauthSuccess) {
135
- if (__DEV__) {
136
- console.log("[useAccountManagement] Retrying delete after reauth");
137
- }
138
-
139
- const retryResult = await deleteCurrentUser({
140
- autoReauthenticate: false,
141
- });
142
-
143
- if (__DEV__) {
144
- console.log("[useAccountManagement] Retry delete result:", retryResult);
145
- }
146
-
147
- if (retryResult.success) {
148
- if (__DEV__) {
149
- console.log("[useAccountManagement] ✅ Delete successful after reauth");
150
- }
151
- return;
152
- }
153
-
154
- if (retryResult.error) {
155
- throw new Error(retryResult.error.message);
156
- }
157
- }
158
- }
159
- }
160
-
161
- if (result.error) {
162
- if (__DEV__) {
163
- console.log("[useAccountManagement] ❌ Delete failed:", result.error);
164
- }
165
- throw new Error(result.error.message);
166
- }
62
+ await handleAccountDeletion({ onReauthRequired, onPasswordRequired });
167
63
  } finally {
168
64
  setIsDeletingAccount(false);
169
65
  }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Account Delete Handler Utility
3
+ * Handles reauthentication flow for account deletion
4
+ */
5
+
6
+ import { deleteCurrentUser } from "@umituz/react-native-firebase";
7
+
8
+ export interface DeleteAccountCallbacks {
9
+ onReauthRequired?: () => Promise<boolean>;
10
+ onPasswordRequired?: () => Promise<string | null>;
11
+ }
12
+
13
+ export interface DeleteAccountOptions {
14
+ autoReauthenticate: boolean;
15
+ password?: string;
16
+ }
17
+
18
+ /**
19
+ * Handles account deletion with reauthentication retry logic
20
+ */
21
+ export async function handleAccountDeletion(
22
+ callbacks: DeleteAccountCallbacks
23
+ ): Promise<void> {
24
+ if (__DEV__) {
25
+ console.log("[AccountDeleteHandler] Starting deletion with auto-reauthenticate");
26
+ }
27
+
28
+ const result = await deleteCurrentUser({ autoReauthenticate: true });
29
+
30
+ if (__DEV__) {
31
+ console.log("[AccountDeleteHandler] First attempt result:", result);
32
+ }
33
+
34
+ if (result.success) {
35
+ if (__DEV__) {
36
+ console.log("[AccountDeleteHandler] Delete successful");
37
+ }
38
+ return;
39
+ }
40
+
41
+ if (result.error?.requiresReauth) {
42
+ await handleReauthentication(result, callbacks);
43
+ return;
44
+ }
45
+
46
+ if (result.error) {
47
+ if (__DEV__) {
48
+ console.log("[AccountDeleteHandler] Delete failed:", result.error);
49
+ }
50
+ throw new Error(result.error.message);
51
+ }
52
+ }
53
+
54
+ async function handleReauthentication(
55
+ initialResult: Awaited<ReturnType<typeof deleteCurrentUser>>,
56
+ callbacks: DeleteAccountCallbacks
57
+ ): Promise<void> {
58
+ const { onReauthRequired, onPasswordRequired } = callbacks;
59
+
60
+ // Handle password reauth
61
+ if (initialResult.error?.code === "auth/password-reauth-required" && onPasswordRequired) {
62
+ await retryWithPassword(onPasswordRequired);
63
+ return;
64
+ }
65
+
66
+ // Handle social auth reauth
67
+ if (onReauthRequired) {
68
+ await retryWithSocialAuth(onReauthRequired);
69
+ return;
70
+ }
71
+ }
72
+
73
+ async function retryWithPassword(onPasswordRequired: () => Promise<string | null>): Promise<void> {
74
+ if (__DEV__) {
75
+ console.log("[AccountDeleteHandler] Prompting for password");
76
+ }
77
+
78
+ const password = await onPasswordRequired();
79
+
80
+ if (!password) {
81
+ if (__DEV__) {
82
+ console.log("[AccountDeleteHandler] Password prompt cancelled");
83
+ }
84
+ throw new Error("Password required to delete account");
85
+ }
86
+
87
+ if (__DEV__) {
88
+ console.log("[AccountDeleteHandler] Retrying with password");
89
+ }
90
+
91
+ const result = await deleteCurrentUser({
92
+ autoReauthenticate: false,
93
+ password,
94
+ });
95
+
96
+ if (__DEV__) {
97
+ console.log("[AccountDeleteHandler] Password retry result:", result);
98
+ }
99
+
100
+ if (result.success) {
101
+ if (__DEV__) {
102
+ console.log("[AccountDeleteHandler] Delete successful after password reauth");
103
+ }
104
+ return;
105
+ }
106
+
107
+ if (result.error) {
108
+ throw new Error(result.error.message);
109
+ }
110
+ }
111
+
112
+ async function retryWithSocialAuth(onReauthRequired: () => Promise<boolean>): Promise<void> {
113
+ if (__DEV__) {
114
+ console.log("[AccountDeleteHandler] Requesting social auth reauth");
115
+ }
116
+
117
+ const reauthSuccess = await onReauthRequired();
118
+
119
+ if (__DEV__) {
120
+ console.log("[AccountDeleteHandler] Reauth result:", reauthSuccess);
121
+ }
122
+
123
+ if (!reauthSuccess) {
124
+ throw new Error("Reauthentication required to delete account");
125
+ }
126
+
127
+ if (__DEV__) {
128
+ console.log("[AccountDeleteHandler] Retrying deletion after reauth");
129
+ }
130
+
131
+ const result = await deleteCurrentUser({ autoReauthenticate: false });
132
+
133
+ if (__DEV__) {
134
+ console.log("[AccountDeleteHandler] Reauth retry result:", result);
135
+ }
136
+
137
+ if (result.success) {
138
+ if (__DEV__) {
139
+ console.log("[AccountDeleteHandler] Delete successful after reauth");
140
+ }
141
+ return;
142
+ }
143
+
144
+ if (result.error) {
145
+ throw new Error(result.error.message);
146
+ }
147
+ }