@umituz/react-native-auth 3.6.60 → 3.6.62
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/application/ports/IAuthRepository.ts +3 -3
- package/src/index.ts +7 -18
- package/src/infrastructure/providers/FirebaseAuthProvider.ts +6 -2
- package/src/infrastructure/repositories/AuthRepository.ts +3 -3
- package/src/infrastructure/services/AuthEventService.ts +4 -0
- package/src/infrastructure/services/AuthService.ts +14 -36
- package/src/infrastructure/services/initializeAuth.ts +0 -15
- package/src/infrastructure/utils/AuthValidation.ts +5 -2
- package/src/infrastructure/utils/UserMapper.ts +9 -6
- package/src/infrastructure/utils/authStateHandler.ts +0 -7
- package/src/infrastructure/utils/validation/sanitization.ts +2 -89
- package/src/init/createAuthInitModule.ts +0 -15
- package/src/presentation/hooks/mutations/useAuthMutations.ts +3 -3
- package/src/presentation/hooks/useAuthBottomSheet.ts +10 -1
- package/src/presentation/hooks/useGoogleAuth.ts +43 -11
- package/src/presentation/stores/auth.selectors.ts +12 -6
- package/src/presentation/stores/authStore.ts +6 -9
- package/src/presentation/stores/initializeAuthListener.ts +44 -7
- package/src/application/ports/IAuthService.ts +0 -60
- package/src/domain/utils/migration.ts +0 -44
- package/src/infrastructure/adapters/UIProviderAdapter.ts +0 -43
- package/src/infrastructure/services/UserDocument.types.ts +0 -60
- package/src/infrastructure/services/UserDocumentService.ts +0 -86
- package/src/infrastructure/services/app-service-helpers.ts +0 -35
- package/src/infrastructure/types/UI.types.ts +0 -11
- package/src/infrastructure/utils/auth-tracker.util.ts +0 -23
- package/src/infrastructure/utils/userDocumentBuilder.util.ts +0 -114
- package/src/infrastructure/utils/validation/BaseValidators.ts +0 -35
- package/src/infrastructure/utils/validation/CollectionValidators.ts +0 -56
- package/src/infrastructure/utils/validation/DateValidators.ts +0 -71
- package/src/infrastructure/utils/validation/FormValidators.ts +0 -22
- package/src/infrastructure/utils/validation/NumberValidators.ts +0 -50
- package/src/infrastructure/utils/validation/StringValidators.ts +0 -55
- package/src/infrastructure/utils/validation/types.ts +0 -15
- package/src/presentation/screens/change-password/ChangePasswordScreen.tsx +0 -179
- package/src/presentation/screens/change-password/ChangePasswordScreen.types.ts +0 -77
- package/src/presentation/screens/change-password/RequirementItem.tsx +0 -47
- package/src/presentation/screens/change-password/index.ts +0 -6
- package/src/types/translations.types.ts +0 -89
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-auth",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.62",
|
|
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",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
import type { AuthUser } from "../../domain/entities/AuthUser";
|
|
3
|
-
import type {
|
|
3
|
+
import type { AuthCredentials, SignUpCredentials } from "./IAuthProvider";
|
|
4
4
|
|
|
5
5
|
export interface IAuthRepository {
|
|
6
|
-
signUp(params:
|
|
7
|
-
signIn(params:
|
|
6
|
+
signUp(params: SignUpCredentials): Promise<AuthUser>;
|
|
7
|
+
signIn(params: AuthCredentials): Promise<AuthUser>;
|
|
8
8
|
signOut(): Promise<void>;
|
|
9
9
|
getCurrentUser(): AuthUser | null;
|
|
10
10
|
onAuthStateChange(callback: (user: AuthUser | null) => void): () => void;
|
package/src/index.ts
CHANGED
|
@@ -39,14 +39,11 @@ export {
|
|
|
39
39
|
} from './domain/value-objects/AuthConfig';
|
|
40
40
|
|
|
41
41
|
export type { UserProfile, UpdateProfileParams } from './domain/entities/UserProfile';
|
|
42
|
-
export { migrateUserData, configureMigration } from './domain/utils/migration';
|
|
43
|
-
export type { MigrationConfig } from './domain/utils/migration';
|
|
44
42
|
|
|
45
43
|
// =============================================================================
|
|
46
44
|
// APPLICATION LAYER
|
|
47
45
|
// =============================================================================
|
|
48
46
|
|
|
49
|
-
export type { IAuthService, SignUpParams, SignInParams } from './application/ports/IAuthService';
|
|
50
47
|
export type {
|
|
51
48
|
IAuthProvider,
|
|
52
49
|
AuthCredentials,
|
|
@@ -70,17 +67,6 @@ export {
|
|
|
70
67
|
createStorageProvider,
|
|
71
68
|
StorageProviderAdapter,
|
|
72
69
|
} from './infrastructure/adapters/StorageProviderAdapter';
|
|
73
|
-
export {
|
|
74
|
-
ensureUserDocument,
|
|
75
|
-
markUserDeleted,
|
|
76
|
-
configureUserDocumentService,
|
|
77
|
-
} from './infrastructure/services/UserDocumentService';
|
|
78
|
-
export type {
|
|
79
|
-
UserDocumentConfig,
|
|
80
|
-
UserDocumentExtras,
|
|
81
|
-
UserDocumentUser,
|
|
82
|
-
} from './infrastructure/services/UserDocumentService';
|
|
83
|
-
|
|
84
70
|
export {
|
|
85
71
|
initializeAuth,
|
|
86
72
|
isAuthInitialized,
|
|
@@ -171,22 +157,24 @@ export { RegisterScreen } from './presentation/screens/RegisterScreen';
|
|
|
171
157
|
export type { RegisterScreenProps } from './presentation/screens/RegisterScreen';
|
|
172
158
|
|
|
173
159
|
export { AccountScreen } from './presentation/screens/AccountScreen';
|
|
174
|
-
export type { AccountScreenProps } from './presentation/screens/AccountScreen';
|
|
160
|
+
export type { AccountScreenProps, AccountScreenConfig } from './presentation/screens/AccountScreen';
|
|
175
161
|
|
|
176
162
|
export { EditProfileScreen } from './presentation/screens/EditProfileScreen';
|
|
177
163
|
export type { EditProfileScreenProps } from './presentation/screens/EditProfileScreen';
|
|
178
164
|
|
|
179
|
-
export { ChangePasswordScreen } from './presentation/screens/change-password';
|
|
180
|
-
export type { ChangePasswordScreenProps } from './presentation/screens/change-password';
|
|
181
|
-
|
|
182
165
|
export { AuthNavigator } from './presentation/navigation/AuthNavigator';
|
|
183
166
|
export type { AuthStackParamList } from './presentation/navigation/AuthNavigator';
|
|
184
167
|
|
|
168
|
+
export { AuthBottomSheet } from './presentation/components/AuthBottomSheet';
|
|
169
|
+
export { ProfileSection } from './presentation/components/ProfileSection';
|
|
170
|
+
export type { ProfileSectionProps, ProfileSectionConfig } from './presentation/components/ProfileSection';
|
|
171
|
+
|
|
185
172
|
// =============================================================================
|
|
186
173
|
// STORES
|
|
187
174
|
// =============================================================================
|
|
188
175
|
|
|
189
176
|
export { useAuthStore } from './presentation/stores/authStore';
|
|
177
|
+
export { useAuthModalStore } from './presentation/stores/authModalStore';
|
|
190
178
|
export {
|
|
191
179
|
initializeAuthListener,
|
|
192
180
|
resetAuthListener,
|
|
@@ -194,6 +182,7 @@ export {
|
|
|
194
182
|
} from './presentation/stores/initializeAuthListener';
|
|
195
183
|
export type { AuthState, AuthActions, UserType } from './types/auth-store.types';
|
|
196
184
|
export type { AuthListenerOptions } from './types/auth-store.types';
|
|
185
|
+
export * from './presentation/stores/auth.selectors';
|
|
197
186
|
|
|
198
187
|
// =============================================================================
|
|
199
188
|
// UTILITIES
|
|
@@ -164,10 +164,14 @@ export class FirebaseAuthProvider implements IAuthProvider {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
getCurrentUser(): AuthUser | null {
|
|
167
|
-
if (!this.auth
|
|
167
|
+
if (!this.auth) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
const currentUser = this.auth.currentUser;
|
|
171
|
+
if (!currentUser) {
|
|
168
172
|
return null;
|
|
169
173
|
}
|
|
170
|
-
return mapToAuthUser(
|
|
174
|
+
return mapToAuthUser(currentUser);
|
|
171
175
|
}
|
|
172
176
|
|
|
173
177
|
onAuthStateChange(callback: (user: AuthUser | null) => void): () => void {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import type { IAuthRepository } from "../../application/ports/IAuthRepository";
|
|
8
8
|
import type { IAuthProvider } from "../../application/ports/IAuthProvider";
|
|
9
9
|
import type { AuthUser } from "../../domain/entities/AuthUser";
|
|
10
|
-
import type {
|
|
10
|
+
import type { AuthCredentials, SignUpCredentials } from "../../application/ports/IAuthProvider";
|
|
11
11
|
import {
|
|
12
12
|
AuthValidationError,
|
|
13
13
|
AuthWeakPasswordError,
|
|
@@ -31,7 +31,7 @@ export class AuthRepository implements IAuthRepository {
|
|
|
31
31
|
this.config = config;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
async signUp(params:
|
|
34
|
+
async signUp(params: SignUpCredentials): Promise<AuthUser> {
|
|
35
35
|
const email = sanitizeEmail(params.email);
|
|
36
36
|
const password = sanitizePassword(params.password);
|
|
37
37
|
const displayName = params.displayName ? sanitizeName(params.displayName) : undefined;
|
|
@@ -64,7 +64,7 @@ export class AuthRepository implements IAuthRepository {
|
|
|
64
64
|
return this.provider.signUp({ email, password, displayName });
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
async signIn(params:
|
|
67
|
+
async signIn(params: AuthCredentials): Promise<AuthUser> {
|
|
68
68
|
const email = sanitizeEmail(params.email);
|
|
69
69
|
const password = sanitizePassword(params.password);
|
|
70
70
|
|
|
@@ -70,6 +70,10 @@ export class AuthEventService {
|
|
|
70
70
|
const index = eventListeners.indexOf(listener);
|
|
71
71
|
if (index > -1) {
|
|
72
72
|
eventListeners.splice(index, 1);
|
|
73
|
+
// Clean up empty arrays to prevent memory leaks
|
|
74
|
+
if (eventListeners.length === 0) {
|
|
75
|
+
this.listeners.delete(event);
|
|
76
|
+
}
|
|
73
77
|
}
|
|
74
78
|
};
|
|
75
79
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Auth } from "firebase/auth";
|
|
7
|
-
import type {
|
|
7
|
+
import type { AuthCredentials, SignUpCredentials } from "../../application/ports/IAuthProvider";
|
|
8
8
|
import type { IAuthProvider } from "../../application/ports/IAuthProvider";
|
|
9
9
|
import { FirebaseAuthProvider } from "../providers/FirebaseAuthProvider";
|
|
10
10
|
import type { AuthUser } from "../../domain/entities/AuthUser";
|
|
@@ -13,10 +13,9 @@ import { sanitizeAuthConfig } from "../../domain/value-objects/AuthConfig";
|
|
|
13
13
|
import { AuthRepository } from "../repositories/AuthRepository";
|
|
14
14
|
import { AnonymousModeService } from "./AnonymousModeService";
|
|
15
15
|
import { authEventService } from "./AuthEventService";
|
|
16
|
-
import { authTracker } from "../utils/auth-tracker.util";
|
|
17
16
|
import type { IStorageProvider } from "../types/Storage.types";
|
|
18
17
|
|
|
19
|
-
export class AuthService
|
|
18
|
+
export class AuthService {
|
|
20
19
|
private repository!: AuthRepository;
|
|
21
20
|
private anonymousModeService: AnonymousModeService;
|
|
22
21
|
private storageProvider?: IStorageProvider;
|
|
@@ -62,44 +61,23 @@ export class AuthService implements IAuthService {
|
|
|
62
61
|
return this.initialized;
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
async signUp(params:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
authTracker.logOperationSuccess("Sign up", { userId: user.uid });
|
|
71
|
-
authEventService.emitUserAuthenticated(user.uid);
|
|
72
|
-
return user;
|
|
73
|
-
} catch (error) {
|
|
74
|
-
authTracker.logOperationError("Sign up", error, { email: params.email });
|
|
75
|
-
throw error;
|
|
76
|
-
}
|
|
64
|
+
async signUp(params: SignUpCredentials): Promise<AuthUser> {
|
|
65
|
+
const user = await this.repositoryInstance.signUp(params);
|
|
66
|
+
await this.clearAnonymousModeIfNeeded();
|
|
67
|
+
authEventService.emitUserAuthenticated(user.uid);
|
|
68
|
+
return user;
|
|
77
69
|
}
|
|
78
70
|
|
|
79
|
-
async signIn(params:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
authTracker.logOperationSuccess("Sign in", { userId: user.uid });
|
|
85
|
-
authEventService.emitUserAuthenticated(user.uid);
|
|
86
|
-
return user;
|
|
87
|
-
} catch (error) {
|
|
88
|
-
authTracker.logOperationError("Sign in", error, { email: params.email });
|
|
89
|
-
throw error;
|
|
90
|
-
}
|
|
71
|
+
async signIn(params: AuthCredentials): Promise<AuthUser> {
|
|
72
|
+
const user = await this.repositoryInstance.signIn(params);
|
|
73
|
+
await this.clearAnonymousModeIfNeeded();
|
|
74
|
+
authEventService.emitUserAuthenticated(user.uid);
|
|
75
|
+
return user;
|
|
91
76
|
}
|
|
92
77
|
|
|
93
78
|
async signOut(): Promise<void> {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
await this.repositoryInstance.signOut();
|
|
97
|
-
await this.clearAnonymousModeIfNeeded();
|
|
98
|
-
authTracker.logOperationSuccess("Sign out");
|
|
99
|
-
} catch (error) {
|
|
100
|
-
authTracker.logOperationError("Sign out", error);
|
|
101
|
-
throw error;
|
|
102
|
-
}
|
|
79
|
+
await this.repositoryInstance.signOut();
|
|
80
|
+
await this.clearAnonymousModeIfNeeded();
|
|
103
81
|
}
|
|
104
82
|
|
|
105
83
|
private async clearAnonymousModeIfNeeded(): Promise<void> {
|
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
import type { Auth, User } from "firebase/auth";
|
|
7
7
|
import { getFirebaseAuth } from "@umituz/react-native-firebase";
|
|
8
8
|
import { initializeAuthService } from "./AuthService";
|
|
9
|
-
import { configureUserDocumentService } from "./UserDocumentService";
|
|
10
|
-
import type { UserDocumentExtras } from "./UserDocumentService";
|
|
11
|
-
import { collectDeviceExtras } from "@umituz/react-native-design-system";
|
|
12
9
|
import { initializeAuthListener } from "../../presentation/stores/initializeAuthListener";
|
|
13
10
|
import { createAuthStateHandler } from "../utils/authStateHandler";
|
|
14
11
|
import type { ConversionState } from "../utils/authConversionDetector";
|
|
@@ -16,9 +13,6 @@ import type { AuthConfig } from "../../domain/value-objects/AuthConfig";
|
|
|
16
13
|
import type { IStorageProvider } from "../types/Storage.types";
|
|
17
14
|
|
|
18
15
|
export interface InitializeAuthOptions {
|
|
19
|
-
userCollection?: string;
|
|
20
|
-
extraFields?: Record<string, unknown>;
|
|
21
|
-
collectExtras?: () => Promise<UserDocumentExtras>;
|
|
22
16
|
storageProvider?: IStorageProvider;
|
|
23
17
|
autoAnonymousSignIn?: boolean;
|
|
24
18
|
onUserConverted?: (anonymousId: string, authenticatedId: string) => void | Promise<void>;
|
|
@@ -58,9 +52,6 @@ async function doInitializeAuth(
|
|
|
58
52
|
): Promise<{ success: boolean; auth: Auth | null }> {
|
|
59
53
|
|
|
60
54
|
const {
|
|
61
|
-
userCollection = "users",
|
|
62
|
-
extraFields,
|
|
63
|
-
collectExtras,
|
|
64
55
|
storageProvider,
|
|
65
56
|
autoAnonymousSignIn = true,
|
|
66
57
|
onUserConverted,
|
|
@@ -71,12 +62,6 @@ async function doInitializeAuth(
|
|
|
71
62
|
const auth = getFirebaseAuth();
|
|
72
63
|
if (!auth) return { success: false, auth: null };
|
|
73
64
|
|
|
74
|
-
configureUserDocumentService({
|
|
75
|
-
collectionName: userCollection,
|
|
76
|
-
extraFields,
|
|
77
|
-
collectExtras: collectExtras || collectDeviceExtras,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
65
|
let authServiceInitFailed = false;
|
|
81
66
|
try {
|
|
82
67
|
await initializeAuthService(auth, authConfig, storageProvider);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { PasswordConfig } from "../../domain/value-objects/AuthConfig";
|
|
2
|
-
import type { ValidationResult } from "./validation/types";
|
|
3
2
|
|
|
4
|
-
export
|
|
3
|
+
export interface ValidationResult {
|
|
4
|
+
isValid: boolean;
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
5
8
|
export interface PasswordStrengthResult extends ValidationResult { requirements: PasswordRequirements; }
|
|
6
9
|
export interface PasswordRequirements {
|
|
7
10
|
hasMinLength: boolean;
|
|
@@ -32,14 +32,17 @@ export function extractProvider(user: FirebaseUserLike): AuthProviderType {
|
|
|
32
32
|
return "unknown";
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// Filter out null providers and find the first valid one
|
|
36
|
+
const validProviders = providerData.filter((p): p is ProviderData => p != null);
|
|
37
37
|
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
38
|
+
const googleProvider = validProviders.find((p) => p.providerId === "google.com");
|
|
39
|
+
if (googleProvider && googleProvider.providerId) return "google.com";
|
|
40
40
|
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
41
|
+
const appleProvider = validProviders.find((p) => p.providerId === "apple.com");
|
|
42
|
+
if (appleProvider && appleProvider.providerId) return "apple.com";
|
|
43
|
+
|
|
44
|
+
const passwordProvider = validProviders.find((p) => p.providerId === "password");
|
|
45
|
+
if (passwordProvider && passwordProvider.providerId) return "password";
|
|
43
46
|
|
|
44
47
|
return "unknown";
|
|
45
48
|
}
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { User } from "firebase/auth";
|
|
7
|
-
import { ensureUserDocument } from "../services/UserDocumentService";
|
|
8
7
|
import { detectConversion, type ConversionState } from "./authConversionDetector";
|
|
9
8
|
|
|
10
9
|
export interface AuthStateHandlerOptions {
|
|
@@ -43,12 +42,6 @@ export function createAuthStateHandler(
|
|
|
43
42
|
}
|
|
44
43
|
}
|
|
45
44
|
|
|
46
|
-
const extras = conversion.isConversion && state.current.previousUserId
|
|
47
|
-
? { previousAnonymousUserId: state.current.previousUserId }
|
|
48
|
-
: undefined;
|
|
49
|
-
|
|
50
|
-
await ensureUserDocument(user, extras);
|
|
51
|
-
|
|
52
45
|
state.current = {
|
|
53
46
|
previousUserId: currentUserId,
|
|
54
47
|
wasAnonymous: isCurrentlyAnonymous,
|
|
@@ -1,141 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Sanitization Utilities
|
|
3
3
|
* Secure input cleaning for user data
|
|
4
|
-
*
|
|
5
|
-
* @module Sanitization
|
|
6
|
-
* @description Provides input sanitization functions to prevent XSS attacks,
|
|
7
|
-
* injection attacks, and ensure data integrity. All user inputs should be
|
|
8
|
-
* sanitized before storage or processing.
|
|
9
|
-
*
|
|
10
|
-
* Security Limits:
|
|
11
|
-
* These constants define maximum lengths for various input fields to prevent
|
|
12
|
-
* DoS attacks and ensure database integrity. They are based on industry standards:
|
|
13
|
-
*
|
|
14
|
-
* - EMAIL_MAX_LENGTH: 254 (RFC 5321 - maximum email address length)
|
|
15
|
-
* - PASSWORD_MAX_LENGTH: 128 (NIST recommendations)
|
|
16
|
-
* - NAME_MAX_LENGTH: 100 (Reasonable limit for display names)
|
|
17
|
-
* - GENERAL_TEXT_MAX_LENGTH: 500 (Prevents abuse of text fields)
|
|
18
|
-
*
|
|
19
|
-
* @note These limits are currently hardcoded for security. If you need to
|
|
20
|
-
* customize them for your application, you can:
|
|
21
|
-
* 1. Create your own sanitization functions with custom limits
|
|
22
|
-
* 2. Use the helper functions like `isWithinLengthLimit()` for validation
|
|
23
|
-
* 3. Submit a PR to make these limits configurable via AuthConfig
|
|
24
4
|
*/
|
|
25
5
|
|
|
26
|
-
/**
|
|
27
|
-
* Security constants for input validation
|
|
28
|
-
*
|
|
29
|
-
* @constant
|
|
30
|
-
* @type {readonly [key: string]: number}
|
|
31
|
-
*
|
|
32
|
-
* @property {number} EMAIL_MAX_LENGTH - Maximum email length per RFC 5321
|
|
33
|
-
* @property {number} PASSWORD_MIN_LENGTH - Minimum password length (configurable via AuthConfig)
|
|
34
|
-
* @property {number} PASSWORD_MAX_LENGTH - Maximum password length to prevent DoS
|
|
35
|
-
* @property {number} NAME_MAX_LENGTH - Maximum display name length
|
|
36
|
-
* @property {number} GENERAL_TEXT_MAX_LENGTH - Maximum general text input length
|
|
37
|
-
*/
|
|
38
6
|
export const SECURITY_LIMITS = {
|
|
39
|
-
EMAIL_MAX_LENGTH: 254,
|
|
7
|
+
EMAIL_MAX_LENGTH: 254,
|
|
40
8
|
PASSWORD_MIN_LENGTH: 6,
|
|
41
9
|
PASSWORD_MAX_LENGTH: 128,
|
|
42
10
|
NAME_MAX_LENGTH: 100,
|
|
43
11
|
GENERAL_TEXT_MAX_LENGTH: 500,
|
|
44
12
|
} as const;
|
|
45
13
|
|
|
46
|
-
/**
|
|
47
|
-
* Type for security limit keys
|
|
48
|
-
*/
|
|
49
14
|
export type SecurityLimitKey = keyof typeof SECURITY_LIMITS;
|
|
50
15
|
|
|
51
|
-
/**
|
|
52
|
-
* Get a specific security limit value
|
|
53
|
-
* @param key - The security limit key
|
|
54
|
-
* @returns The security limit value
|
|
55
|
-
* @example
|
|
56
|
-
* ```ts
|
|
57
|
-
* const maxEmailLength = getSecurityLimit('EMAIL_MAX_LENGTH'); // 254
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
|
-
export function getSecurityLimit(key: SecurityLimitKey): number {
|
|
61
|
-
return SECURITY_LIMITS[key];
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Trim and normalize whitespace
|
|
66
|
-
*/
|
|
67
16
|
export const sanitizeWhitespace = (input: string): string => {
|
|
68
17
|
return input.trim().replace(/\s+/g, ' ');
|
|
69
18
|
};
|
|
70
19
|
|
|
71
|
-
/**
|
|
72
|
-
* Sanitize email address
|
|
73
|
-
* - Trim whitespace
|
|
74
|
-
* - Convert to lowercase
|
|
75
|
-
* - Limit length
|
|
76
|
-
*/
|
|
77
20
|
export const sanitizeEmail = (email: string): string => {
|
|
78
21
|
const trimmed = email.trim().toLowerCase();
|
|
79
22
|
return trimmed.substring(0, SECURITY_LIMITS.EMAIL_MAX_LENGTH);
|
|
80
23
|
};
|
|
81
24
|
|
|
82
|
-
/**
|
|
83
|
-
* Sanitize password
|
|
84
|
-
* - Trim leading/trailing whitespace to prevent login issues
|
|
85
|
-
* - Preserve case and special chars
|
|
86
|
-
* - Limit length to prevent DoS
|
|
87
|
-
*/
|
|
88
25
|
export const sanitizePassword = (password: string): string => {
|
|
89
|
-
// Trim leading/trailing spaces to prevent authentication issues
|
|
90
|
-
// Internal spaces are preserved for special use cases
|
|
91
26
|
return password.trim().substring(0, SECURITY_LIMITS.PASSWORD_MAX_LENGTH);
|
|
92
27
|
};
|
|
93
28
|
|
|
94
|
-
/**
|
|
95
|
-
* Sanitize display name
|
|
96
|
-
* - Trim whitespace
|
|
97
|
-
* - Normalize multiple spaces
|
|
98
|
-
* - Remove potential XSS characters
|
|
99
|
-
* - Limit length
|
|
100
|
-
*/
|
|
101
29
|
export const sanitizeName = (name: string): string => {
|
|
102
30
|
const trimmed = sanitizeWhitespace(name);
|
|
103
|
-
// Remove HTML tags and script content
|
|
104
31
|
const noTags = trimmed.replace(/<[^>]*>/g, '');
|
|
105
32
|
return noTags.substring(0, SECURITY_LIMITS.NAME_MAX_LENGTH);
|
|
106
33
|
};
|
|
107
34
|
|
|
108
|
-
/**
|
|
109
|
-
* Sanitize general text input
|
|
110
|
-
* - Trim whitespace
|
|
111
|
-
* - Remove HTML/script tags
|
|
112
|
-
* - Limit length
|
|
113
|
-
*/
|
|
114
35
|
export const sanitizeText = (text: string): string => {
|
|
115
36
|
const trimmed = sanitizeWhitespace(text);
|
|
116
37
|
const noTags = trimmed.replace(/<[^>]*>/g, '');
|
|
117
38
|
return noTags.substring(0, SECURITY_LIMITS.GENERAL_TEXT_MAX_LENGTH);
|
|
118
39
|
};
|
|
119
40
|
|
|
120
|
-
/**
|
|
121
|
-
* Check if string contains potentially dangerous characters
|
|
122
|
-
*/
|
|
123
41
|
export const containsDangerousChars = (input: string): boolean => {
|
|
124
|
-
// Check for common XSS patterns
|
|
125
42
|
const dangerousPatterns = [
|
|
126
43
|
/<script/i,
|
|
127
44
|
/javascript:/i,
|
|
128
|
-
/on\w+\s*=/i,
|
|
45
|
+
/on\w+\s*=/i,
|
|
129
46
|
/<iframe/i,
|
|
130
47
|
/eval\(/i,
|
|
131
48
|
];
|
|
132
|
-
|
|
133
49
|
return dangerousPatterns.some(pattern => pattern.test(input));
|
|
134
50
|
};
|
|
135
51
|
|
|
136
|
-
/**
|
|
137
|
-
* Validate string length is within bounds
|
|
138
|
-
*/
|
|
139
52
|
export const isWithinLengthLimit = (
|
|
140
53
|
input: string,
|
|
141
54
|
maxLength: number,
|
|
@@ -8,23 +8,12 @@ import { initializeAuth } from '../infrastructure/services/initializeAuth';
|
|
|
8
8
|
import type { InitializeAuthOptions } from '../infrastructure/services/initializeAuth';
|
|
9
9
|
|
|
10
10
|
export interface AuthInitModuleConfig {
|
|
11
|
-
/**
|
|
12
|
-
* Firestore collection name for user documents
|
|
13
|
-
* @default "users"
|
|
14
|
-
*/
|
|
15
|
-
userCollection?: string;
|
|
16
|
-
|
|
17
11
|
/**
|
|
18
12
|
* Whether to auto sign-in as anonymous user
|
|
19
13
|
* @default true
|
|
20
14
|
*/
|
|
21
15
|
autoAnonymousSignIn?: boolean;
|
|
22
16
|
|
|
23
|
-
/**
|
|
24
|
-
* Function to collect device extras for user document
|
|
25
|
-
*/
|
|
26
|
-
collectExtras?: InitializeAuthOptions['collectExtras'];
|
|
27
|
-
|
|
28
17
|
/**
|
|
29
18
|
* Storage provider for auth persistence
|
|
30
19
|
*/
|
|
@@ -80,9 +69,7 @@ export function createAuthInitModule(
|
|
|
80
69
|
config: AuthInitModuleConfig = {}
|
|
81
70
|
): InitModule {
|
|
82
71
|
const {
|
|
83
|
-
userCollection = 'users',
|
|
84
72
|
autoAnonymousSignIn = true,
|
|
85
|
-
collectExtras,
|
|
86
73
|
storageProvider,
|
|
87
74
|
onRestorePurchases,
|
|
88
75
|
onUserConverted,
|
|
@@ -97,9 +84,7 @@ export function createAuthInitModule(
|
|
|
97
84
|
init: async () => {
|
|
98
85
|
try {
|
|
99
86
|
await initializeAuth({
|
|
100
|
-
userCollection,
|
|
101
87
|
autoAnonymousSignIn,
|
|
102
|
-
collectExtras,
|
|
103
88
|
storageProvider,
|
|
104
89
|
onUserConverted: async (anonymousId: string, authenticatedId: string) => {
|
|
105
90
|
if (__DEV__) {
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
import { useMutation } from "@umituz/react-native-design-system";
|
|
7
7
|
import { getAuthService } from "../../../infrastructure/services/AuthService";
|
|
8
|
-
import type {
|
|
8
|
+
import type { AuthCredentials, SignUpCredentials } from "../../../application/ports/IAuthProvider";
|
|
9
9
|
import type { AuthUser } from "../../../domain/entities/AuthUser";
|
|
10
10
|
|
|
11
11
|
export const useSignUpMutation = () => {
|
|
12
12
|
return useMutation({
|
|
13
|
-
mutationFn: async (params:
|
|
13
|
+
mutationFn: async (params: SignUpCredentials): Promise<AuthUser> => {
|
|
14
14
|
const service = getAuthService();
|
|
15
15
|
if (!service) throw new Error("Auth Service not initialized");
|
|
16
16
|
return service.signUp(params);
|
|
@@ -20,7 +20,7 @@ export const useSignUpMutation = () => {
|
|
|
20
20
|
|
|
21
21
|
export const useSignInMutation = () => {
|
|
22
22
|
return useMutation({
|
|
23
|
-
mutationFn: async (params:
|
|
23
|
+
mutationFn: async (params: AuthCredentials): Promise<AuthUser> => {
|
|
24
24
|
const service = getAuthService();
|
|
25
25
|
if (!service) throw new Error("Auth Service not initialized");
|
|
26
26
|
return service.signIn(params);
|
|
@@ -103,6 +103,8 @@ export function useAuthBottomSheet(params: UseAuthBottomSheetParams = {}) {
|
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
let timeoutId: NodeJS.Timeout | undefined;
|
|
107
|
+
|
|
106
108
|
if ((justAuthenticated || justConvertedFromAnonymous) && isVisible && !isAnonymous) {
|
|
107
109
|
if (__DEV__) {
|
|
108
110
|
console.log("[useAuthBottomSheet] Auto-closing due to successful authentication transition", {
|
|
@@ -116,7 +118,7 @@ export function useAuthBottomSheet(params: UseAuthBottomSheetParams = {}) {
|
|
|
116
118
|
// Notify auth success
|
|
117
119
|
onAuthSuccess?.();
|
|
118
120
|
// Execute callback with delay to ensure auth state has propagated
|
|
119
|
-
setTimeout(() => {
|
|
121
|
+
timeoutId = setTimeout(() => {
|
|
120
122
|
if (__DEV__) {
|
|
121
123
|
console.log("[useAuthBottomSheet] Executing pending callback after auth");
|
|
122
124
|
}
|
|
@@ -127,6 +129,13 @@ export function useAuthBottomSheet(params: UseAuthBottomSheetParams = {}) {
|
|
|
127
129
|
prevIsAuthenticatedRef.current = isAuthenticated;
|
|
128
130
|
prevIsVisibleRef.current = isVisible;
|
|
129
131
|
prevIsAnonymousRef.current = isAnonymous;
|
|
132
|
+
|
|
133
|
+
// Cleanup timeout on unmount or dependency change
|
|
134
|
+
return () => {
|
|
135
|
+
if (timeoutId) {
|
|
136
|
+
clearTimeout(timeoutId);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
130
139
|
}, [isAuthenticated, isVisible, isAnonymous, executePendingCallback, hideAuthModal, onAuthSuccess]);
|
|
131
140
|
|
|
132
141
|
const handleNavigateToRegister = useCallback(() => {
|