@umituz/react-native-auth 3.6.55 → 3.6.56

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.6.55",
3
+ "version": "3.6.56",
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",
package/src/index.ts CHANGED
@@ -101,6 +101,7 @@ export {
101
101
  validateAge,
102
102
  } from './infrastructure/utils/validation/NumberValidators';
103
103
  export {
104
+ calculateAge,
104
105
  validateDateOfBirth,
105
106
  validateDateRange,
106
107
  } from './infrastructure/utils/validation/DateValidators';
@@ -222,7 +223,7 @@ export type { UserType, AuthState, AuthActions } from './presentation/stores/aut
222
223
  export type { AuthListenerOptions } from './types/auth-store.types';
223
224
 
224
225
  // UTILITIES
225
- export { getAuthErrorLocalizationKey } from './presentation/utils/getAuthErrorMessage';
226
+ export { getAuthErrorLocalizationKey, resolveErrorMessage } from './presentation/utils/getAuthErrorMessage';
226
227
 
227
228
  // App Service Helper (for configureAppServices)
228
229
  export {
@@ -74,7 +74,7 @@ export class AuthService implements IAuthService {
74
74
  authEventService.emitUserAuthenticated(user.uid);
75
75
  return user;
76
76
  } catch (error) {
77
- authTracker.logOperationError("sign-up", error, { email: params.email });
77
+ authTracker.logOperationError("Sign up", error, { email: params.email });
78
78
  throw error;
79
79
  }
80
80
  }
@@ -88,7 +88,7 @@ export class AuthService implements IAuthService {
88
88
  authEventService.emitUserAuthenticated(user.uid);
89
89
  return user;
90
90
  } catch (error) {
91
- authTracker.logOperationError("sign-in", error, { email: params.email });
91
+ authTracker.logOperationError("Sign in", error, { email: params.email });
92
92
  throw error;
93
93
  }
94
94
  }
@@ -100,7 +100,7 @@ export class AuthService implements IAuthService {
100
100
  await this.clearAnonymousModeIfNeeded();
101
101
  authTracker.logOperationSuccess("Sign out");
102
102
  } catch (error) {
103
- authTracker.logOperationError("sign-out", error);
103
+ authTracker.logOperationError("Sign out", error);
104
104
  throw error;
105
105
  }
106
106
  }
@@ -77,12 +77,12 @@ async function doInitializeAuth(
77
77
  collectExtras: collectExtras || collectDeviceExtras,
78
78
  });
79
79
 
80
+ let authServiceInitFailed = false;
80
81
  try {
81
82
  await initializeAuthService(auth, authConfig, storageProvider);
82
83
  } catch (error) {
83
- if (__DEV__) {
84
- console.warn("[initializeAuth] Auth service init failed, continuing:", error);
85
- }
84
+ authServiceInitFailed = true;
85
+ console.warn("[initializeAuth] Auth service init failed, continuing:", error);
86
86
  }
87
87
 
88
88
  const handleAuthStateChange = createAuthStateHandler(conversionState, {
@@ -101,8 +101,12 @@ async function doInitializeAuth(
101
101
  },
102
102
  });
103
103
 
104
+ if (authServiceInitFailed) {
105
+ console.warn("[initializeAuth] Auth service initialization failed. Some auth features may not work.");
106
+ }
107
+
104
108
  isInitialized = true;
105
- return { success: true, auth };
109
+ return { success: !authServiceInitFailed, auth };
106
110
  }
107
111
 
