@umituz/react-native-auth 2.7.3 → 2.7.6
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 +40 -16
- package/src/application/ports/IAuthRepository.ts +11 -0
- package/src/infrastructure/adapters/StorageProviderAdapter.ts +26 -10
- package/src/infrastructure/adapters/UIProviderAdapter.ts +20 -9
- package/src/infrastructure/providers/FirebaseAuthProvider.ts +3 -2
- package/src/infrastructure/repositories/AuthRepository.ts +91 -0
- package/src/infrastructure/services/AuthPackage.ts +14 -6
- package/src/infrastructure/services/AuthService.ts +67 -119
- package/src/infrastructure/services/GuestModeService.ts +1 -6
- package/src/infrastructure/utils/AuthValidation.ts +15 -14
- package/src/infrastructure/utils/auth-tracker.util.ts +28 -0
- package/src/presentation/components/AccountActions.tsx +38 -50
- package/src/presentation/components/AuthBottomSheet.tsx +4 -4
- package/src/presentation/components/AuthDivider.tsx +0 -1
- package/src/presentation/components/AuthGradientBackground.tsx +1 -1
- package/src/presentation/components/AuthLegalLinks.tsx +7 -8
- package/src/presentation/components/EditProfileActions.tsx +53 -0
- package/src/presentation/components/EditProfileAvatar.tsx +33 -0
- package/src/presentation/components/EditProfileForm.tsx +55 -0
- package/src/presentation/components/LoginForm.tsx +1 -1
- package/src/presentation/components/PasswordMatchIndicator.tsx +6 -14
- package/src/presentation/components/PasswordStrengthIndicator.tsx +11 -17
- package/src/presentation/components/ProfileBenefitsList.tsx +47 -0
- package/src/presentation/components/ProfileSection.tsx +20 -85
- package/src/presentation/components/RegisterForm.tsx +6 -6
- package/src/presentation/components/SocialLoginButtons.tsx +11 -15
- package/src/presentation/hooks/mutations/useAuthMutations.ts +50 -0
- package/src/presentation/hooks/useAccountManagement.ts +1 -0
- package/src/presentation/hooks/useAuthActions.ts +19 -35
- package/src/presentation/hooks/useAuthState.ts +3 -1
- package/src/presentation/hooks/useLoginForm.ts +6 -8
- package/src/presentation/hooks/useProfileUpdate.ts +7 -7
- package/src/presentation/hooks/useRegisterForm.ts +16 -17
- package/src/presentation/hooks/useUserProfile.ts +3 -3
- package/src/presentation/navigation/AuthNavigator.tsx +10 -6
- package/src/presentation/screens/AccountScreen.tsx +9 -1
- package/src/presentation/screens/EditProfileScreen.tsx +40 -185
- package/src/presentation/screens/LoginScreen.tsx +4 -6
- package/src/presentation/screens/RegisterScreen.tsx +4 -6
- package/src/presentation/stores/authModalStore.ts +2 -1
- package/src/types/external.d.ts +31 -45
- package/src/infrastructure/services/AuthCoreService.ts +0 -138
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Edit Profile Screen
|
|
3
|
-
* Pure UI for profile editing
|
|
4
|
-
* Business logic provided via props
|
|
3
|
+
* Pure UI for profile editing - Composition only
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
import React from "react";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
import { View, ScrollView, StyleSheet, ActivityIndicator } from "react-native";
|
|
8
|
+
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
9
|
+
import { EditProfileAvatar } from "../components/EditProfileAvatar";
|
|
10
|
+
import { EditProfileForm } from "../components/EditProfileForm";
|
|
11
|
+
import { EditProfileActions } from "../components/EditProfileActions";
|
|
12
|
+
|
|
13
|
+
export interface EditProfileLabels {
|
|
14
|
+
title: string;
|
|
15
|
+
displayNameLabel: string;
|
|
16
|
+
displayNamePlaceholder: string;
|
|
17
|
+
emailLabel: string;
|
|
18
|
+
emailPlaceholder: string;
|
|
19
|
+
photoLabel: string;
|
|
20
|
+
changePhotoButton: string;
|
|
21
|
+
saveButton: string;
|
|
22
|
+
cancelButton: string;
|
|
23
|
+
}
|
|
19
24
|
|
|
20
25
|
export interface EditProfileConfig {
|
|
21
26
|
displayName: string;
|
|
@@ -28,26 +33,14 @@ export interface EditProfileConfig {
|
|
|
28
33
|
onChangePhoto?: () => void;
|
|
29
34
|
onSave: () => void;
|
|
30
35
|
onCancel?: () => void;
|
|
31
|
-
labels:
|
|
32
|
-
title: string;
|
|
33
|
-
displayNameLabel: string;
|
|
34
|
-
displayNamePlaceholder: string;
|
|
35
|
-
emailLabel: string;
|
|
36
|
-
emailPlaceholder: string;
|
|
37
|
-
photoLabel: string;
|
|
38
|
-
changePhotoButton: string;
|
|
39
|
-
saveButton: string;
|
|
40
|
-
cancelButton: string;
|
|
41
|
-
};
|
|
36
|
+
labels: EditProfileLabels;
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
export interface EditProfileScreenProps {
|
|
45
40
|
config: EditProfileConfig;
|
|
46
41
|
}
|
|
47
42
|
|
|
48
|
-
export const EditProfileScreen: React.FC<EditProfileScreenProps> = ({
|
|
49
|
-
config,
|
|
50
|
-
}) => {
|
|
43
|
+
export const EditProfileScreen: React.FC<EditProfileScreenProps> = ({ config }) => {
|
|
51
44
|
const tokens = useAppDesignTokens();
|
|
52
45
|
|
|
53
46
|
if (config.isLoading) {
|
|
@@ -63,115 +56,29 @@ export const EditProfileScreen: React.FC<EditProfileScreenProps> = ({
|
|
|
63
56
|
style={[styles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}
|
|
64
57
|
contentContainerStyle={styles.content}
|
|
65
58
|
>
|
|
66
|
-
<
|
|
59
|
+
<AtomicText type="headlineSmall" style={styles.title}>
|
|
67
60
|
{config.labels.title}
|
|
68
|
-
</
|
|
69
|
-
|
|
70
|
-
{/* Avatar */}
|
|
71
|
-
<View style={styles.avatarContainer}>
|
|
72
|
-
<Avatar
|
|
73
|
-
uri={config.photoURL || undefined}
|
|
74
|
-
name={config.displayName}
|
|
75
|
-
size="xl"
|
|
76
|
-
shape="circle"
|
|
77
|
-
/>
|
|
78
|
-
</View>
|
|
79
|
-
|
|
80
|
-
{/* Display Name */}
|
|
81
|
-
<View style={styles.field}>
|
|
82
|
-
<Text style={[styles.label, { color: tokens.colors.textSecondary }]}>
|
|
83
|
-
{config.labels.displayNameLabel}
|
|
84
|
-
</Text>
|
|
85
|
-
<TextInput
|
|
86
|
-
style={[
|
|
87
|
-
styles.input,
|
|
88
|
-
{
|
|
89
|
-
backgroundColor: tokens.colors.surface,
|
|
90
|
-
color: tokens.colors.text,
|
|
91
|
-
borderColor: tokens.colors.border,
|
|
92
|
-
},
|
|
93
|
-
]}
|
|
94
|
-
value={config.displayName}
|
|
95
|
-
onChangeText={config.onChangeDisplayName}
|
|
96
|
-
placeholder={config.labels.displayNamePlaceholder}
|
|
97
|
-
placeholderTextColor={tokens.colors.textTertiary}
|
|
98
|
-
/>
|
|
99
|
-
</View>
|
|
100
|
-
|
|
101
|
-
{/* Email */}
|
|
102
|
-
<View style={styles.field}>
|
|
103
|
-
<Text style={[styles.label, { color: tokens.colors.textSecondary }]}>
|
|
104
|
-
{config.labels.emailLabel}
|
|
105
|
-
</Text>
|
|
106
|
-
<TextInput
|
|
107
|
-
style={[
|
|
108
|
-
styles.input,
|
|
109
|
-
{
|
|
110
|
-
backgroundColor: tokens.colors.surface,
|
|
111
|
-
color: tokens.colors.text,
|
|
112
|
-
borderColor: tokens.colors.border,
|
|
113
|
-
},
|
|
114
|
-
]}
|
|
115
|
-
value={config.email}
|
|
116
|
-
onChangeText={config.onChangeEmail}
|
|
117
|
-
placeholder={config.labels.emailPlaceholder}
|
|
118
|
-
placeholderTextColor={tokens.colors.textTertiary}
|
|
119
|
-
keyboardType="email-address"
|
|
120
|
-
autoCapitalize="none"
|
|
121
|
-
/>
|
|
122
|
-
</View>
|
|
61
|
+
</AtomicText>
|
|
123
62
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
{config.labels.photoLabel}
|
|
129
|
-
</Text>
|
|
130
|
-
<TouchableOpacity
|
|
131
|
-
style={[
|
|
132
|
-
styles.photoButton,
|
|
133
|
-
{ backgroundColor: tokens.colors.surface, borderColor: tokens.colors.border },
|
|
134
|
-
]}
|
|
135
|
-
onPress={config.onChangePhoto}
|
|
136
|
-
>
|
|
137
|
-
<Text style={[styles.photoButtonText, { color: tokens.colors.primary }]}>
|
|
138
|
-
{config.labels.changePhotoButton}
|
|
139
|
-
</Text>
|
|
140
|
-
</TouchableOpacity>
|
|
141
|
-
</View>
|
|
142
|
-
)}
|
|
63
|
+
<EditProfileAvatar
|
|
64
|
+
photoURL={config.photoURL}
|
|
65
|
+
displayName={config.displayName}
|
|
66
|
+
/>
|
|
143
67
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
{config.isSaving ? (
|
|
152
|
-
<ActivityIndicator size="small" color={tokens.colors.onPrimary} />
|
|
153
|
-
) : (
|
|
154
|
-
<Text style={[styles.saveButtonText, { color: tokens.colors.onPrimary }]}>
|
|
155
|
-
{config.labels.saveButton}
|
|
156
|
-
</Text>
|
|
157
|
-
)}
|
|
158
|
-
</TouchableOpacity>
|
|
68
|
+
<EditProfileForm
|
|
69
|
+
displayName={config.displayName}
|
|
70
|
+
email={config.email}
|
|
71
|
+
onChangeDisplayName={config.onChangeDisplayName}
|
|
72
|
+
onChangeEmail={config.onChangeEmail}
|
|
73
|
+
labels={config.labels}
|
|
74
|
+
/>
|
|
159
75
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
onPress={config.onCancel}
|
|
167
|
-
disabled={config.isSaving}
|
|
168
|
-
>
|
|
169
|
-
<Text style={[styles.cancelButtonText, { color: tokens.colors.text }]}>
|
|
170
|
-
{config.labels.cancelButton}
|
|
171
|
-
</Text>
|
|
172
|
-
</TouchableOpacity>
|
|
173
|
-
)}
|
|
174
|
-
</View>
|
|
76
|
+
<EditProfileActions
|
|
77
|
+
isSaving={config.isSaving}
|
|
78
|
+
onSave={config.onSave}
|
|
79
|
+
onCancel={config.onCancel}
|
|
80
|
+
labels={config.labels}
|
|
81
|
+
/>
|
|
175
82
|
</ScrollView>
|
|
176
83
|
);
|
|
177
84
|
};
|
|
@@ -189,59 +96,7 @@ const styles = StyleSheet.create({
|
|
|
189
96
|
alignItems: "center",
|
|
190
97
|
},
|
|
191
98
|
title: {
|
|
192
|
-
fontSize: 24,
|
|
193
|
-
fontWeight: "600",
|
|
194
99
|
marginBottom: 24,
|
|
195
100
|
},
|
|
196
|
-
avatarContainer: {
|
|
197
|
-
alignItems: "center",
|
|
198
|
-
marginBottom: 24,
|
|
199
|
-
},
|
|
200
|
-
field: {
|
|
201
|
-
marginBottom: 20,
|
|
202
|
-
},
|
|
203
|
-
label: {
|
|
204
|
-
fontSize: 14,
|
|
205
|
-
fontWeight: "500",
|
|
206
|
-
marginBottom: 8,
|
|
207
|
-
},
|
|
208
|
-
input: {
|
|
209
|
-
borderWidth: 1,
|
|
210
|
-
borderRadius: 8,
|
|
211
|
-
padding: 12,
|
|
212
|
-
fontSize: 16,
|
|
213
|
-
},
|
|
214
|
-
photoButton: {
|
|
215
|
-
borderWidth: 1,
|
|
216
|
-
borderRadius: 8,
|
|
217
|
-
padding: 12,
|
|
218
|
-
alignItems: "center",
|
|
219
|
-
},
|
|
220
|
-
photoButtonText: {
|
|
221
|
-
fontSize: 14,
|
|
222
|
-
fontWeight: "500",
|
|
223
|
-
},
|
|
224
|
-
actions: {
|
|
225
|
-
marginTop: 32,
|
|
226
|
-
gap: 12,
|
|
227
|
-
},
|
|
228
|
-
saveButton: {
|
|
229
|
-
padding: 16,
|
|
230
|
-
borderRadius: 8,
|
|
231
|
-
alignItems: "center",
|
|
232
|
-
},
|
|
233
|
-
saveButtonText: {
|
|
234
|
-
fontSize: 16,
|
|
235
|
-
fontWeight: "600",
|
|
236
|
-
},
|
|
237
|
-
cancelButton: {
|
|
238
|
-
padding: 16,
|
|
239
|
-
borderRadius: 8,
|
|
240
|
-
alignItems: "center",
|
|
241
|
-
borderWidth: 1,
|
|
242
|
-
},
|
|
243
|
-
cancelButtonText: {
|
|
244
|
-
fontSize: 16,
|
|
245
|
-
fontWeight: "500",
|
|
246
|
-
},
|
|
247
101
|
});
|
|
102
|
+
|
|
@@ -4,23 +4,20 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
|
-
import { useNavigation } from "@react-navigation/native";
|
|
7
|
+
import { useNavigation, NavigationProp } from "@react-navigation/native";
|
|
8
8
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
9
9
|
import type { AuthStackParamList } from "../navigation/AuthNavigator";
|
|
10
|
-
import type { StackNavigationProp } from "@react-navigation/stack";
|
|
11
10
|
import { AuthContainer } from "../components/AuthContainer";
|
|
12
11
|
import { AuthHeader } from "../components/AuthHeader";
|
|
13
12
|
import { AuthFormCard } from "../components/AuthFormCard";
|
|
14
13
|
import { LoginForm } from "../components/LoginForm";
|
|
15
14
|
|
|
16
|
-
type LoginScreenNavigationProp = any;
|
|
17
|
-
|
|
18
15
|
export const LoginScreen: React.FC = () => {
|
|
19
16
|
const { t } = useLocalization();
|
|
20
|
-
const navigation = useNavigation();
|
|
17
|
+
const navigation = useNavigation<NavigationProp<AuthStackParamList>>();
|
|
21
18
|
|
|
22
19
|
const handleNavigateToRegister = () => {
|
|
23
|
-
navigation.navigate("Register"
|
|
20
|
+
navigation.navigate("Register");
|
|
24
21
|
};
|
|
25
22
|
|
|
26
23
|
return (
|
|
@@ -32,3 +29,4 @@ export const LoginScreen: React.FC = () => {
|
|
|
32
29
|
</AuthContainer>
|
|
33
30
|
);
|
|
34
31
|
};
|
|
32
|
+
|
|
@@ -4,17 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from "react";
|
|
7
|
-
import { useNavigation } from "@react-navigation/native";
|
|
7
|
+
import { useNavigation, NavigationProp } from "@react-navigation/native";
|
|
8
8
|
import { useLocalization } from "@umituz/react-native-localization";
|
|
9
9
|
import type { AuthStackParamList } from "../navigation/AuthNavigator";
|
|
10
|
-
import type { StackNavigationProp } from "@react-navigation/stack";
|
|
11
10
|
import { AuthContainer } from "../components/AuthContainer";
|
|
12
11
|
import { AuthHeader } from "../components/AuthHeader";
|
|
13
12
|
import { AuthFormCard } from "../components/AuthFormCard";
|
|
14
13
|
import { RegisterForm } from "../components/RegisterForm";
|
|
15
14
|
|
|
16
|
-
type RegisterScreenNavigationProp = any;
|
|
17
|
-
|
|
18
15
|
export interface RegisterScreenProps {
|
|
19
16
|
termsUrl?: string;
|
|
20
17
|
privacyUrl?: string;
|
|
@@ -29,10 +26,10 @@ export const RegisterScreen: React.FC<RegisterScreenProps> = ({
|
|
|
29
26
|
onPrivacyPress,
|
|
30
27
|
}) => {
|
|
31
28
|
const { t } = useLocalization();
|
|
32
|
-
const navigation = useNavigation();
|
|
29
|
+
const navigation = useNavigation<NavigationProp<AuthStackParamList>>();
|
|
33
30
|
|
|
34
31
|
const handleNavigateToLogin = () => {
|
|
35
|
-
navigation.navigate("Login"
|
|
32
|
+
navigation.navigate("Login");
|
|
36
33
|
};
|
|
37
34
|
|
|
38
35
|
return (
|
|
@@ -50,3 +47,4 @@ export const RegisterScreen: React.FC<RegisterScreenProps> = ({
|
|
|
50
47
|
</AuthContainer>
|
|
51
48
|
);
|
|
52
49
|
};
|
|
50
|
+
|
|
@@ -64,11 +64,12 @@ export const useAuthModalStore = create<AuthModalStore>((set, get) => ({
|
|
|
64
64
|
executePendingCallback: () => {
|
|
65
65
|
const { pendingCallback } = get();
|
|
66
66
|
if (pendingCallback) {
|
|
67
|
-
pendingCallback();
|
|
67
|
+
void pendingCallback();
|
|
68
68
|
set({ pendingCallback: null });
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
71
|
|
|
72
|
+
|
|
72
73
|
clearPendingCallback: () => {
|
|
73
74
|
set({ pendingCallback: null });
|
|
74
75
|
},
|
package/src/types/external.d.ts
CHANGED
|
@@ -1,46 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Type declarations for external dependencies
|
|
3
|
-
*
|
|
2
|
+
* Type declarations for external dependencies that might not have types
|
|
3
|
+
* or where package resolution is failing in the current environment.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export const defaultTheme: any;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
declare module '@umituz/react-native-design-system' {
|
|
12
|
-
export const AtomicInput: any;
|
|
13
|
-
export const AtomicButton: any;
|
|
14
|
-
export const AtomicText: any;
|
|
15
|
-
export const AtomicView: any;
|
|
16
|
-
}
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
17
8
|
|
|
18
|
-
|
|
19
|
-
export function useTranslation(): {
|
|
20
|
-
t: (key: string, params?: Record<string, any>) => string;
|
|
21
|
-
};
|
|
22
|
-
export function useLocalization(): {
|
|
23
|
-
t: (key: string, params?: Record<string, any>) => string;
|
|
24
|
-
};
|
|
25
|
-
}
|
|
9
|
+
import React from 'react';
|
|
26
10
|
|
|
27
|
-
declare module '
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
declare module '@
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
declare module '@umituz/react-native-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
setString: (key: string, value: string) => Promise<void>;
|
|
40
|
-
removeItem: (key: string) => Promise<void>;
|
|
41
|
-
};
|
|
42
|
-
export function unwrap(result: any, defaultValue: any): any;
|
|
43
|
-
}
|
|
11
|
+
declare module 'expo-device';
|
|
12
|
+
declare module 'expo-application';
|
|
13
|
+
declare module '@umituz/react-native-uuid';
|
|
14
|
+
declare module 'rn-emoji-keyboard';
|
|
15
|
+
declare module '@react-navigation/bottom-tabs';
|
|
16
|
+
declare module '@umituz/react-native-haptics';
|
|
17
|
+
declare module 'expo-clipboard';
|
|
18
|
+
declare module 'expo-sharing';
|
|
19
|
+
declare module 'expo-file-system/build/legacy';
|
|
20
|
+
declare module '@umituz/react-native-design-system-theme';
|
|
21
|
+
declare module '@sentry/react-native';
|
|
22
|
+
declare module '@sentry/types';
|
|
44
23
|
|
|
45
24
|
declare module 'react-native-safe-area-context' {
|
|
46
25
|
export function useSafeAreaInsets(): {
|
|
@@ -52,17 +31,24 @@ declare module 'react-native-safe-area-context' {
|
|
|
52
31
|
}
|
|
53
32
|
|
|
54
33
|
declare module 'expo-linear-gradient' {
|
|
55
|
-
|
|
56
|
-
export const LinearGradient: ComponentType<any>;
|
|
34
|
+
export const LinearGradient: React.ComponentType<any>;
|
|
57
35
|
}
|
|
58
36
|
|
|
59
37
|
declare module '@react-navigation/stack' {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
38
|
+
export function createStackNavigator<T = any>(): {
|
|
39
|
+
Navigator: React.ComponentType<any>;
|
|
40
|
+
Screen: React.ComponentType<any>;
|
|
41
|
+
};
|
|
42
|
+
export type StackNavigationProp<T = any> = any;
|
|
43
|
+
export type StackScreenProps<T = any, K = any> = any;
|
|
63
44
|
}
|
|
64
45
|
|
|
65
46
|
declare module '@react-navigation/native' {
|
|
66
|
-
export function useNavigation():
|
|
47
|
+
export function useNavigation<T = any>(): T;
|
|
67
48
|
export function useFocusEffect(effect: () => void | (() => void)): void;
|
|
68
|
-
|
|
49
|
+
export type NavigationProp<T = any> = {
|
|
50
|
+
navigate: (name: string, params?: any) => void;
|
|
51
|
+
goBack: () => void;
|
|
52
|
+
reset: (state: any) => void;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -1,138 +0,0 @@
|
|
|
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
|
-
}
|