@umituz/react-native-auth 3.6.76 → 3.6.77
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/infrastructure/providers/FirebaseAuthProvider.ts +11 -3
- package/src/infrastructure/utils/AuthErrorMapper.ts +18 -74
- package/src/infrastructure/utils/error/errorCodeMapping.constants.ts +120 -0
- package/src/infrastructure/utils/listener/listenerLifecycle.util.ts +42 -27
- package/src/presentation/components/LoginForm.tsx +10 -25
- package/src/presentation/components/RegisterForm.tsx +23 -43
- package/src/presentation/components/form/FormEmailInput.tsx +60 -0
- package/src/presentation/components/form/FormPasswordInput.tsx +62 -0
- package/src/presentation/components/form/FormTextInput.tsx +60 -0
- package/src/presentation/components/form/index.ts +6 -0
- package/src/presentation/hooks/useAccountManagement.ts +32 -2
- package/src/presentation/hooks/useAuth.ts +9 -3
- package/src/presentation/hooks/useAuthBottomSheet.ts +8 -6
- package/src/presentation/hooks/useLoginForm.ts +30 -17
- package/src/presentation/hooks/useRegisterForm.ts +74 -59
- package/src/presentation/stores/authStore.ts +13 -5
- package/src/presentation/stores/initializeAuthListener.ts +9 -3
- package/src/presentation/utils/authTransition.util.ts +13 -2
- package/src/presentation/utils/form/useFormField.hook.ts +97 -0
- package/src/presentation/utils/form/usePasswordValidation.hook.ts +87 -0
- package/src/presentation/utils/getAuthErrorMessage.ts +17 -40
- package/src/presentation/utils/socialAuthHandler.util.ts +20 -37
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Form Field Hook
|
|
3
|
+
* Provides reusable form field state management with automatic error clearing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useCallback, useState } from "react";
|
|
7
|
+
|
|
8
|
+
export interface UseFormFieldOptions {
|
|
9
|
+
clearLocalError?: () => void;
|
|
10
|
+
fieldsToClear?: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UseFormFieldResult {
|
|
14
|
+
value: string;
|
|
15
|
+
error: string | null;
|
|
16
|
+
setValue: (value: string) => void;
|
|
17
|
+
setError: (error: string | null) => void;
|
|
18
|
+
clearError: () => void;
|
|
19
|
+
handleChange: (text: string) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Hook for managing a single form field with automatic error clearing
|
|
24
|
+
* @param initialValue - Initial field value
|
|
25
|
+
* @param setError - Function to set field error in parent state
|
|
26
|
+
* @param options - Additional options
|
|
27
|
+
* @returns Field state and handlers
|
|
28
|
+
*/
|
|
29
|
+
export function useFormField(
|
|
30
|
+
initialValue: string,
|
|
31
|
+
setError: (error: string | null) => void,
|
|
32
|
+
options?: UseFormFieldOptions
|
|
33
|
+
): UseFormFieldResult {
|
|
34
|
+
const [value, setValue] = useState(initialValue);
|
|
35
|
+
const [error, setLocalError] = useState<string | null>(null);
|
|
36
|
+
|
|
37
|
+
const clearError = useCallback(() => {
|
|
38
|
+
setLocalError(null);
|
|
39
|
+
setError(null);
|
|
40
|
+
options?.clearLocalError?.();
|
|
41
|
+
}, [setError, options]);
|
|
42
|
+
|
|
43
|
+
const handleChange = useCallback(
|
|
44
|
+
(text: string) => {
|
|
45
|
+
setValue(text);
|
|
46
|
+
if (error || options?.clearLocalError) {
|
|
47
|
+
clearError();
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
[error, options, clearError]
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
value,
|
|
55
|
+
error,
|
|
56
|
+
setValue,
|
|
57
|
+
setError: setLocalError,
|
|
58
|
+
clearError,
|
|
59
|
+
handleChange,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Hook for managing multiple form fields
|
|
65
|
+
* @param initialFields - Initial field values
|
|
66
|
+
* @param setFieldErrors - Function to set field errors in parent state
|
|
67
|
+
* @param options - Additional options
|
|
68
|
+
* @returns Field states and handlers
|
|
69
|
+
*/
|
|
70
|
+
export function useFormFields<T extends Record<string, string>>(
|
|
71
|
+
initialFields: T,
|
|
72
|
+
setFieldErrors: ((errors: Partial<Record<string, string>> | ((prev: Partial<Record<string, string>>) => Partial<Record<string, string>>)) => void) | null,
|
|
73
|
+
options?: UseFormFieldOptions
|
|
74
|
+
) {
|
|
75
|
+
type FieldKey = keyof T;
|
|
76
|
+
|
|
77
|
+
const [fields, setFields] = useState<T>(initialFields);
|
|
78
|
+
|
|
79
|
+
const updateField = useCallback(
|
|
80
|
+
(field: FieldKey, value: string) => {
|
|
81
|
+
setFields((prev) => ({ ...prev, [field]: value }));
|
|
82
|
+
// Note: setFieldErrors is handled externally by form hooks
|
|
83
|
+
options?.clearLocalError?.();
|
|
84
|
+
},
|
|
85
|
+
[options]
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const resetFields = useCallback(() => {
|
|
89
|
+
setFields(initialFields);
|
|
90
|
+
}, [initialFields]);
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
fields,
|
|
94
|
+
updateField,
|
|
95
|
+
resetFields,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Password Validation Hook
|
|
3
|
+
* Provides reusable password validation logic with requirements tracking
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useMemo } from "react";
|
|
7
|
+
import {
|
|
8
|
+
validatePasswordForRegister,
|
|
9
|
+
validatePasswordConfirmation,
|
|
10
|
+
type PasswordRequirements,
|
|
11
|
+
type ValidationResult,
|
|
12
|
+
} from "../../../infrastructure/utils/AuthValidation";
|
|
13
|
+
import type { PasswordConfig } from "../../../domain/value-objects/AuthConfig";
|
|
14
|
+
|
|
15
|
+
export interface UsePasswordValidationResult {
|
|
16
|
+
passwordRequirements: PasswordRequirements;
|
|
17
|
+
passwordsMatch: boolean;
|
|
18
|
+
isValid: boolean;
|
|
19
|
+
confirmationError: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface UsePasswordValidationOptions {
|
|
23
|
+
passwordConfig?: PasswordConfig;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Hook for password validation with requirements tracking
|
|
28
|
+
* @param password - Password value
|
|
29
|
+
* @param confirmPassword - Confirm password value
|
|
30
|
+
* @param options - Validation options
|
|
31
|
+
* @returns Password validation state
|
|
32
|
+
*/
|
|
33
|
+
export function usePasswordValidation(
|
|
34
|
+
password: string,
|
|
35
|
+
confirmPassword: string,
|
|
36
|
+
options?: UsePasswordValidationOptions
|
|
37
|
+
): UsePasswordValidationResult {
|
|
38
|
+
const config = options?.passwordConfig;
|
|
39
|
+
|
|
40
|
+
const passwordRequirements = useMemo((): PasswordRequirements => {
|
|
41
|
+
if (!password || !config) {
|
|
42
|
+
return { hasMinLength: false };
|
|
43
|
+
}
|
|
44
|
+
const result = validatePasswordForRegister(password, config);
|
|
45
|
+
return result.requirements;
|
|
46
|
+
}, [password, config]);
|
|
47
|
+
|
|
48
|
+
const passwordsMatch = useMemo(() => {
|
|
49
|
+
if (!password || !confirmPassword) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return password === confirmPassword;
|
|
53
|
+
}, [password, confirmPassword]);
|
|
54
|
+
|
|
55
|
+
const confirmationError = useMemo(() => {
|
|
56
|
+
if (!confirmPassword) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const result = validatePasswordConfirmation(password, confirmPassword);
|
|
60
|
+
return result.error ?? null;
|
|
61
|
+
}, [password, confirmPassword]);
|
|
62
|
+
|
|
63
|
+
const isValid = useMemo(() => {
|
|
64
|
+
return passwordRequirements.hasMinLength && passwordsMatch;
|
|
65
|
+
}, [passwordRequirements, passwordsMatch]);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
passwordRequirements,
|
|
69
|
+
passwordsMatch,
|
|
70
|
+
isValid,
|
|
71
|
+
confirmationError,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Hook for login password validation (simpler, no requirements)
|
|
77
|
+
* @param password - Password value
|
|
78
|
+
* @returns Validation result
|
|
79
|
+
*/
|
|
80
|
+
export function useLoginPasswordValidation(password: string): ValidationResult {
|
|
81
|
+
return useMemo(() => {
|
|
82
|
+
if (!password || password.length === 0) {
|
|
83
|
+
return { isValid: false, error: "auth.validation.passwordRequired" };
|
|
84
|
+
}
|
|
85
|
+
return { isValid: true };
|
|
86
|
+
}, [password]);
|
|
87
|
+
}
|
|
@@ -32,52 +32,29 @@ export function getAuthErrorLocalizationKey(error: unknown): string {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
// Check error name for specific error types
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (error.name === "AuthNetworkError") {
|
|
51
|
-
return "auth.errors.networkError";
|
|
52
|
-
}
|
|
53
|
-
if (error.name === "AuthConfigurationError") {
|
|
54
|
-
return "auth.errors.configurationError";
|
|
55
|
-
}
|
|
56
|
-
if (error.name === "AuthInitializationError") {
|
|
57
|
-
return "auth.errors.authNotInitialized";
|
|
35
|
+
const errorNameMap: Record<string, string> = {
|
|
36
|
+
AuthInvalidEmailError: "auth.errors.invalidEmail",
|
|
37
|
+
AuthWeakPasswordError: "auth.errors.weakPassword",
|
|
38
|
+
AuthUserNotFoundError: "auth.errors.userNotFound",
|
|
39
|
+
AuthWrongPasswordError: "auth.errors.wrongPassword",
|
|
40
|
+
AuthEmailAlreadyInUseError: "auth.errors.emailAlreadyInUse",
|
|
41
|
+
AuthNetworkError: "auth.errors.networkError",
|
|
42
|
+
AuthConfigurationError: "auth.errors.configurationError",
|
|
43
|
+
AuthInitializationError: "auth.errors.authNotInitialized",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// First check by error name (most specific)
|
|
47
|
+
const mappedByName = errorNameMap[error.name];
|
|
48
|
+
if (mappedByName) {
|
|
49
|
+
return mappedByName;
|
|
58
50
|
}
|
|
59
51
|
|
|
60
|
-
//
|
|
52
|
+
// Then check by error code
|
|
61
53
|
if (code && errorCodeMap[code]) {
|
|
62
54
|
return errorCodeMap[code];
|
|
63
55
|
}
|
|
64
56
|
|
|
65
|
-
//
|
|
66
|
-
const message = error.message.toLowerCase();
|
|
67
|
-
if (message.includes("too many requests")) {
|
|
68
|
-
return "auth.errors.tooManyRequests";
|
|
69
|
-
}
|
|
70
|
-
if (message.includes("user account has been disabled") || message.includes("user disabled")) {
|
|
71
|
-
return "auth.errors.userDisabled";
|
|
72
|
-
}
|
|
73
|
-
if (message.includes("not properly configured") || message.includes("configuration")) {
|
|
74
|
-
return "auth.errors.configurationError";
|
|
75
|
-
}
|
|
76
|
-
if (message.includes("not enabled") || message.includes("operation not allowed")) {
|
|
77
|
-
return "auth.errors.operationNotAllowed";
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Default to unknown error
|
|
57
|
+
// Default to unknown error - don't leak system information through error messages
|
|
81
58
|
return "auth.errors.unknownError";
|
|
82
59
|
}
|
|
83
60
|
|
|
@@ -20,34 +20,24 @@ export interface SocialAuthConfig {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Hook for managing social auth loading
|
|
23
|
+
* Hook for managing a single social auth provider's loading state
|
|
24
|
+
* @returns Loading state and handler creator
|
|
24
25
|
*/
|
|
25
26
|
export function useSocialAuthLoading() {
|
|
26
27
|
const [googleLoading, setGoogleLoading] = useState(false);
|
|
27
28
|
const [appleLoading, setAppleLoading] = useState(false);
|
|
28
29
|
|
|
29
|
-
const
|
|
30
|
-
(
|
|
30
|
+
const createHandler = useCallback(
|
|
31
|
+
(setter: (loading: boolean) => void, signInHandler?: () => Promise<void>) => {
|
|
31
32
|
return async () => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
await handler();
|
|
35
|
-
} finally {
|
|
36
|
-
setGoogleLoading(false);
|
|
33
|
+
if (!signInHandler) {
|
|
34
|
+
throw new Error("Sign-in handler not available");
|
|
37
35
|
}
|
|
38
|
-
|
|
39
|
-
},
|
|
40
|
-
[]
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const createAppleHandler = useCallback(
|
|
44
|
-
(handler: () => Promise<void>) => {
|
|
45
|
-
return async () => {
|
|
46
|
-
setAppleLoading(true);
|
|
36
|
+
setter(true);
|
|
47
37
|
try {
|
|
48
|
-
await
|
|
38
|
+
await signInHandler();
|
|
49
39
|
} finally {
|
|
50
|
-
|
|
40
|
+
setter(false);
|
|
51
41
|
}
|
|
52
42
|
};
|
|
53
43
|
},
|
|
@@ -59,8 +49,7 @@ export function useSocialAuthLoading() {
|
|
|
59
49
|
appleLoading,
|
|
60
50
|
setGoogleLoading,
|
|
61
51
|
setAppleLoading,
|
|
62
|
-
|
|
63
|
-
createAppleHandler,
|
|
52
|
+
createHandler,
|
|
64
53
|
};
|
|
65
54
|
}
|
|
66
55
|
|
|
@@ -86,21 +75,15 @@ export function determineEnabledProviders(
|
|
|
86
75
|
}
|
|
87
76
|
|
|
88
77
|
/**
|
|
89
|
-
*
|
|
78
|
+
* Select the appropriate sign-in handler from external and internal options
|
|
79
|
+
* @param externalHandler - External handler provided by parent
|
|
80
|
+
* @param internalHandler - Internal handler from auth hook
|
|
81
|
+
* @returns The selected handler or undefined
|
|
90
82
|
*/
|
|
91
|
-
export function
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
): SocialAuthHandlers {
|
|
97
|
-
const googleHandler = googleSignIn || internalGoogleHandler;
|
|
98
|
-
const appleHandler = appleSignIn || internalAppleHandler;
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
googleLoading: false,
|
|
102
|
-
appleLoading: false,
|
|
103
|
-
handleGoogleSignIn: googleHandler || (async () => {}),
|
|
104
|
-
handleAppleSignIn: appleHandler || (async () => {}),
|
|
105
|
-
};
|
|
83
|
+
export function selectSignInHandler(
|
|
84
|
+
externalHandler?: () => Promise<void>,
|
|
85
|
+
internalHandler?: () => Promise<void>
|
|
86
|
+
): (() => Promise<void>) | undefined {
|
|
87
|
+
return externalHandler ?? internalHandler;
|
|
106
88
|
}
|
|
89
|
+
|