108
112
  export function isAuthInitialized(): boolean {
@@ -1,6 +1,7 @@
1
1
  import type { PasswordConfig } from "../../domain/value-objects/AuthConfig";
2
+ import type { ValidationResult } from "./validation/types";
2
3
 
3
- export interface ValidationResult { isValid: boolean; error?: string; }
4
+ export type { ValidationResult };
4
5
  export interface PasswordStrengthResult extends ValidationResult { requirements: PasswordRequirements; }
5
6
  export interface PasswordRequirements {
6
7
  hasMinLength: boolean;
@@ -34,12 +35,14 @@ export function validatePasswordForRegister(
34
35
  password: string,
35
36
  config: PasswordConfig,
36
37
  ): PasswordStrengthResult {
38
+ if (!password) {
39
+ return { isValid: false, error: "auth.validation.passwordRequired", requirements: { hasMinLength: false } };
40
+ }
41
+
37
42
  const req: PasswordRequirements = {
38
43
  hasMinLength: password.length >= config.minLength,
39
44
  };
40
45
 
41
- if (!password) return { isValid: false, error: "auth.validation.passwordRequired", requirements: req };
42
-
43
46
  if (!req.hasMinLength) return { isValid: false, error: "auth.validation.passwordTooShort", requirements: req };
44
47
 
45
48
  return { isValid: true, requirements: req };
@@ -22,7 +22,7 @@ interface FirebaseUserLike {
22
22
  /**
23
23
  * Extract auth provider from Firebase user's providerData
24
24
  */
25
- function extractProvider(user: FirebaseUserLike): AuthProviderType {
25
+ export function extractProvider(user: FirebaseUserLike): AuthProviderType {
26
26
  if (user.isAnonymous) {
27
27
  return "anonymous";
28
28
  }
@@ -8,25 +8,27 @@ import type {
8
8
  UserDocumentUser,
9
9
  UserDocumentExtras,
10
10
  } from "../../infrastructure/services/UserDocument.types";
11
+ import { extractProvider } from "./UserMapper";
12
+ import type { AuthProviderType } from "../../domain/entities/AuthUser";
13
+
14
+ /**
15
+ * Map AuthProviderType to sign-up method string
16
+ */
17
+ const PROVIDER_TO_SIGNUP_METHOD: Record<string, string> = {
18
+ "google.com": "google",
19
+ "apple.com": "apple",
20
+ "password": "email",
21
+ "anonymous": "anonymous",
22
+ };
11
23
 
12
24
  /**
13
25
  * Gets the sign-up method from user provider data
26
+ * Uses extractProvider from UserMapper as single source of truth for provider detection
14
27
  */
15
28
  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 | null }[] }
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;
29
+ const provider: AuthProviderType = extractProvider(user as Parameters<typeof extractProvider>[0]);
30
+ if (provider === "unknown") return user.email ? "email" : undefined;
31
+ return PROVIDER_TO_SIGNUP_METHOD[provider];
30
32
  }
31
33
 
32
34
  /**
@@ -58,6 +60,20 @@ export function buildBaseData(
58
60
  return data;
59
61
  }
60
62
 
63
+ /**
64
+ * Apply anonymous-to-authenticated conversion fields to document data
65
+ */
66
+ function applyConversionFields(data: Record<string, unknown>, extras?: UserDocumentExtras): void {
67
+ if (extras?.previousAnonymousUserId) {
68
+ data.previousAnonymousUserId = extras.previousAnonymousUserId;
69
+ data.convertedFromAnonymous = true;
70
+ data.convertedAt = serverTimestamp();
71
+ }
72
+ if (extras?.signUpMethod) {
73
+ data.signUpMethod = extras.signUpMethod;
74
+ }
75
+ }
76
+
61
77
  /**
62
78
  * Builds user document data for creation
63
79
  */
@@ -74,13 +90,7 @@ export function buildCreateData(
74
90
  lastLoginAt: serverTimestamp(),
75
91
  };
76
92
 
77
- if (extras?.previousAnonymousUserId) {
78
- createData.previousAnonymousUserId = extras.previousAnonymousUserId;
79
- createData.convertedFromAnonymous = true;
80
- createData.convertedAt = serverTimestamp();
81
- }
82
-
83
- if (extras?.signUpMethod) createData.signUpMethod = extras.signUpMethod;
93
+ applyConversionFields(createData, extras);
84
94
 
85
95
  return createData;
86
96
  }
@@ -98,12 +108,7 @@ export function buildUpdateData(
98
108
  updatedAt: serverTimestamp(),
99
109
  };
100
110
 
101
- if (extras?.previousAnonymousUserId) {
102
- updateData.previousAnonymousUserId = extras.previousAnonymousUserId;
103
- updateData.convertedFromAnonymous = true;
104
- updateData.convertedAt = serverTimestamp();
105
- if (extras?.signUpMethod) updateData.signUpMethod = extras.signUpMethod;
106
- }
111
+ applyConversionFields(updateData, extras);
107
112
 
108
113
  return updateData;
109
114
  }
@@ -1,5 +1,18 @@
1
1
  import { ValidationResult } from './types';
2
2
 
3
+ /**
4
+ * Calculate age from a birth date
5
+ */
6
+ export function calculateAge(birthDate: Date): number {
7
+ const today = new Date();
8
+ let age = today.getFullYear() - birthDate.getFullYear();
9
+ const m = today.getMonth() - birthDate.getMonth();
10
+ if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
11
+ age--;
12
+ }
13
+ return age;
14
+ }
15
+
3
16
  /**
4
17
  * Validate date of birth
5
18
  */
@@ -12,12 +25,7 @@ export const validateDateOfBirth = (
12
25
  return { isValid: false, error: "Please enter a valid date" };
13
26
  }
14
27
 
15
- const today = new Date();
16
- let age = today.getFullYear() - date.getFullYear();
17
- const m = today.getMonth() - date.getMonth();
18
- if (m < 0 || (m === 0 && today.getDate() < date.getDate())) {
19
- age--;
20
- }
28
+ const age = calculateAge(date);
21
29
 
