@umituz/react-native-auth 1.12.0 → 2.0.2
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/README.md +0 -0
- package/lib/__tests__/services/AuthCoreService.test.d.ts +4 -0
- package/lib/__tests__/services/AuthCoreService.test.js +198 -0
- package/lib/__tests__/services/AuthPackage.test.d.ts +4 -0
- package/lib/__tests__/services/AuthPackage.test.js +177 -0
- package/lib/__tests__/services/GuestModeService.test.d.ts +4 -0
- package/lib/__tests__/services/GuestModeService.test.js +141 -0
- package/lib/__tests__/utils/AuthValidation.test.d.ts +4 -0
- package/lib/__tests__/utils/AuthValidation.test.js +222 -0
- package/lib/application/ports/IAuthProvider.d.ts +42 -0
- package/lib/application/ports/IAuthProvider.js +5 -0
- package/lib/application/ports/IAuthService.d.ts +48 -0
- package/lib/application/ports/IAuthService.js +5 -0
- package/lib/domain/entities/AuthUser.d.ts +12 -0
- package/lib/domain/entities/AuthUser.js +5 -0
- package/lib/domain/errors/AuthError.d.ts +36 -0
- package/lib/domain/errors/AuthError.js +76 -0
- package/lib/domain/value-objects/AuthConfig.d.ts +16 -0
- package/lib/domain/value-objects/AuthConfig.js +14 -0
- package/lib/index.d.ts +45 -0
- package/lib/index.js +59 -0
- package/lib/infrastructure/adapters/StorageProviderAdapter.d.ts +16 -0
- package/lib/infrastructure/adapters/StorageProviderAdapter.js +72 -0
- package/lib/infrastructure/adapters/UIProviderAdapter.d.ts +18 -0
- package/lib/infrastructure/adapters/UIProviderAdapter.js +28 -0
- package/lib/infrastructure/providers/FirebaseAuthProvider.d.ts +19 -0
- package/lib/infrastructure/providers/FirebaseAuthProvider.js +94 -0
- package/lib/infrastructure/services/AuthCoreService.d.ts +22 -0
- package/lib/infrastructure/services/AuthCoreService.js +102 -0
- package/lib/infrastructure/services/AuthEventService.d.ts +28 -0
- package/lib/infrastructure/services/AuthEventService.js +88 -0
- package/lib/infrastructure/services/AuthPackage.d.ts +62 -0
- package/lib/infrastructure/services/AuthPackage.js +91 -0
- package/lib/infrastructure/services/AuthService.d.ts +42 -0
- package/lib/infrastructure/services/AuthService.js +123 -0
- package/lib/infrastructure/services/GuestModeService.d.ts +23 -0
- package/lib/infrastructure/services/GuestModeService.js +69 -0
- package/lib/infrastructure/storage/GuestModeStorage.d.ts +16 -0
- package/lib/infrastructure/storage/GuestModeStorage.js +73 -0
- package/lib/infrastructure/utils/AuthErrorMapper.d.ts +8 -0
- package/lib/infrastructure/utils/AuthErrorMapper.js +51 -0
- package/lib/infrastructure/utils/AuthEventEmitter.d.ts +12 -0
- package/lib/infrastructure/utils/AuthEventEmitter.js +25 -0
- package/lib/infrastructure/utils/AuthValidation.d.ts +49 -0
- package/lib/infrastructure/utils/AuthValidation.js +133 -0
- package/lib/infrastructure/utils/UserMapper.d.ts +15 -0
- package/lib/infrastructure/utils/UserMapper.js +16 -0
- package/lib/presentation/components/AuthContainer.d.ts +10 -0
- package/lib/presentation/components/AuthContainer.js +27 -0
- package/lib/presentation/components/AuthDivider.d.ts +6 -0
- package/lib/presentation/components/AuthDivider.js +36 -0
- package/lib/presentation/components/AuthErrorDisplay.d.ts +10 -0
- package/lib/presentation/components/AuthErrorDisplay.js +24 -0
- package/lib/presentation/components/AuthFormCard.d.ts +10 -0
- package/lib/presentation/components/AuthFormCard.js +19 -0
- package/lib/presentation/components/AuthGradientBackground.d.ts +6 -0
- package/lib/presentation/components/AuthGradientBackground.js +8 -0
- package/lib/presentation/components/AuthHeader.d.ts +11 -0
- package/lib/presentation/components/AuthHeader.js +38 -0
- package/lib/presentation/components/AuthLegalLinks.d.ts +28 -0
- package/lib/presentation/components/AuthLegalLinks.js +54 -0
- package/lib/presentation/components/AuthLink.d.ts +13 -0
- package/lib/presentation/components/AuthLink.js +27 -0
- package/lib/presentation/components/LoginForm.d.ts +10 -0
- package/lib/presentation/components/LoginForm.js +27 -0
- package/lib/presentation/components/PasswordMatchIndicator.d.ts +9 -0
- package/lib/presentation/components/PasswordMatchIndicator.js +30 -0
- package/lib/presentation/components/PasswordStrengthIndicator.d.ts +11 -0
- package/lib/presentation/components/PasswordStrengthIndicator.js +60 -0
- package/lib/presentation/components/RegisterForm.d.ts +14 -0
- package/lib/presentation/components/RegisterForm.js +30 -0
- package/lib/presentation/hooks/useAuth.d.ts +44 -0
- package/lib/presentation/hooks/useAuth.js +38 -0
- package/lib/presentation/hooks/useAuthActions.d.ts +15 -0
- package/lib/presentation/hooks/useAuthActions.js +162 -0
- package/lib/presentation/hooks/useAuthState.d.ts +19 -0
- package/lib/presentation/hooks/useAuthState.js +79 -0
- package/lib/presentation/hooks/useLoginForm.d.ts +21 -0
- package/lib/presentation/hooks/useLoginForm.js +131 -0
- package/lib/presentation/hooks/useRegisterForm.d.ts +31 -0
- package/lib/presentation/hooks/useRegisterForm.js +136 -0
- package/lib/presentation/navigation/AuthNavigator.d.ts +28 -0
- package/lib/presentation/navigation/AuthNavigator.js +37 -0
- package/lib/presentation/screens/LoginScreen.d.ts +6 -0
- package/lib/presentation/screens/LoginScreen.js +15 -0
- package/lib/presentation/screens/RegisterScreen.d.ts +12 -0
- package/lib/presentation/screens/RegisterScreen.js +15 -0
- package/lib/presentation/utils/getAuthErrorMessage.d.ts +8 -0
- package/lib/presentation/utils/getAuthErrorMessage.js +69 -0
- package/package.json +12 -4
- package/src/__tests__/services/AuthCoreService.test.ts +247 -0
- package/src/__tests__/services/AuthPackage.test.ts +226 -0
- package/src/__tests__/services/GuestModeService.test.ts +194 -0
- package/src/__tests__/utils/AuthValidation.test.ts +270 -0
- package/src/application/ports/IAuthProvider.ts +0 -0
- package/src/application/ports/IAuthService.ts +0 -0
- package/src/domain/entities/AuthUser.ts +0 -0
- package/src/domain/errors/AuthError.ts +0 -0
- package/src/domain/value-objects/AuthConfig.ts +0 -0
- package/src/index.ts +0 -0
- package/src/infrastructure/adapters/StorageProviderAdapter.ts +73 -0
- package/src/infrastructure/adapters/UIProviderAdapter.ts +39 -0
- package/src/infrastructure/providers/FirebaseAuthProvider.ts +10 -2
- package/src/infrastructure/services/AuthCoreService.ts +138 -0
- package/src/infrastructure/services/AuthEventService.ts +115 -0
- package/src/infrastructure/services/AuthPackage.ts +148 -0
- package/src/infrastructure/services/AuthService.ts +62 -128
- package/src/infrastructure/services/GuestModeService.ts +86 -0
- package/src/infrastructure/storage/GuestModeStorage.ts +40 -14
- package/src/infrastructure/utils/AuthErrorMapper.ts +7 -3
- package/src/infrastructure/utils/AuthEventEmitter.ts +0 -0
- package/src/infrastructure/utils/AuthValidation.ts +47 -17
- package/src/infrastructure/utils/UserMapper.ts +0 -0
- package/src/presentation/components/AuthContainer.tsx +0 -0
- package/src/presentation/components/AuthDivider.tsx +0 -0
- package/src/presentation/components/AuthErrorDisplay.tsx +0 -0
- package/src/presentation/components/AuthFormCard.tsx +0 -0
- package/src/presentation/components/AuthGradientBackground.tsx +0 -0
- package/src/presentation/components/AuthHeader.tsx +0 -0
- package/src/presentation/components/AuthLegalLinks.tsx +0 -0
- package/src/presentation/components/AuthLink.tsx +0 -0
- package/src/presentation/components/LoginForm.tsx +0 -0
- package/src/presentation/components/PasswordMatchIndicator.tsx +2 -2
- package/src/presentation/components/PasswordStrengthIndicator.tsx +0 -0
- package/src/presentation/components/RegisterForm.tsx +0 -0
- package/src/presentation/hooks/useAuth.ts +0 -0
- package/src/presentation/hooks/useAuthActions.ts +8 -11
- package/src/presentation/hooks/useAuthState.ts +10 -0
- package/src/presentation/hooks/useLoginForm.ts +0 -0
- package/src/presentation/hooks/useRegisterForm.ts +16 -17
- package/src/presentation/navigation/AuthNavigator.tsx +2 -2
- package/src/presentation/screens/LoginScreen.tsx +3 -6
- package/src/presentation/screens/RegisterScreen.tsx +3 -6
- package/src/presentation/utils/getAuthErrorMessage.ts +0 -0
- package/src/types/external.d.ts +68 -0
- package/LICENSE +0 -22
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Core Service
|
|
3
|
+
* Handles core authentication operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { IAuthService, SignUpParams, SignInParams } from "../../application/ports/IAuthService";
|
|
7
|
+
import type { IAuthProvider } from "../../application/ports/IAuthProvider";
|
|
8
|
+
import type { AuthUser } from "../../domain/entities/AuthUser";
|
|
9
|
+
import type { AuthConfig } from "../../domain/value-objects/AuthConfig";
|
|
10
|
+
import {
|
|
11
|
+
AuthInitializationError,
|
|
12
|
+
AuthValidationError,
|
|
13
|
+
AuthWeakPasswordError,
|
|
14
|
+
AuthInvalidEmailError,
|
|
15
|
+
} from "../../domain/errors/AuthError";
|
|
16
|
+
import {
|
|
17
|
+
validateEmail,
|
|
18
|
+
validatePasswordForLogin,
|
|
19
|
+
validatePasswordForRegister,
|
|
20
|
+
validateDisplayName,
|
|
21
|
+
} from "../utils/AuthValidation";
|
|
22
|
+
|
|
23
|
+
export class AuthCoreService implements Partial<IAuthService> {
|
|
24
|
+
private provider: IAuthProvider | null = null;
|
|
25
|
+
private config: AuthConfig;
|
|
26
|
+
|
|
27
|
+
constructor(config: AuthConfig) {
|
|
28
|
+
this.config = config;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async initialize(providerOrAuth: IAuthProvider | any): Promise<void> {
|
|
32
|
+
if (!providerOrAuth) {
|
|
33
|
+
throw new AuthInitializationError("Auth provider or Firebase Auth instance is required");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check if it's a Firebase Auth instance (has currentUser property)
|
|
37
|
+
if ("currentUser" in providerOrAuth) {
|
|
38
|
+
// Import FirebaseAuthProvider directly
|
|
39
|
+
const { FirebaseAuthProvider } = require("../providers/FirebaseAuthProvider");
|
|
40
|
+
const firebaseProvider = new FirebaseAuthProvider(providerOrAuth);
|
|
41
|
+
await firebaseProvider.initialize();
|
|
42
|
+
this.provider = firebaseProvider;
|
|
43
|
+
} else {
|
|
44
|
+
this.provider = providerOrAuth as IAuthProvider;
|
|
45
|
+
await this.provider.initialize();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
isInitialized(): boolean {
|
|
50
|
+
return this.provider !== null && this.provider.isInitialized();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private getProvider(): IAuthProvider {
|
|
54
|
+
if (!this.provider || !this.provider.isInitialized()) {
|
|
55
|
+
throw new AuthInitializationError("Auth service is not initialized");
|
|
56
|
+
}
|
|
57
|
+
return this.provider;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async signUp(params: SignUpParams): Promise<AuthUser> {
|
|
61
|
+
const provider = this.getProvider();
|
|
62
|
+
|
|
63
|
+
// Validate email
|
|
64
|
+
const emailResult = validateEmail(params.email);
|
|
65
|
+
if (!emailResult.isValid) {
|
|
66
|
+
throw new AuthInvalidEmailError(emailResult.error);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Validate display name if provided
|
|
70
|
+
if (params.displayName) {
|
|
71
|
+
const nameResult = validateDisplayName(params.displayName);
|
|
72
|
+
if (!nameResult.isValid) {
|
|
73
|
+
throw new AuthValidationError(nameResult.error || "Invalid name", "displayName");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Validate password strength for registration
|
|
78
|
+
const passwordResult = validatePasswordForRegister(params.password, this.config.password);
|
|
79
|
+
if (!passwordResult.isValid) {
|
|
80
|
+
throw new AuthWeakPasswordError(passwordResult.error);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return provider.signUp({
|
|
84
|
+
email: params.email,
|
|
85
|
+
password: params.password,
|
|
86
|
+
displayName: params.displayName,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async signIn(params: SignInParams): Promise<AuthUser> {
|
|
91
|
+
const provider = this.getProvider();
|
|
92
|
+
|
|
93
|
+
// Validate email format
|
|
94
|
+
const emailResult = validateEmail(params.email);
|
|
95
|
+
if (!emailResult.isValid) {
|
|
96
|
+
throw new AuthInvalidEmailError(emailResult.error);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// For login, only check if password is provided (no strength requirements)
|
|
100
|
+
const passwordResult = validatePasswordForLogin(params.password);
|
|
101
|
+
if (!passwordResult.isValid) {
|
|
102
|
+
throw new AuthValidationError(passwordResult.error || "Password is required", "password");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return provider.signIn({
|
|
106
|
+
email: params.email,
|
|
107
|
+
password: params.password,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async signOut(): Promise<void> {
|
|
112
|
+
if (!this.provider) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
await this.provider.signOut();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getCurrentUser(): AuthUser | null {
|
|
120
|
+
if (!this.provider) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return this.provider.getCurrentUser();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
onAuthStateChange(callback: (user: AuthUser | null) => void): () => void {
|
|
127
|
+
if (!this.provider) {
|
|
128
|
+
callback(null);
|
|
129
|
+
return () => {};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return this.provider.onAuthStateChange(callback);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getConfig(): AuthConfig {
|
|
136
|
+
return this.config;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Event Service
|
|
3
|
+
* Handles authentication event emission and management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DeviceEventEmitter } from "react-native";
|
|
7
|
+
|
|
8
|
+
export interface AuthEventPayload {
|
|
9
|
+
userId?: string;
|
|
10
|
+
error?: string;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AuthEventListener {
|
|
15
|
+
(payload: AuthEventPayload): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class AuthEventService {
|
|
19
|
+
private static instance: AuthEventService;
|
|
20
|
+
private listeners: Map<string, AuthEventListener[]> = new Map();
|
|
21
|
+
|
|
22
|
+
private constructor() {}
|
|
23
|
+
|
|
24
|
+
static getInstance(): AuthEventService {
|
|
25
|
+
if (!AuthEventService.instance) {
|
|
26
|
+
AuthEventService.instance = new AuthEventService();
|
|
27
|
+
}
|
|
28
|
+
return AuthEventService.instance;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
emitUserAuthenticated(userId: string): void {
|
|
32
|
+
const payload: AuthEventPayload = {
|
|
33
|
+
userId,
|
|
34
|
+
timestamp: Date.now(),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
DeviceEventEmitter.emit("user-authenticated", payload);
|
|
38
|
+
this.notifyListeners("user-authenticated", payload);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
emitGuestModeEnabled(): void {
|
|
42
|
+
const payload: AuthEventPayload = {
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
DeviceEventEmitter.emit("guest-mode-enabled", payload);
|
|
47
|
+
this.notifyListeners("guest-mode-enabled", payload);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
emitAuthError(error: string): void {
|
|
51
|
+
const payload: AuthEventPayload = {
|
|
52
|
+
error,
|
|
53
|
+
timestamp: Date.now(),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
DeviceEventEmitter.emit("auth-error", payload);
|
|
57
|
+
this.notifyListeners("auth-error", payload);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
addEventListener(event: string, listener: AuthEventListener): () => void {
|
|
61
|
+
if (!this.listeners.has(event)) {
|
|
62
|
+
this.listeners.set(event, []);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const eventListeners = this.listeners.get(event)!;
|
|
66
|
+
eventListeners.push(listener);
|
|
67
|
+
|
|
68
|
+
// Return cleanup function
|
|
69
|
+
return () => {
|
|
70
|
+
const index = eventListeners.indexOf(listener);
|
|
71
|
+
if (index > -1) {
|
|
72
|
+
eventListeners.splice(index, 1);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
removeAllListeners(event?: string): void {
|
|
78
|
+
if (event) {
|
|
79
|
+
this.listeners.delete(event);
|
|
80
|
+
} else {
|
|
81
|
+
this.listeners.clear();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private notifyListeners(event: string, payload: AuthEventPayload): void {
|
|
86
|
+
const eventListeners = this.listeners.get(event);
|
|
87
|
+
if (eventListeners) {
|
|
88
|
+
eventListeners.forEach(listener => {
|
|
89
|
+
try {
|
|
90
|
+
listener(payload);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (__DEV__) {
|
|
93
|
+
console.error(`Error in auth event listener for ${event}:`, error);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Export singleton instance for backward compatibility
|
|
102
|
+
export const authEventService = AuthEventService.getInstance();
|
|
103
|
+
|
|
104
|
+
// Legacy functions for backward compatibility
|
|
105
|
+
export function emitUserAuthenticated(userId: string): void {
|
|
106
|
+
authEventService.emitUserAuthenticated(userId);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function emitGuestModeEnabled(): void {
|
|
110
|
+
authEventService.emitGuestModeEnabled();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function emitAuthError(error: string): void {
|
|
114
|
+
authEventService.emitAuthError(error);
|
|
115
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Package Configuration
|
|
3
|
+
* Centralized configuration for the auth package
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AuthConfig, PasswordConfig } from "../../domain/value-objects/AuthConfig";
|
|
7
|
+
|
|
8
|
+
export interface AuthPackageConfig {
|
|
9
|
+
storageKeys: {
|
|
10
|
+
guestMode: string;
|
|
11
|
+
showRegister: string;
|
|
12
|
+
};
|
|
13
|
+
validation: {
|
|
14
|
+
emailRegex: RegExp;
|
|
15
|
+
passwordConfig: PasswordConfig;
|
|
16
|
+
};
|
|
17
|
+
ui: {
|
|
18
|
+
theme?: any;
|
|
19
|
+
localization?: any;
|
|
20
|
+
};
|
|
21
|
+
features: {
|
|
22
|
+
guestMode: boolean;
|
|
23
|
+
registration: boolean;
|
|
24
|
+
passwordStrength: boolean;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const DEFAULT_AUTH_PACKAGE_CONFIG: AuthPackageConfig = {
|
|
29
|
+
storageKeys: {
|
|
30
|
+
guestMode: "@auth_guest_mode",
|
|
31
|
+
showRegister: "auth_show_register",
|
|
32
|
+
},
|
|
33
|
+
validation: {
|
|
34
|
+
emailRegex: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
35
|
+
passwordConfig: {
|
|
36
|
+
minLength: 8,
|
|
37
|
+
requireUppercase: true,
|
|
38
|
+
requireLowercase: true,
|
|
39
|
+
requireNumber: true,
|
|
40
|
+
requireSpecialChar: false,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
ui: {
|
|
44
|
+
theme: undefined,
|
|
45
|
+
localization: undefined,
|
|
46
|
+
},
|
|
47
|
+
features: {
|
|
48
|
+
guestMode: true,
|
|
49
|
+
registration: true,
|
|
50
|
+
passwordStrength: true,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export interface IStorageProvider {
|
|
55
|
+
get(key: string): Promise<string | null>;
|
|
56
|
+
set(key: string, value: string): Promise<void>;
|
|
57
|
+
remove(key: string): Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface IUIProvider {
|
|
61
|
+
getTheme(): any;
|
|
62
|
+
getLocalization(): any;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface IValidationProvider {
|
|
66
|
+
validateEmail(email: string): { isValid: boolean; error?: string };
|
|
67
|
+
validatePassword(password: string, config: PasswordConfig): { isValid: boolean; error?: string };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class AuthPackage {
|
|
71
|
+
private config: AuthPackageConfig;
|
|
72
|
+
private storageProvider?: IStorageProvider;
|
|
73
|
+
private uiProvider?: IUIProvider;
|
|
74
|
+
private validationProvider?: IValidationProvider;
|
|
75
|
+
|
|
76
|
+
constructor(config: Partial<AuthPackageConfig> = {}) {
|
|
77
|
+
this.config = {
|
|
78
|
+
...DEFAULT_AUTH_PACKAGE_CONFIG,
|
|
79
|
+
...config,
|
|
80
|
+
validation: {
|
|
81
|
+
...DEFAULT_AUTH_PACKAGE_CONFIG.validation,
|
|
82
|
+
...config.validation,
|
|
83
|
+
passwordConfig: {
|
|
84
|
+
...DEFAULT_AUTH_PACKAGE_CONFIG.validation.passwordConfig,
|
|
85
|
+
...config.validation?.passwordConfig,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
ui: {
|
|
89
|
+
...DEFAULT_AUTH_PACKAGE_CONFIG.ui,
|
|
90
|
+
...config.ui,
|
|
91
|
+
},
|
|
92
|
+
features: {
|
|
93
|
+
...DEFAULT_AUTH_PACKAGE_CONFIG.features,
|
|
94
|
+
...config.features,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
setStorageProvider(provider: IStorageProvider): void {
|
|
100
|
+
this.storageProvider = provider;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setUIProvider(provider: IUIProvider): void {
|
|
104
|
+
this.uiProvider = provider;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
setValidationProvider(provider: IValidationProvider): void {
|
|
108
|
+
this.validationProvider = provider;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getConfig(): AuthPackageConfig {
|
|
112
|
+
return this.config;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getStorageProvider(): IStorageProvider | undefined {
|
|
116
|
+
return this.storageProvider;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getUIProvider(): IUIProvider | undefined {
|
|
120
|
+
return this.uiProvider;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getValidationProvider(): IValidationProvider | undefined {
|
|
124
|
+
return this.validationProvider;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
isFeatureEnabled(feature: keyof AuthPackageConfig['features']): boolean {
|
|
128
|
+
return this.config.features[feature];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Global package instance
|
|
133
|
+
let packageInstance: AuthPackage | null = null;
|
|
134
|
+
|
|
135
|
+
export function initializeAuthPackage(config: Partial<AuthPackageConfig> = {}): AuthPackage {
|
|
136
|
+
if (!packageInstance) {
|
|
137
|
+
packageInstance = new AuthPackage(config);
|
|
138
|
+
}
|
|
139
|
+
return packageInstance;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function getAuthPackage(): AuthPackage | null {
|
|
143
|
+
return packageInstance;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function resetAuthPackage(): void {
|
|
147
|
+
packageInstance = null;
|
|
148
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auth Service
|
|
3
|
-
*
|
|
3
|
+
* Orchestrates authentication operations using composition
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Auth } from "firebase/auth";
|
|
@@ -9,29 +9,19 @@ import type { IAuthProvider } from "../../application/ports/IAuthProvider";
|
|
|
9
9
|
import type { AuthUser } from "../../domain/entities/AuthUser";
|
|
10
10
|
import type { AuthConfig } from "../../domain/value-objects/AuthConfig";
|
|
11
11
|
import { DEFAULT_AUTH_CONFIG } from "../../domain/value-objects/AuthConfig";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
AuthInvalidEmailError,
|
|
17
|
-
} from "../../domain/errors/AuthError";
|
|
18
|
-
import {
|
|
19
|
-
validateEmail,
|
|
20
|
-
validatePasswordForLogin,
|
|
21
|
-
validatePasswordForRegister,
|
|
22
|
-
validateDisplayName,
|
|
23
|
-
} from "../utils/AuthValidation";
|
|
24
|
-
import { FirebaseAuthProvider } from "../providers/FirebaseAuthProvider";
|
|
25
|
-
import { emitUserAuthenticated, emitGuestModeEnabled } from "../utils/AuthEventEmitter";
|
|
26
|
-
import { loadGuestMode, saveGuestMode, clearGuestMode } from "../storage/GuestModeStorage";
|
|
12
|
+
import { AuthCoreService } from "./AuthCoreService";
|
|
13
|
+
import { GuestModeService, type IStorageProvider } from "./GuestModeService";
|
|
14
|
+
import { authEventService } from "./AuthEventService";
|
|
15
|
+
import { initializeAuthPackage, getAuthPackage } from "./AuthPackage";
|
|
27
16
|
|
|
28
17
|
export class AuthService implements IAuthService {
|
|
29
|
-
private
|
|
30
|
-
private
|
|
31
|
-
private
|
|
18
|
+
private coreService: AuthCoreService;
|
|
19
|
+
private guestModeService: GuestModeService;
|
|
20
|
+
private storageProvider?: IStorageProvider;
|
|
21
|
+
private initialized: boolean = false;
|
|
32
22
|
|
|
33
|
-
constructor(config: Partial<AuthConfig> = {}) {
|
|
34
|
-
|
|
23
|
+
constructor(config: Partial<AuthConfig> = {}, storageProvider?: IStorageProvider) {
|
|
24
|
+
const authConfig = {
|
|
35
25
|
...DEFAULT_AUTH_CONFIG,
|
|
36
26
|
...config,
|
|
37
27
|
password: {
|
|
@@ -39,160 +29,101 @@ export class AuthService implements IAuthService {
|
|
|
39
29
|
...config.password,
|
|
40
30
|
},
|
|
41
31
|
};
|
|
32
|
+
|
|
33
|
+
this.coreService = new AuthCoreService(authConfig);
|
|
34
|
+
this.guestModeService = new GuestModeService();
|
|
35
|
+
this.storageProvider = storageProvider;
|
|
42
36
|
}
|
|
43
37
|
|
|
44
38
|
async initialize(providerOrAuth: IAuthProvider | Auth): Promise<void> {
|
|
45
|
-
if (
|
|
46
|
-
|
|
39
|
+
if (this.initialized) {
|
|
40
|
+
return;
|
|
47
41
|
}
|
|
48
42
|
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
this.provider = providerOrAuth as IAuthProvider;
|
|
56
|
-
await this.provider.initialize();
|
|
43
|
+
// Initialize core service
|
|
44
|
+
await this.coreService.initialize(providerOrAuth);
|
|
45
|
+
|
|
46
|
+
// Initialize guest mode if storage provider is available
|
|
47
|
+
if (this.storageProvider) {
|
|
48
|
+
await this.guestModeService.load(this.storageProvider);
|
|
57
49
|
}
|
|
58
50
|
|
|
59
|
-
this.
|
|
51
|
+
this.initialized = true;
|
|
60
52
|
}
|
|
61
53
|
|
|
62
54
|
isInitialized(): boolean {
|
|
63
|
-
return this.
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
private getProvider(): IAuthProvider {
|
|
67
|
-
if (!this.provider || !this.provider.isInitialized()) {
|
|
68
|
-
throw new AuthInitializationError("Auth service is not initialized");
|
|
69
|
-
}
|
|
70
|
-
return this.provider;
|
|
55
|
+
return this.initialized && this.coreService.isInitialized();
|
|
71
56
|
}
|
|
72
57
|
|
|
73
58
|
async signUp(params: SignUpParams): Promise<AuthUser> {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
// Validate email
|
|
77
|
-
const emailResult = validateEmail(params.email);
|
|
78
|
-
if (!emailResult.isValid) {
|
|
79
|
-
throw new AuthInvalidEmailError(emailResult.error);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Validate display name if provided
|
|
83
|
-
if (params.displayName) {
|
|
84
|
-
const nameResult = validateDisplayName(params.displayName);
|
|
85
|
-
if (!nameResult.isValid) {
|
|
86
|
-
throw new AuthValidationError(nameResult.error || "Invalid name", "displayName");
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Validate password strength for registration
|
|
91
|
-
const passwordResult = validatePasswordForRegister(params.password, this.config.password);
|
|
92
|
-
if (!passwordResult.isValid) {
|
|
93
|
-
throw new AuthWeakPasswordError(passwordResult.error);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const user = await provider.signUp({
|
|
97
|
-
email: params.email,
|
|
98
|
-
password: params.password,
|
|
99
|
-
displayName: params.displayName,
|
|
100
|
-
});
|
|
59
|
+
const user = await this.coreService.signUp(params);
|
|
101
60
|
|
|
102
61
|
// Clear guest mode when user signs up
|
|
103
|
-
if (this.
|
|
104
|
-
this.
|
|
105
|
-
await clearGuestMode();
|
|
62
|
+
if (this.guestModeService.getIsGuestMode() && this.storageProvider) {
|
|
63
|
+
await this.guestModeService.clear(this.storageProvider);
|
|
106
64
|
}
|
|
107
65
|
|
|
108
|
-
emitUserAuthenticated(user.uid);
|
|
66
|
+
authEventService.emitUserAuthenticated(user.uid);
|
|
109
67
|
return user;
|
|
110
68
|
}
|
|
111
69
|
|
|
112
70
|
async signIn(params: SignInParams): Promise<AuthUser> {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
// Validate email format
|
|
116
|
-
const emailResult = validateEmail(params.email);
|
|
117
|
-
if (!emailResult.isValid) {
|
|
118
|
-
throw new AuthInvalidEmailError(emailResult.error);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// For login, only check if password is provided (no strength requirements)
|
|
122
|
-
const passwordResult = validatePasswordForLogin(params.password);
|
|
123
|
-
if (!passwordResult.isValid) {
|
|
124
|
-
throw new AuthValidationError(passwordResult.error || "Password is required", "password");
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const user = await provider.signIn({
|
|
128
|
-
email: params.email,
|
|
129
|
-
password: params.password,
|
|
130
|
-
});
|
|
71
|
+
const user = await this.coreService.signIn(params);
|
|
131
72
|
|
|
132
73
|
// Clear guest mode when user signs in
|
|
133
|
-
if (this.
|
|
134
|
-
this.
|
|
135
|
-
await clearGuestMode();
|
|
74
|
+
if (this.guestModeService.getIsGuestMode() && this.storageProvider) {
|
|
75
|
+
await this.guestModeService.clear(this.storageProvider);
|
|
136
76
|
}
|
|
137
77
|
|
|
138
|
-
emitUserAuthenticated(user.uid);
|
|
78
|
+
authEventService.emitUserAuthenticated(user.uid);
|
|
139
79
|
return user;
|
|
140
80
|
}
|
|
141
81
|
|
|
142
82
|
async signOut(): Promise<void> {
|
|
143
|
-
|
|
144
|
-
this.isGuestMode = false;
|
|
145
|
-
await clearGuestMode();
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
83
|
+
await this.coreService.signOut();
|
|
148
84
|
|
|
149
|
-
|
|
85
|
+
// Clear guest mode if signing out explicitly
|
|
86
|
+
if (this.guestModeService.getIsGuestMode() && this.storageProvider) {
|
|
87
|
+
await this.guestModeService.clear(this.storageProvider);
|
|
88
|
+
}
|
|
150
89
|
}
|
|
151
90
|
|
|
152
91
|
async setGuestMode(): Promise<void> {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
try {
|
|
156
|
-
await this.provider.signOut();
|
|
157
|
-
} catch {
|
|
158
|
-
// Ignore sign out errors when switching to guest mode
|
|
159
|
-
}
|
|
92
|
+
if (!this.storageProvider) {
|
|
93
|
+
throw new Error("Storage provider is required for guest mode");
|
|
160
94
|
}
|
|
161
95
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
96
|
+
// No provider needed for guest mode enablement
|
|
97
|
+
|
|
98
|
+
await this.guestModeService.enable(this.storageProvider);
|
|
165
99
|
}
|
|
166
100
|
|
|
167
101
|
getCurrentUser(): AuthUser | null {
|
|
168
|
-
if (
|
|
102
|
+
if (this.guestModeService.getIsGuestMode()) {
|
|
169
103
|
return null;
|
|
170
104
|
}
|
|
171
|
-
return this.
|
|
105
|
+
return this.coreService.getCurrentUser();
|
|
172
106
|
}
|
|
173
107
|
|
|
174
108
|
getIsGuestMode(): boolean {
|
|
175
|
-
return this.
|
|
109
|
+
return this.guestModeService.getIsGuestMode();
|
|
176
110
|
}
|
|
177
111
|
|
|
178
112
|
onAuthStateChange(callback: (user: AuthUser | null) => void): () => void {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return () => {};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return this.provider.onAuthStateChange((user) => {
|
|
185
|
-
// Don't update if in guest mode
|
|
186
|
-
if (!this.isGuestMode) {
|
|
187
|
-
callback(user);
|
|
188
|
-
} else {
|
|
189
|
-
callback(null);
|
|
190
|
-
}
|
|
191
|
-
});
|
|
113
|
+
const wrappedCallback = this.guestModeService.wrapAuthStateCallback(callback);
|
|
114
|
+
return this.coreService.onAuthStateChange(wrappedCallback);
|
|
192
115
|
}
|
|
193
116
|
|
|
194
117
|
getConfig(): AuthConfig {
|
|
195
|
-
return this.
|
|
118
|
+
return this.coreService.getConfig();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
getCoreService(): AuthCoreService {
|
|
122
|
+
return this.coreService;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
getGuestModeService(): GuestModeService {
|
|
126
|
+
return this.guestModeService;
|
|
196
127
|
}
|
|
197
128
|
}
|
|
198
129
|
|
|
@@ -204,10 +135,13 @@ let authServiceInstance: AuthService | null = null;
|
|
|
204
135
|
*/
|
|
205
136
|
export async function initializeAuthService(
|
|
206
137
|
providerOrAuth: IAuthProvider | Auth,
|
|
207
|
-
config?: Partial<AuthConfig
|
|
138
|
+
config?: Partial<AuthConfig>,
|
|
139
|
+
storageProvider?: IStorageProvider
|
|
208
140
|
): Promise<AuthService> {
|
|
209
141
|
if (!authServiceInstance) {
|
|
210
|
-
|
|
142
|
+
// Initialize package if not already done
|
|
143
|
+
const packageConfig = getAuthPackage()?.getConfig();
|
|
144
|
+
authServiceInstance = new AuthService(config, storageProvider);
|
|
211
145
|
}
|
|
212
146
|
await authServiceInstance.initialize(providerOrAuth);
|
|
213
147
|
return authServiceInstance;
|