@umituz/web-firebase 3.0.1 → 3.2.0
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 +166 -25
- package/package.json +3 -1
- package/src/domain/config/auth.config.ts +200 -0
- package/src/domain/index.ts +3 -0
- package/src/domains/auth/services/auth.service.ts +75 -154
- package/src/infrastructure/firebase/auth.adapter.ts +204 -14
- package/src/presentation/hooks/useAuth.ts +288 -47
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auth Hook
|
|
3
|
-
* @description React hook for authentication
|
|
4
|
-
* Uses new domain services - no adapter injection needed
|
|
3
|
+
* @description React hook for authentication with Google & Apple OAuth
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
import { useCallback, useEffect, useState } from 'react'
|
|
@@ -10,34 +9,69 @@ import type { User as FirestoreUser } from '../../domains/firestore/entities'
|
|
|
10
9
|
import { authService } from '../../domains/auth/services'
|
|
11
10
|
import { firestoreService } from '../../domains/firestore/services'
|
|
12
11
|
import { useFirebaseContext } from '../providers/FirebaseProvider'
|
|
12
|
+
import { getAuthConfig } from '../../domain/config/auth.config'
|
|
13
13
|
|
|
14
14
|
export interface UseAuthResult {
|
|
15
|
+
// State
|
|
15
16
|
firebaseUser: FirebaseUser | null
|
|
16
17
|
user: FirestoreUser | null
|
|
17
18
|
loading: boolean
|
|
18
19
|
error: Error | null
|
|
19
20
|
isAuthenticated: boolean
|
|
20
21
|
isEmailVerified: boolean
|
|
22
|
+
|
|
23
|
+
// Email/Password Auth
|
|
21
24
|
signIn: (email: string, password: string) => Promise<FirebaseUser>
|
|
22
25
|
signUp: (email: string, password: string, displayName: string) => Promise<FirebaseUser>
|
|
23
|
-
|
|
26
|
+
|
|
27
|
+
// OAuth Providers
|
|
28
|
+
signInWithGoogle: (useRedirect?: boolean) => Promise<FirebaseUser>
|
|
29
|
+
signInWithApple: (useRedirect?: boolean) => Promise<FirebaseUser>
|
|
30
|
+
|
|
31
|
+
// Provider Management
|
|
32
|
+
linkGoogle: () => Promise<void>
|
|
33
|
+
linkApple: () => Promise<void>
|
|
34
|
+
unlinkProvider: (providerId: string) => Promise<void>
|
|
35
|
+
|
|
36
|
+
// Account Management
|
|
24
37
|
signOut: () => Promise<void>
|
|
25
38
|
sendPasswordReset: (email: string) => Promise<void>
|
|
26
39
|
resendEmailVerification: () => Promise<void>
|
|
27
40
|
updateProfile: (updates: { displayName?: string; photoURL?: string }) => Promise<void>
|
|
41
|
+
updateEmail: (newEmail: string, password: string) => Promise<void>
|
|
42
|
+
updatePassword: (currentPassword: string, newPassword: string) => Promise<void>
|
|
43
|
+
deleteAccount: (password: string) => Promise<void>
|
|
44
|
+
|
|
45
|
+
// Token Management
|
|
46
|
+
getIdToken: (forceRefresh?: boolean) => Promise<string>
|
|
47
|
+
refreshToken: () => Promise<void>
|
|
48
|
+
|
|
49
|
+
// User Data
|
|
28
50
|
refreshUser: () => Promise<void>
|
|
51
|
+
|
|
52
|
+
// Config
|
|
53
|
+
googleEnabled: boolean
|
|
54
|
+
appleEnabled: boolean
|
|
55
|
+
emailPasswordEnabled: boolean
|
|
29
56
|
}
|
|
30
57
|
|
|
31
58
|
export function useAuth(): UseAuthResult {
|
|
32
59
|
const { instances, loading, error } = useFirebaseContext()
|
|
33
60
|
const [user, setUser] = useState<FirestoreUser | null>(null)
|
|
61
|
+
const config = getAuthConfig()
|
|
34
62
|
|
|
63
|
+
// Fetch user data from Firestore
|
|
35
64
|
useEffect(() => {
|
|
36
65
|
const fetchUser = async () => {
|
|
37
66
|
const currentUser = instances?.auth.currentUser
|
|
38
67
|
if (currentUser) {
|
|
39
|
-
|
|
40
|
-
|
|
68
|
+
try {
|
|
69
|
+
const userData = await firestoreService.getUser(currentUser.uid)
|
|
70
|
+
setUser(userData)
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.error('Error fetching user data:', err)
|
|
73
|
+
setUser(null)
|
|
74
|
+
}
|
|
41
75
|
} else {
|
|
42
76
|
setUser(null)
|
|
43
77
|
}
|
|
@@ -45,78 +79,237 @@ export function useAuth(): UseAuthResult {
|
|
|
45
79
|
fetchUser()
|
|
46
80
|
}, [instances?.auth.currentUser])
|
|
47
81
|
|
|
82
|
+
// Email/Password Sign In
|
|
48
83
|
const signIn = useCallback(async (email: string, password: string) => {
|
|
49
84
|
const result = await authService.signIn(email, password)
|
|
50
85
|
return result.user
|
|
51
86
|
}, [])
|
|
52
87
|
|
|
88
|
+
// Email/Password Sign Up
|
|
53
89
|
const signUp = useCallback(async (email: string, password: string, displayName: string) => {
|
|
54
90
|
const userCredential = await authService.signUp(email, password, displayName)
|
|
55
91
|
|
|
56
|
-
// Create user profile in Firestore
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
// Create user profile in Firestore if auto-create is enabled
|
|
93
|
+
if (config.shouldCreateUserDocument()) {
|
|
94
|
+
const now = Date.now()
|
|
95
|
+
const defaultSettings = config.getConfig().defaultUserSettings
|
|
96
|
+
|
|
97
|
+
await firestoreService.createUser(userCredential.user.uid, {
|
|
98
|
+
profile: {
|
|
99
|
+
id: userCredential.user.uid,
|
|
100
|
+
email: email,
|
|
101
|
+
displayName: displayName,
|
|
102
|
+
createdAt: now,
|
|
103
|
+
updatedAt: now,
|
|
104
|
+
lastLoginAt: now,
|
|
105
|
+
emailVerified: false,
|
|
106
|
+
},
|
|
107
|
+
settings: {
|
|
108
|
+
theme: defaultSettings?.theme ?? 'system',
|
|
109
|
+
language: defaultSettings?.language ?? 'en',
|
|
110
|
+
timezone: defaultSettings?.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
111
|
+
currency: defaultSettings?.currency ?? 'USD',
|
|
112
|
+
notifications: {
|
|
113
|
+
email: defaultSettings?.notifications?.email ?? true,
|
|
114
|
+
push: defaultSettings?.notifications?.push ?? true,
|
|
115
|
+
marketing: defaultSettings?.notifications?.marketing ?? false,
|
|
116
|
+
security: defaultSettings?.notifications?.security ?? true,
|
|
117
|
+
weeklyDigest: defaultSettings?.notifications?.weeklyDigest ?? false,
|
|
118
|
+
},
|
|
119
|
+
privacy: {
|
|
120
|
+
profileVisibility: defaultSettings?.privacy?.profileVisibility ?? 'public',
|
|
121
|
+
showEmail: defaultSettings?.privacy?.showEmail ?? false,
|
|
122
|
+
showPhone: defaultSettings?.privacy?.showPhone ?? false,
|
|
123
|
+
dataSharing: defaultSettings?.privacy?.dataSharing ?? false,
|
|
124
|
+
},
|
|
79
125
|
},
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
126
|
+
subscription: {
|
|
127
|
+
plan: config.getConfig().defaultSubscriptionPlan ?? 'free',
|
|
128
|
+
status: 'active',
|
|
129
|
+
cancelAtPeriodEnd: false,
|
|
130
|
+
createdAt: now,
|
|
131
|
+
updatedAt: now,
|
|
85
132
|
},
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
plan: 'free',
|
|
89
|
-
status: 'active',
|
|
90
|
-
cancelAtPeriodEnd: false,
|
|
91
|
-
createdAt: now,
|
|
92
|
-
updatedAt: now,
|
|
93
|
-
},
|
|
94
|
-
})
|
|
133
|
+
})
|
|
134
|
+
}
|
|
95
135
|
|
|
96
136
|
return userCredential.user
|
|
97
|
-
}, [])
|
|
137
|
+
}, [config])
|
|
138
|
+
|
|
139
|
+
// Google Sign In
|
|
140
|
+
const signInWithGoogleCallback = useCallback(async (useRedirect = false): Promise<FirebaseUser> => {
|
|
141
|
+
const result = await authService.signInWithGoogle(useRedirect)
|
|
142
|
+
|
|
143
|
+
// Create user profile in Firestore if it doesn't exist
|
|
144
|
+
if (config.shouldCreateUserDocument()) {
|
|
145
|
+
const existingUser = await firestoreService.getUser(result.user.uid)
|
|
146
|
+
if (!existingUser) {
|
|
147
|
+
const now = Date.now()
|
|
148
|
+
const defaultSettings = config.getConfig().defaultUserSettings
|
|
149
|
+
|
|
150
|
+
await firestoreService.createUser(result.user.uid, {
|
|
151
|
+
profile: {
|
|
152
|
+
id: result.user.uid,
|
|
153
|
+
email: result.user.email ?? '',
|
|
154
|
+
displayName: result.user.displayName ?? '',
|
|
155
|
+
photoURL: result.user.photoURL,
|
|
156
|
+
createdAt: now,
|
|
157
|
+
updatedAt: now,
|
|
158
|
+
lastLoginAt: now,
|
|
159
|
+
emailVerified: result.user.emailVerified,
|
|
160
|
+
},
|
|
161
|
+
settings: {
|
|
162
|
+
theme: defaultSettings?.theme ?? 'system',
|
|
163
|
+
language: defaultSettings?.language ?? 'en',
|
|
164
|
+
timezone: defaultSettings?.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
165
|
+
currency: defaultSettings?.currency ?? 'USD',
|
|
166
|
+
notifications: {
|
|
167
|
+
email: defaultSettings?.notifications?.email ?? true,
|
|
168
|
+
push: defaultSettings?.notifications?.push ?? true,
|
|
169
|
+
marketing: defaultSettings?.notifications?.marketing ?? false,
|
|
170
|
+
security: defaultSettings?.notifications?.security ?? true,
|
|
171
|
+
weeklyDigest: defaultSettings?.notifications?.weeklyDigest ?? false,
|
|
172
|
+
},
|
|
173
|
+
privacy: {
|
|
174
|
+
profileVisibility: defaultSettings?.privacy?.profileVisibility ?? 'public',
|
|
175
|
+
showEmail: defaultSettings?.privacy?.showEmail ?? false,
|
|
176
|
+
showPhone: defaultSettings?.privacy?.showPhone ?? false,
|
|
177
|
+
dataSharing: defaultSettings?.privacy?.dataSharing ?? false,
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
subscription: {
|
|
181
|
+
plan: config.getConfig().defaultSubscriptionPlan ?? 'free',
|
|
182
|
+
status: 'active',
|
|
183
|
+
cancelAtPeriodEnd: false,
|
|
184
|
+
createdAt: now,
|
|
185
|
+
updatedAt: now,
|
|
186
|
+
},
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
}
|
|
98
190
|
|
|
99
|
-
const signInWithGoogleCallback = useCallback(async () => {
|
|
100
|
-
const result = await authService.signInWithGoogle()
|
|
101
191
|
return result.user
|
|
102
|
-
}, [])
|
|
192
|
+
}, [config])
|
|
193
|
+
|
|
194
|
+
// Apple Sign In
|
|
195
|
+
const signInWithAppleCallback = useCallback(async (useRedirect = false): Promise<FirebaseUser> => {
|
|
196
|
+
const result = await authService.signInWithApple(useRedirect)
|
|
197
|
+
|
|
198
|
+
// Create user profile in Firestore if it doesn't exist
|
|
199
|
+
if (config.shouldCreateUserDocument()) {
|
|
200
|
+
const existingUser = await firestoreService.getUser(result.user.uid)
|
|
201
|
+
if (!existingUser) {
|
|
202
|
+
const now = Date.now()
|
|
203
|
+
const defaultSettings = config.getConfig().defaultUserSettings
|
|
204
|
+
|
|
205
|
+
await firestoreService.createUser(result.user.uid, {
|
|
206
|
+
profile: {
|
|
207
|
+
id: result.user.uid,
|
|
208
|
+
email: result.user.email ?? '',
|
|
209
|
+
displayName: result.user.displayName ?? '',
|
|
210
|
+
photoURL: result.user.photoURL,
|
|
211
|
+
createdAt: now,
|
|
212
|
+
updatedAt: now,
|
|
213
|
+
lastLoginAt: now,
|
|
214
|
+
emailVerified: result.user.emailVerified,
|
|
215
|
+
},
|
|
216
|
+
settings: {
|
|
217
|
+
theme: defaultSettings?.theme ?? 'system',
|
|
218
|
+
language: defaultSettings?.language ?? 'en',
|
|
219
|
+
timezone: defaultSettings?.timezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
220
|
+
currency: defaultSettings?.currency ?? 'USD',
|
|
221
|
+
notifications: {
|
|
222
|
+
email: defaultSettings?.notifications?.email ?? true,
|
|
223
|
+
push: defaultSettings?.notifications?.push ?? true,
|
|
224
|
+
marketing: defaultSettings?.notifications?.marketing ?? false,
|
|
225
|
+
security: defaultSettings?.notifications?.security ?? true,
|
|
226
|
+
weeklyDigest: defaultSettings?.notifications?.weeklyDigest ?? false,
|
|
227
|
+
},
|
|
228
|
+
privacy: {
|
|
229
|
+
profileVisibility: defaultSettings?.privacy?.profileVisibility ?? 'public',
|
|
230
|
+
showEmail: defaultSettings?.privacy?.showEmail ?? false,
|
|
231
|
+
showPhone: defaultSettings?.privacy?.showPhone ?? false,
|
|
232
|
+
dataSharing: defaultSettings?.privacy?.dataSharing ?? false,
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
subscription: {
|
|
236
|
+
plan: config.getConfig().defaultSubscriptionPlan ?? 'free',
|
|
237
|
+
status: 'active',
|
|
238
|
+
cancelAtPeriodEnd: false,
|
|
239
|
+
createdAt: now,
|
|
240
|
+
updatedAt: now,
|
|
241
|
+
},
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return result.user
|
|
247
|
+
}, [config])
|
|
248
|
+
|
|
249
|
+
// Provider Linking
|
|
250
|
+
const linkGoogleCallback = useCallback(async () => {
|
|
251
|
+
await authService.linkGoogle()
|
|
252
|
+
// Refresh user data
|
|
253
|
+
const currentUser = instances?.auth.currentUser
|
|
254
|
+
if (currentUser) {
|
|
255
|
+
const userData = await firestoreService.getUser(currentUser.uid)
|
|
256
|
+
setUser(userData)
|
|
257
|
+
}
|
|
258
|
+
}, [instances])
|
|
259
|
+
|
|
260
|
+
const linkAppleCallback = useCallback(async () => {
|
|
261
|
+
await authService.linkApple()
|
|
262
|
+
// Refresh user data
|
|
263
|
+
const currentUser = instances?.auth.currentUser
|
|
264
|
+
if (currentUser) {
|
|
265
|
+
const userData = await firestoreService.getUser(currentUser.uid)
|
|
266
|
+
setUser(userData)
|
|
267
|
+
}
|
|
268
|
+
}, [instances])
|
|
103
269
|
|
|
270
|
+
const unlinkProviderCallback = useCallback(async (providerId: string) => {
|
|
271
|
+
await authService.unlinkProvider(providerId)
|
|
272
|
+
// Refresh user data
|
|
273
|
+
const currentUser = instances?.auth.currentUser
|
|
274
|
+
if (currentUser) {
|
|
275
|
+
const userData = await firestoreService.getUser(currentUser.uid)
|
|
276
|
+
setUser(userData)
|
|
277
|
+
}
|
|
278
|
+
}, [instances])
|
|
279
|
+
|
|
280
|
+
// Sign Out
|
|
104
281
|
const signOut = useCallback(async () => {
|
|
105
282
|
await authService.signOut()
|
|
283
|
+
setUser(null)
|
|
106
284
|
}, [])
|
|
107
285
|
|
|
286
|
+
// Password Reset
|
|
108
287
|
const sendPasswordReset = useCallback(async (email: string) => {
|
|
109
288
|
await authService.sendPasswordReset(email)
|
|
110
289
|
}, [])
|
|
111
290
|
|
|
291
|
+
// Email Verification
|
|
112
292
|
const resendEmailVerification = useCallback(async () => {
|
|
113
293
|
await authService.resendEmailVerification()
|
|
114
294
|
}, [])
|
|
115
295
|
|
|
296
|
+
// Update Profile
|
|
116
297
|
const updateProfile = useCallback(async (updates: { displayName?: string; photoURL?: string }) => {
|
|
117
298
|
await authService.updateProfile(updates)
|
|
118
299
|
|
|
119
|
-
// Refresh user data from Firestore
|
|
300
|
+
// Refresh user data from Firestore
|
|
301
|
+
const currentUser = instances?.auth.currentUser
|
|
302
|
+
if (currentUser) {
|
|
303
|
+
const userData = await firestoreService.getUser(currentUser.uid)
|
|
304
|
+
setUser(userData)
|
|
305
|
+
}
|
|
306
|
+
}, [instances])
|
|
307
|
+
|
|
308
|
+
// Update Email
|
|
309
|
+
const updateEmailCallback = useCallback(async (newEmail: string, password: string) => {
|
|
310
|
+
await authService.updateEmail(newEmail, password)
|
|
311
|
+
|
|
312
|
+
// Refresh user data
|
|
120
313
|
const currentUser = instances?.auth.currentUser
|
|
121
314
|
if (currentUser) {
|
|
122
315
|
const userData = await firestoreService.getUser(currentUser.uid)
|
|
@@ -124,6 +317,27 @@ export function useAuth(): UseAuthResult {
|
|
|
124
317
|
}
|
|
125
318
|
}, [instances])
|
|
126
319
|
|
|
320
|
+
// Update Password
|
|
321
|
+
const updatePasswordCallback = useCallback(async (currentPassword: string, newPassword: string) => {
|
|
322
|
+
await authService.updatePassword(currentPassword, newPassword)
|
|
323
|
+
}, [])
|
|
324
|
+
|
|
325
|
+
// Delete Account
|
|
326
|
+
const deleteAccountCallback = useCallback(async (password: string) => {
|
|
327
|
+
await authService.deleteAccount(password)
|
|
328
|
+
setUser(null)
|
|
329
|
+
}, [])
|
|
330
|
+
|
|
331
|
+
// Token Management
|
|
332
|
+
const getIdTokenCallback = useCallback(async (forceRefresh = false) => {
|
|
333
|
+
return await authService.getIdToken(forceRefresh)
|
|
334
|
+
}, [])
|
|
335
|
+
|
|
336
|
+
const refreshTokenCallback = useCallback(async () => {
|
|
337
|
+
await authService.refreshToken()
|
|
338
|
+
}, [])
|
|
339
|
+
|
|
340
|
+
// Refresh User Data
|
|
127
341
|
const refreshUser = useCallback(async () => {
|
|
128
342
|
const currentUser = instances?.auth.currentUser
|
|
129
343
|
if (currentUser) {
|
|
@@ -133,19 +347,46 @@ export function useAuth(): UseAuthResult {
|
|
|
133
347
|
}, [instances])
|
|
134
348
|
|
|
135
349
|
return {
|
|
350
|
+
// State
|
|
136
351
|
firebaseUser: instances?.auth.currentUser || null,
|
|
137
352
|
user,
|
|
138
353
|
loading,
|
|
139
354
|
error,
|
|
140
355
|
isAuthenticated: !!instances?.auth.currentUser,
|
|
141
356
|
isEmailVerified: instances?.auth.currentUser?.emailVerified || false,
|
|
357
|
+
|
|
358
|
+
// Email/Password Auth
|
|
142
359
|
signIn,
|
|
143
360
|
signUp,
|
|
361
|
+
|
|
362
|
+
// OAuth Providers
|
|
144
363
|
signInWithGoogle: signInWithGoogleCallback,
|
|
364
|
+
signInWithApple: signInWithAppleCallback,
|
|
365
|
+
|
|
366
|
+
// Provider Management
|
|
367
|
+
linkGoogle: linkGoogleCallback,
|
|
368
|
+
linkApple: linkAppleCallback,
|
|
369
|
+
unlinkProvider: unlinkProviderCallback,
|
|
370
|
+
|
|
371
|
+
// Account Management
|
|
145
372
|
signOut,
|
|
146
373
|
sendPasswordReset,
|
|
147
374
|
resendEmailVerification,
|
|
148
375
|
updateProfile,
|
|
376
|
+
updateEmail: updateEmailCallback,
|
|
377
|
+
updatePassword: updatePasswordCallback,
|
|
378
|
+
deleteAccount: deleteAccountCallback,
|
|
379
|
+
|
|
380
|
+
// Token Management
|
|
381
|
+
getIdToken: getIdTokenCallback,
|
|
382
|
+
refreshToken: refreshTokenCallback,
|
|
383
|
+
|
|
384
|
+
// User Data
|
|
149
385
|
refreshUser,
|
|
386
|
+
|
|
387
|
+
// Config
|
|
388
|
+
googleEnabled: config.isGoogleEnabled(),
|
|
389
|
+
appleEnabled: config.isAppleEnabled(),
|
|
390
|
+
emailPasswordEnabled: config.isEmailPasswordEnabled(),
|
|
150
391
|
}
|
|
151
392
|
}
|