22
30
  if (age < minAge) {
23
31
  return { isValid: false, error: errorKey || `You must be at least ${minAge} years old` };
@@ -1,4 +1,5 @@
1
1
  import { ValidationResult } from './types';
2
+ import { calculateAge } from './DateValidators';
2
3
 
3
4
  /**
4
5
  * Validate numeric range
@@ -40,13 +41,7 @@ export const validateAge = (
40
41
  minAge: number,
41
42
  errorKey?: string
42
43
  ): ValidationResult => {
43
- const today = new Date();
44
- let age = today.getFullYear() - birthDate.getFullYear();
45
- const m = today.getMonth() - birthDate.getMonth();
46
- if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
47
- age--;
48
- }
49
-
44
+ const age = calculateAge(birthDate);
50
45
  const isValid = age >= minAge;
51
46
  return {
52
47
  isValid,
@@ -1,6 +1,6 @@
1
1
  import { useState, useCallback } from "react";
2
2
  import { useAuth } from "./useAuth";
3
- import { getAuthErrorLocalizationKey } from "../utils/getAuthErrorMessage";
3
+ import { getAuthErrorLocalizationKey, resolveErrorMessage } from "../utils/getAuthErrorMessage";
4
4
  import { validateEmail, validatePasswordForLogin } from "../../infrastructure/utils/AuthValidation";
5
5
  import { alertService } from "@umituz/react-native-design-system";
6
6
 
@@ -39,7 +39,7 @@ export function useLoginForm(config?: UseLoginFormConfig): UseLoginFormResult {
39
39
  const [localError, setLocalError] = useState<string | null>(null);
40
40
 
41
41
  const getErrorMessage = useCallback((key: string) => {
42
- return translations?.errors?.[key] || key;
42
+ return resolveErrorMessage(key, translations?.errors);
43
43
  }, [translations]);
44
44
 
45
45
  const handleEmailChange = useCallback(
@@ -4,7 +4,7 @@
4
4
  * Apps should use Firebase SDK directly or backend API
5
5
  */
6
6
 
7
- import { useState, useCallback } from "react";
7
+ import { useCallback } from "react";
8
8
  import { useAuth } from "./useAuth";
9
9
  import type { UpdateProfileParams } from "../../domain/entities/UserProfile";
10
10
 
@@ -16,8 +16,6 @@ export interface UseProfileUpdateReturn {
16
16
 
17
17
  export const useProfileUpdate = (): UseProfileUpdateReturn => {
18
18
  const { user } = useAuth();
19
- const [isUpdating] = useState(false);
20
- const [error] = useState<string | null>(null);
21
19
 
22
20
  const updateProfile = useCallback(
23
21
  (_params: UpdateProfileParams) => {
@@ -37,8 +35,8 @@ export const useProfileUpdate = (): UseProfileUpdateReturn => {
37
35
 
38
36
  return {
39
37
  updateProfile,
40
- isUpdating,
41
- error,
38
+ isUpdating: false,
39
+ error: null,
42
40
  };
43
41
  };
44
42
 
@@ -6,7 +6,7 @@ import {
6
6
  } from "../../infrastructure/utils/AuthValidation";
7
7
  import { DEFAULT_PASSWORD_CONFIG } from "../../domain/value-objects/AuthConfig";
8
8
  import { useAuth } from "./useAuth";
9
- import { getAuthErrorLocalizationKey } from "../utils/getAuthErrorMessage";
9
+ import { getAuthErrorLocalizationKey, resolveErrorMessage } from "../utils/getAuthErrorMessage";
10
10
  import type { PasswordRequirements } from "../../infrastructure/utils/AuthValidation";
11
11
  import { alertService } from "@umituz/react-native-design-system";
12
12
 
@@ -60,7 +60,7 @@ export function useRegisterForm(config?: UseRegisterFormConfig): UseRegisterForm
60
60
  }>({});
61
61
 
62
62
  const getErrorMessage = useCallback((key: string) => {
63
- return translations?.errors?.[key] || key;
63
+ return resolveErrorMessage(key, translations?.errors);
64
64
  }, [translations]);
65
65
 
66
66
  const passwordRequirements = useMemo((): PasswordRequirements => {
@@ -98,10 +98,7 @@ export const selectUserType = (state: AuthStore): UserType => {
98
98
  return "none";
99
99
  }
100
100
 
101
- const isAnonymous =
102
- state.firebaseUser?.isAnonymous ?? state.user?.isAnonymous ?? false;
103
-
104
- return isAnonymous ? "anonymous" : "authenticated";
101
+ return selectIsAnonymous(state) ? "anonymous" : "authenticated";
105
102
  };
106
103
 
107
104
  /**
@@ -117,5 +114,5 @@ export const selectIsAuthReady = (state: AuthStore): boolean => {
117
114
  */
118
115
  export const selectIsRegisteredUser = (state: AuthStore): boolean => {
119
116
  if (!state.initialized) return false;
120
- return !!state.user && !state.isAnonymous && !state.user.isAnonymous;
117
+ return selectIsAuthenticated(state);
121
118
  };
@@ -80,3 +80,11 @@ export function getAuthErrorLocalizationKey(error: unknown): string {
80
80
  // Default to unknown error
81
81
  return "auth.errors.unknownError";
82
82
  }
83
+
84
+ /**
85
+ * Resolve an error key to a localized message using the provided error map.
86
+ * Falls back to the key itself if no translation is found.
87
+ */
88
+ export function resolveErrorMessage(key: string, errors?: Record<string, string>): string {
89
+ return errors?.[key] || key;
90
+ }