@umituz/react-native-auth 4.2.15 → 4.2.16
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/utils/AuthValidation.ts +6 -3
- package/src/infrastructure/utils/validation/sanitization.ts +10 -1
- package/src/presentation/components/LoginForm.tsx +1 -1
- package/src/presentation/components/RegisterForm.tsx +1 -1
- package/src/presentation/screens/PasswordPromptScreen.tsx +2 -1
- package/src/presentation/stores/initializeAuthListener.ts +4 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-auth",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.16",
|
|
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",
|
|
@@ -34,7 +34,8 @@ export function validateEmail(
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export function validatePasswordForLogin(password: string): ValidationResult {
|
|
37
|
-
|
|
37
|
+
// Don't trim passwords - whitespace may be intentional
|
|
38
|
+
if (!password || password.length === 0) return { isValid: false, error: "auth.validation.passwordRequired" };
|
|
38
39
|
return { isValid: true };
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -42,7 +43,8 @@ export function validatePasswordForRegister(
|
|
|
42
43
|
password: string,
|
|
43
44
|
config: PasswordConfig,
|
|
44
45
|
): PasswordStrengthResult {
|
|
45
|
-
|
|
46
|
+
// Don't trim passwords - whitespace may be intentional
|
|
47
|
+
if (!password || password.length === 0) {
|
|
46
48
|
return { isValid: false, error: "auth.validation.passwordRequired", requirements: { hasMinLength: false } };
|
|
47
49
|
}
|
|
48
50
|
|
|
@@ -56,7 +58,8 @@ export function validatePasswordForRegister(
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
export function validatePasswordConfirmation(password: string, confirm: string): ValidationResult {
|
|
59
|
-
|
|
61
|
+
// Don't trim passwords - whitespace may be intentional
|
|
62
|
+
if (!confirm || confirm.length === 0) return { isValid: false, error: "auth.validation.confirmPasswordRequired" };
|
|
60
63
|
if (password !== confirm) return { isValid: false, error: "auth.validation.passwordsDoNotMatch" };
|
|
61
64
|
return { isValid: true };
|
|
62
65
|
}
|
|
@@ -14,31 +14,39 @@ export const SECURITY_LIMITS = {
|
|
|
14
14
|
export type SecurityLimitKey = keyof typeof SECURITY_LIMITS;
|
|
15
15
|
|
|
16
16
|
export const sanitizeWhitespace = (input: string): string => {
|
|
17
|
+
if (!input) return '';
|
|
17
18
|
return input.trim().replace(/\s+/g, ' ');
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
export const sanitizeEmail = (email: string): string => {
|
|
22
|
+
if (!email) return '';
|
|
21
23
|
const trimmed = email.trim().toLowerCase();
|
|
22
24
|
return trimmed.substring(0, SECURITY_LIMITS.EMAIL_MAX_LENGTH);
|
|
23
25
|
};
|
|
24
26
|
|
|
25
27
|
export const sanitizePassword = (password: string): string => {
|
|
26
|
-
|
|
28
|
+
// Don't trim passwords - leading/trailing whitespace may be intentional
|
|
29
|
+
// Only enforce max length for security
|
|
30
|
+
if (!password) return '';
|
|
31
|
+
return password.substring(0, SECURITY_LIMITS.PASSWORD_MAX_LENGTH);
|
|
27
32
|
};
|
|
28
33
|
|
|
29
34
|
export const sanitizeName = (name: string): string => {
|
|
35
|
+
if (!name) return '';
|
|
30
36
|
const trimmed = sanitizeWhitespace(name);
|
|
31
37
|
const noTags = trimmed.replace(/<[^>]*>/g, '');
|
|
32
38
|
return noTags.substring(0, SECURITY_LIMITS.NAME_MAX_LENGTH);
|
|
33
39
|
};
|
|
34
40
|
|
|
35
41
|
export const sanitizeText = (text: string): string => {
|
|
42
|
+
if (!text) return '';
|
|
36
43
|
const trimmed = sanitizeWhitespace(text);
|
|
37
44
|
const noTags = trimmed.replace(/<[^>]*>/g, '');
|
|
38
45
|
return noTags.substring(0, SECURITY_LIMITS.GENERAL_TEXT_MAX_LENGTH);
|
|
39
46
|
};
|
|
40
47
|
|
|
41
48
|
export const containsDangerousChars = (input: string): boolean => {
|
|
49
|
+
if (!input) return false;
|
|
42
50
|
const dangerousPatterns = [
|
|
43
51
|
/<script/i,
|
|
44
52
|
/javascript:/i,
|
|
@@ -54,6 +62,7 @@ export const isWithinLengthLimit = (
|
|
|
54
62
|
maxLength: number,
|
|
55
63
|
minLength = 0
|
|
56
64
|
): boolean => {
|
|
65
|
+
if (!input) return minLength === 0;
|
|
57
66
|
const length = input.trim().length;
|
|
58
67
|
return length >= minLength && length <= maxLength;
|
|
59
68
|
};
|
|
@@ -68,7 +68,7 @@ export const LoginForm: React.FC<LoginFormProps> = ({
|
|
|
68
68
|
<AtomicButton
|
|
69
69
|
variant="primary"
|
|
70
70
|
onPress={() => { void handleSignIn(); }}
|
|
71
|
-
disabled={loading || !email.trim() || !password
|
|
71
|
+
disabled={loading || !email.trim() || !password}
|
|
72
72
|
fullWidth
|
|
73
73
|
style={styles.signInButton}
|
|
74
74
|
>
|
|
@@ -128,7 +128,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
|
|
|
128
128
|
<AtomicButton
|
|
129
129
|
variant="primary"
|
|
130
130
|
onPress={() => { void handleSignUp(); }}
|
|
131
|
-
disabled={loading || !email.trim() || !password
|
|
131
|
+
disabled={loading || !email.trim() || !password || !confirmPassword}
|
|
132
132
|
fullWidth
|
|
133
133
|
style={styles.signUpButton}
|
|
134
134
|
>
|
|
@@ -49,7 +49,8 @@ export const PasswordPromptScreen: React.FC<PasswordPromptScreenProps> = ({
|
|
|
49
49
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
50
50
|
console.log("[PasswordPromptScreen] handleConfirm called, password length:", password.length);
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
// Don't trim password - whitespace may be intentional
|
|
53
|
+
if (!password) {
|
|
53
54
|
setError('Password is required');
|
|
54
55
|
return;
|
|
55
56
|
}
|
|
@@ -34,14 +34,16 @@ export function initializeAuthListener(
|
|
|
34
34
|
if (!startInitialization()) {
|
|
35
35
|
// Either already initializing or initialized - handle accordingly
|
|
36
36
|
if (isListenerInitialized()) {
|
|
37
|
-
|
|
37
|
+
const unsubscribe = handleExistingInitialization();
|
|
38
|
+
return unsubscribe || handleInitializationInProgress();
|
|
38
39
|
}
|
|
39
40
|
return handleInitializationInProgress();
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
// If already initialized, increment ref count and return unsubscribe
|
|
43
44
|
if (isListenerInitialized()) {
|
|
44
|
-
|
|
45
|
+
const unsubscribe = handleExistingInitialization();
|
|
46
|
+
return unsubscribe || (() => {});
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
const auth = getFirebaseAuth();
|