@chem-po/firebase-native 0.0.17 → 0.0.18
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 +215 -0
- package/lib/commonjs/adapter/auth.js +288 -78
- package/lib/commonjs/adapter/auth.js.map +1 -1
- package/lib/commonjs/components/FirebaseSignIn.js +1 -1
- package/lib/commonjs/components/FirebaseSignIn.js.map +1 -1
- package/lib/commonjs/index.js +11 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/utils/validation.js +71 -0
- package/lib/commonjs/utils/validation.js.map +1 -0
- package/lib/module/adapter/auth.js +288 -78
- package/lib/module/adapter/auth.js.map +1 -1
- package/lib/module/components/FirebaseSignIn.js +1 -1
- package/lib/module/components/FirebaseSignIn.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/validation.js +62 -0
- package/lib/module/utils/validation.js.map +1 -0
- package/lib/typescript/adapter/auth.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/utils/validation.d.ts +21 -0
- package/lib/typescript/utils/validation.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/adapter/auth.ts +263 -70
- package/src/components/FirebaseSignIn.tsx +1 -1
- package/src/index.ts +1 -0
- package/src/utils/validation.ts +85 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/adapter/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,gBAAgB,EAQjB,MAAM,eAAe,CAAA;AAiBtB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/adapter/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,gBAAgB,EAQjB,MAAM,eAAe,CAAA;AAiBtB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAkY1C,eAAO,MAAM,sBAAsB,GACjC,MAAM,IAAI,EACV,mBAAmB,OAAO,KACzB,WAAW,CAAC,gBAAgB,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAyDzE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,QAAQ,CAAA;AACtB,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,MAAM,CAAA;AACpB,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,QAAQ,CAAA;AACtB,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA;AAC1B,cAAc,MAAM,CAAA;AACpB,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,SAAS,CAAA;AACvB,cAAc,oBAAoB,CAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BaseAuthProvider } from '@chem-po/core';
|
|
2
|
+
interface ValidationResult {
|
|
3
|
+
isValid: boolean;
|
|
4
|
+
errors: string[];
|
|
5
|
+
warnings: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Validates Firebase authentication configuration to help identify common setup issues
|
|
9
|
+
*/
|
|
10
|
+
export declare const validateAuthConfiguration: (providers: BaseAuthProvider[]) => ValidationResult;
|
|
11
|
+
/**
|
|
12
|
+
* Logs validation results with appropriate console methods
|
|
13
|
+
*/
|
|
14
|
+
export declare const logValidationResults: (result: ValidationResult, packageName?: string) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Validates and logs Firebase auth configuration
|
|
17
|
+
* Call this during development to identify setup issues early
|
|
18
|
+
*/
|
|
19
|
+
export declare const validateAndLogAuthConfig: (providers: BaseAuthProvider[]) => ValidationResult;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAsB,MAAM,eAAe,CAAA;AAEpE,UAAU,gBAAgB;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,GAAI,WAAW,gBAAgB,EAAE,KAAG,gBAyCzE,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,gBAAgB,EACxB,oBAAwC,SAezC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAAI,WAAW,gBAAgB,EAAE,qBAIrE,CAAA"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@chem-po/firebase-native",
|
|
3
3
|
"author": "Elan Canfield",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.18",
|
|
6
6
|
"main": "lib/commonjs/index.js",
|
|
7
7
|
"types": "lib/typescript/index.d.ts",
|
|
8
8
|
"source": "src/index.ts",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"react-native-paper": "^5.14.3",
|
|
44
44
|
"react-native-svg": "15.11.2",
|
|
45
45
|
"zustand": "^4.3.3",
|
|
46
|
-
"@chem-po/react": "0.0.
|
|
47
|
-
"@chem-po/core": "0.0.
|
|
48
|
-
"@chem-po/react-native": "0.0.
|
|
46
|
+
"@chem-po/react": "0.0.18",
|
|
47
|
+
"@chem-po/core": "0.0.18",
|
|
48
|
+
"@chem-po/react-native": "0.0.18"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@babel/core": "^7.26.0",
|
package/src/adapter/auth.ts
CHANGED
|
@@ -27,17 +27,46 @@ import {
|
|
|
27
27
|
import { GoogleSignin } from '@react-native-google-signin/google-signin'
|
|
28
28
|
import { Auth, User } from '../types/auth'
|
|
29
29
|
|
|
30
|
+
const isDebug = (
|
|
31
|
+
typeof process !== 'undefined' &&
|
|
32
|
+
process.env?.EXPO_PUBLIC_DEBUG === 'true'
|
|
33
|
+
) || false
|
|
34
|
+
|
|
35
|
+
const debugLog = (message: string, data?: any) => {
|
|
36
|
+
if (isDebug) {
|
|
37
|
+
console.log(`[FirebaseAuth Debug] ${message}`, data || '')
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
30
41
|
const providerInitialized: Record<string, boolean> = {}
|
|
31
42
|
|
|
32
43
|
const getUserWithRole = async <UserData extends BaseUserData>(
|
|
33
44
|
user: FirebaseAuthTypes.User
|
|
34
45
|
): Promise<WithMultiFactorVerified<UserData>> => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
debugLog('Getting user with role', { uid: user.uid, email: user.email })
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const { claims } = await getIdTokenResult(user)
|
|
50
|
+
debugLog('Retrieved ID token claims', { role: claims.role, customClaims: Object.keys(claims) })
|
|
51
|
+
|
|
52
|
+
const userWithRole = {
|
|
53
|
+
...(user as any),
|
|
54
|
+
role: claims.role ?? ('user' as BaseUserRole),
|
|
55
|
+
multiFactorVerified: !!user.multiFactor?.enrolledFactors.length
|
|
56
|
+
} as WithMultiFactorVerified<UserData>
|
|
57
|
+
|
|
58
|
+
debugLog('User with role created', {
|
|
59
|
+
uid: userWithRole.uid,
|
|
60
|
+
role: userWithRole.role,
|
|
61
|
+
multiFactorVerified: userWithRole.multiFactorVerified,
|
|
62
|
+
enrolledFactorsCount: user.multiFactor?.enrolledFactors.length || 0
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
return userWithRole
|
|
66
|
+
} catch (error) {
|
|
67
|
+
debugLog('Error getting user with role', error)
|
|
68
|
+
throw error
|
|
69
|
+
}
|
|
41
70
|
}
|
|
42
71
|
|
|
43
72
|
const toEnrollmentFactor = (hint: FirebaseAuthTypes.MultiFactorInfo): EnrollmentFactor => {
|
|
@@ -89,30 +118,52 @@ const sendMultiFactorCode = async (
|
|
|
89
118
|
factor: EnrollmentFactor,
|
|
90
119
|
resolver: FirebaseAuthTypes.MultiFactorResolver
|
|
91
120
|
): Promise<MultiFactorVerification> => {
|
|
121
|
+
debugLog('Sending multi-factor code', { factorType: factor.type, factorUid: factor.uid })
|
|
122
|
+
|
|
92
123
|
const sessionId = resolver.session
|
|
93
124
|
// const verificationId = await new PhoneAuthProvider(auth).verifyPhoneNumber(phoneSignInFactor, sessionId)
|
|
94
125
|
if (factor.type === 'phone') {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
verificationId,
|
|
101
|
-
|
|
102
|
-
|
|
126
|
+
try {
|
|
127
|
+
const verificationId = await auth.verifyPhoneNumberWithMultiFactorInfo(
|
|
128
|
+
toFirebaseFactor(factor),
|
|
129
|
+
sessionId
|
|
130
|
+
)
|
|
131
|
+
debugLog('Multi-factor code sent successfully', { verificationId: verificationId.substring(0, 10) + '...' })
|
|
132
|
+
return {
|
|
133
|
+
verificationId,
|
|
134
|
+
factor,
|
|
135
|
+
resolver,
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
debugLog('Error sending multi-factor code', error)
|
|
139
|
+
throw error
|
|
103
140
|
}
|
|
104
141
|
}
|
|
105
142
|
throw new Error(`Unsupported factor type: ${factor.type ?? 'Missing factor type'}`)
|
|
106
143
|
}
|
|
107
144
|
|
|
108
145
|
const getEnrolledFactors = async (auth: Auth, error: any): Promise<EnrollmentFactorsResult> => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
146
|
+
debugLog('Getting enrolled factors from error', { errorCode: error?.code })
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const resolver = getMultiFactorResolver(auth, error)
|
|
150
|
+
debugLog('Multi-factor resolver created', { hintsCount: resolver.hints.length })
|
|
151
|
+
|
|
152
|
+
if (resolver.hints.length === 0) {
|
|
153
|
+
debugLog('No enrolled factors found')
|
|
154
|
+
throw new Error('No multi-factor verification methods found, please enroll one on the website')
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const enrollmentFactors = resolver.hints.map(toEnrollmentFactor)
|
|
158
|
+
debugLog('Enrollment factors mapped', { factors: enrollmentFactors.map(f => ({ type: f.type, uid: f.uid })) })
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
enrollmentFactors,
|
|
162
|
+
multiFactorResolver: resolver,
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
debugLog('Error getting enrolled factors', error)
|
|
166
|
+
throw error
|
|
116
167
|
}
|
|
117
168
|
}
|
|
118
169
|
|
|
@@ -120,75 +171,163 @@ const verifyMultiFactor = async (
|
|
|
120
171
|
verification: MultiFactorVerification,
|
|
121
172
|
code: string
|
|
122
173
|
): Promise<LoginResult<User>> => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
174
|
+
debugLog('Verifying multi-factor code', {
|
|
175
|
+
factorType: verification.factor.type,
|
|
176
|
+
codeLength: code.length
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const credential = await PhoneAuthProvider.credential(verification.verificationId, code)
|
|
181
|
+
debugLog('Phone credential created')
|
|
182
|
+
|
|
183
|
+
const assertion = PhoneMultiFactorGenerator.assertion(credential)
|
|
184
|
+
debugLog('Multi-factor assertion created')
|
|
185
|
+
|
|
186
|
+
const resolver = (verification as any).resolver
|
|
187
|
+
if (!resolver) {
|
|
188
|
+
debugLog('Multi-factor resolver not found')
|
|
189
|
+
throw new Error('Internal error signing in with two factor: resolver not found')
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const userCredential = await resolver.resolveSignIn(assertion)
|
|
193
|
+
debugLog('Multi-factor sign-in resolved', { uid: userCredential.user?.uid })
|
|
194
|
+
|
|
195
|
+
const user = userCredential.user
|
|
196
|
+
if (!user) {
|
|
197
|
+
debugLog('No user found after multi-factor resolution')
|
|
198
|
+
throw new Error('No user found')
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const userWithRole = await getUserWithRole(user)
|
|
202
|
+
debugLog('Multi-factor verification completed successfully')
|
|
203
|
+
|
|
204
|
+
return { user: userWithRole }
|
|
205
|
+
} catch (error) {
|
|
206
|
+
debugLog('Error verifying multi-factor', error)
|
|
207
|
+
throw error
|
|
208
|
+
}
|
|
131
209
|
}
|
|
132
210
|
|
|
133
211
|
const handleSignInError = async (error: any): Promise<LoginResult<User>> => {
|
|
212
|
+
debugLog('Handling sign-in error', { code: error?.code, message: error?.message })
|
|
213
|
+
|
|
134
214
|
if (error.code === 'auth/multi-factor-auth-required') {
|
|
215
|
+
debugLog('Multi-factor authentication required')
|
|
135
216
|
return { requestArgs: error }
|
|
136
217
|
}
|
|
218
|
+
|
|
219
|
+
debugLog('Re-throwing sign-in error')
|
|
137
220
|
throw error
|
|
138
221
|
}
|
|
139
222
|
|
|
140
223
|
const initializeProvider = async (provider: BaseAuthProvider) => {
|
|
141
|
-
|
|
224
|
+
debugLog('Initializing provider', { name: provider.name, alreadyInitialized: providerInitialized[provider.name] })
|
|
225
|
+
|
|
226
|
+
if (providerInitialized[provider.name]) {
|
|
227
|
+
debugLog('Provider already initialized, skipping')
|
|
228
|
+
return
|
|
229
|
+
}
|
|
230
|
+
|
|
142
231
|
switch (provider.name) {
|
|
143
232
|
case 'google':
|
|
233
|
+
debugLog('Initializing Google provider', { webClientId: !!(provider as GoogleAuthProvider).webClientId })
|
|
234
|
+
|
|
144
235
|
if (!(provider as GoogleAuthProvider).webClientId) {
|
|
236
|
+
debugLog('Google web client ID missing')
|
|
145
237
|
throw new Error(
|
|
146
|
-
'Google web client ID is required when using Google Auth.
|
|
238
|
+
'Google web client ID is required when using Google Auth. ' +
|
|
239
|
+
'Get your webClientId from Firebase Console > Authentication > Sign-in method > Google > Web SDK configuration. ' +
|
|
240
|
+
'Then provide it in your GoogleAuthProvider: { name: "google", webClientId: "YOUR_CLIENT_ID" }'
|
|
147
241
|
)
|
|
148
242
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true })
|
|
246
|
+
debugLog('Google Play Services available')
|
|
247
|
+
|
|
248
|
+
GoogleSignin.configure({
|
|
249
|
+
webClientId: (provider as any).webClientId,
|
|
250
|
+
})
|
|
251
|
+
debugLog('Google Sign-In configured successfully')
|
|
252
|
+
} catch (error) {
|
|
253
|
+
debugLog('Error initializing Google provider', error)
|
|
254
|
+
if (error instanceof Error && error.message.includes('UNAVAILABLE')) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
'Google Play Services is not available or needs to be updated. ' +
|
|
257
|
+
'Please ensure Google Play Services is installed and up to date on this device.'
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
throw error
|
|
261
|
+
}
|
|
153
262
|
break
|
|
154
263
|
case 'email':
|
|
264
|
+
debugLog('Email provider initialization (no special setup required)')
|
|
155
265
|
// Native SDK doesn't need special initialization
|
|
156
266
|
break
|
|
157
267
|
default:
|
|
268
|
+
debugLog('Unsupported provider', { name: provider.name })
|
|
158
269
|
throw new Error(`Unsupported provider: ${provider.name}`)
|
|
159
270
|
}
|
|
160
271
|
|
|
161
272
|
providerInitialized[provider.name] = true
|
|
273
|
+
debugLog('Provider initialization completed', { name: provider.name })
|
|
162
274
|
}
|
|
163
275
|
|
|
164
276
|
const handleInitialLogin = async (
|
|
165
277
|
userCredential: FirebaseAuthTypes.UserCredential,
|
|
166
278
|
twoFactorRequired: boolean
|
|
167
279
|
): Promise<LoginResult<User>> => {
|
|
280
|
+
debugLog('Handling initial login', {
|
|
281
|
+
uid: userCredential.user?.uid,
|
|
282
|
+
twoFactorRequired,
|
|
283
|
+
enrolledFactorsCount: userCredential.user?.multiFactor?.enrolledFactors.length || 0
|
|
284
|
+
})
|
|
285
|
+
|
|
168
286
|
const user = userCredential.user
|
|
169
|
-
if (!user)
|
|
287
|
+
if (!user) {
|
|
288
|
+
debugLog('No user found in credential')
|
|
289
|
+
throw new Error('No user found')
|
|
290
|
+
}
|
|
291
|
+
|
|
170
292
|
if (twoFactorRequired) {
|
|
293
|
+
debugLog('Two-factor authentication is required but user has no enrolled factors')
|
|
171
294
|
throw new Error(
|
|
172
295
|
'This app requires two factor authentication, please enroll a factor on the website and try again'
|
|
173
296
|
)
|
|
174
297
|
}
|
|
175
|
-
|
|
298
|
+
|
|
299
|
+
const userWithRole = await getUserWithRole(user)
|
|
300
|
+
debugLog('Initial login completed successfully')
|
|
301
|
+
|
|
302
|
+
return { user: userWithRole }
|
|
176
303
|
}
|
|
177
304
|
|
|
178
305
|
const loginWithGoogle = async (
|
|
179
306
|
auth: Auth,
|
|
180
307
|
twoFactorRequired: boolean
|
|
181
308
|
): Promise<LoginResult<User>> => {
|
|
309
|
+
debugLog('Starting Google sign-in', { twoFactorRequired })
|
|
310
|
+
|
|
182
311
|
try {
|
|
312
|
+
debugLog('Calling GoogleSignin.signIn()')
|
|
183
313
|
const signInResult = await GoogleSignin.signIn()
|
|
314
|
+
debugLog('Google sign-in result received', { hasIdToken: !!signInResult.data?.idToken })
|
|
315
|
+
|
|
184
316
|
const idToken = signInResult.data?.idToken
|
|
185
317
|
if (!idToken) {
|
|
318
|
+
debugLog('No ID token found in Google sign-in result')
|
|
186
319
|
throw new Error('No ID token found')
|
|
187
320
|
}
|
|
321
|
+
|
|
322
|
+
debugLog('Creating Google credential')
|
|
188
323
|
const googleCredential = GoogleAuthProvider.credential(idToken)
|
|
324
|
+
|
|
325
|
+
debugLog('Signing in with Google credential')
|
|
189
326
|
const userCredential = await signInWithCredential(auth, googleCredential)
|
|
327
|
+
|
|
190
328
|
return await handleInitialLogin(userCredential, twoFactorRequired)
|
|
191
329
|
} catch (error) {
|
|
330
|
+
debugLog('Error in Google sign-in', error)
|
|
192
331
|
return await handleSignInError(error)
|
|
193
332
|
}
|
|
194
333
|
}
|
|
@@ -199,11 +338,21 @@ const getLoginWithPassword =
|
|
|
199
338
|
provider: BaseAuthProvider,
|
|
200
339
|
{ email, password }: { email: string; password: string }
|
|
201
340
|
): Promise<LoginResult<User>> => {
|
|
341
|
+
debugLog('Starting email/password sign-in', {
|
|
342
|
+
provider: provider.name,
|
|
343
|
+
email,
|
|
344
|
+
twoFactorRequired
|
|
345
|
+
})
|
|
346
|
+
|
|
202
347
|
await initializeProvider(provider)
|
|
203
348
|
try {
|
|
349
|
+
debugLog('Calling signInWithEmailAndPassword')
|
|
204
350
|
const userCredential = await signInWithEmailAndPassword(auth, email, password)
|
|
351
|
+
debugLog('Email/password sign-in successful', { uid: userCredential.user?.uid })
|
|
352
|
+
|
|
205
353
|
return await handleInitialLogin(userCredential, twoFactorRequired)
|
|
206
354
|
} catch (error) {
|
|
355
|
+
debugLog('Error in email/password sign-in', error)
|
|
207
356
|
return await handleSignInError(error)
|
|
208
357
|
}
|
|
209
358
|
}
|
|
@@ -211,11 +360,14 @@ const getLoginWithPassword =
|
|
|
211
360
|
const getLoginWithPopup =
|
|
212
361
|
(auth: Auth, twoFactorRequired: boolean) =>
|
|
213
362
|
async (provider: BaseAuthProvider): Promise<LoginResult<User>> => {
|
|
363
|
+
debugLog('Starting popup sign-in', { provider: provider.name, twoFactorRequired })
|
|
364
|
+
|
|
214
365
|
await initializeProvider(provider)
|
|
215
366
|
switch (provider.name) {
|
|
216
367
|
case 'google':
|
|
217
368
|
return loginWithGoogle(auth, twoFactorRequired)
|
|
218
369
|
default:
|
|
370
|
+
debugLog('Unsupported popup provider', { name: provider.name })
|
|
219
371
|
throw new Error(`Unsupported provider: ${provider.name}`)
|
|
220
372
|
}
|
|
221
373
|
}
|
|
@@ -223,11 +375,21 @@ const getLoginWithPopup =
|
|
|
223
375
|
const getLoginWithToken =
|
|
224
376
|
(auth: Auth, twoFactorRequired: boolean) =>
|
|
225
377
|
async (provider: BaseAuthProvider, token: string): Promise<LoginResult<User>> => {
|
|
378
|
+
debugLog('Starting token sign-in', {
|
|
379
|
+
provider: provider.name,
|
|
380
|
+
tokenLength: token.length,
|
|
381
|
+
twoFactorRequired
|
|
382
|
+
})
|
|
383
|
+
|
|
226
384
|
await initializeProvider(provider)
|
|
227
385
|
try {
|
|
386
|
+
debugLog('Calling signInWithCustomToken')
|
|
228
387
|
const userCredential = await signInWithCustomToken(auth, token)
|
|
388
|
+
debugLog('Token sign-in successful', { uid: userCredential.user?.uid })
|
|
389
|
+
|
|
229
390
|
return await handleInitialLogin(userCredential, twoFactorRequired)
|
|
230
391
|
} catch (error) {
|
|
392
|
+
debugLog('Error in token sign-in', error)
|
|
231
393
|
return await handleSignInError(error)
|
|
232
394
|
}
|
|
233
395
|
}
|
|
@@ -237,45 +399,76 @@ const resetPassword = async (
|
|
|
237
399
|
provider: BaseAuthProvider,
|
|
238
400
|
usernameOrEmail: string
|
|
239
401
|
): Promise<void> => {
|
|
402
|
+
debugLog('Starting password reset', { provider: provider.name, email: usernameOrEmail })
|
|
403
|
+
|
|
240
404
|
await initializeProvider(provider)
|
|
241
|
-
|
|
405
|
+
try {
|
|
406
|
+
await sendPasswordResetEmail(auth, usernameOrEmail)
|
|
407
|
+
debugLog('Password reset email sent successfully')
|
|
408
|
+
} catch (error) {
|
|
409
|
+
debugLog('Error sending password reset email', error)
|
|
410
|
+
throw error
|
|
411
|
+
}
|
|
242
412
|
}
|
|
243
413
|
|
|
244
414
|
export const getFirebaseAuthAdapter = (
|
|
245
415
|
auth: Auth,
|
|
246
416
|
twoFactorRequired: boolean
|
|
247
|
-
): AuthAdapter<BaseAuthProvider, User, { email: string; password: string }> =>
|
|
248
|
-
twoFactorRequired
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
loginWithPopup: getLoginWithPopup(auth, twoFactorRequired),
|
|
256
|
-
loginWithToken: getLoginWithToken(auth, twoFactorRequired),
|
|
257
|
-
resetPassword: (...args) => {
|
|
258
|
-
return resetPassword(auth, ...args)
|
|
259
|
-
},
|
|
260
|
-
verifyMultiFactor,
|
|
261
|
-
sendMultiFactorCode: (...args) => {
|
|
262
|
-
return sendMultiFactorCode(auth, ...args)
|
|
263
|
-
},
|
|
264
|
-
getEnrolledFactors: (...args) => {
|
|
265
|
-
return getEnrolledFactors(auth, ...args)
|
|
266
|
-
},
|
|
267
|
-
logout: async () => {
|
|
268
|
-
await signOut(auth)
|
|
269
|
-
},
|
|
270
|
-
subscribeToUser: (callback) => {
|
|
271
|
-
return onAuthStateChanged(auth, (user) => {
|
|
417
|
+
): AuthAdapter<BaseAuthProvider, User, { email: string; password: string }> => {
|
|
418
|
+
debugLog('Creating Firebase Auth Adapter', { twoFactorRequired })
|
|
419
|
+
|
|
420
|
+
return {
|
|
421
|
+
twoFactorRequired,
|
|
422
|
+
getCurrentUser: async () => {
|
|
423
|
+
debugLog('Getting current user')
|
|
424
|
+
const user = auth.currentUser
|
|
272
425
|
if (!user) {
|
|
273
|
-
|
|
274
|
-
return
|
|
426
|
+
debugLog('No current user found')
|
|
427
|
+
return null
|
|
428
|
+
}
|
|
429
|
+
debugLog('Current user found', { uid: user.uid, email: user.email })
|
|
430
|
+
return getUserWithRole(user)
|
|
431
|
+
},
|
|
432
|
+
loginWithPassword: getLoginWithPassword(auth, twoFactorRequired),
|
|
433
|
+
loginWithPopup: getLoginWithPopup(auth, twoFactorRequired),
|
|
434
|
+
loginWithToken: getLoginWithToken(auth, twoFactorRequired),
|
|
435
|
+
resetPassword: (...args) => {
|
|
436
|
+
return resetPassword(auth, ...args)
|
|
437
|
+
},
|
|
438
|
+
verifyMultiFactor,
|
|
439
|
+
sendMultiFactorCode: (...args) => {
|
|
440
|
+
return sendMultiFactorCode(auth, ...args)
|
|
441
|
+
},
|
|
442
|
+
getEnrolledFactors: (...args) => {
|
|
443
|
+
return getEnrolledFactors(auth, ...args)
|
|
444
|
+
},
|
|
445
|
+
logout: async () => {
|
|
446
|
+
debugLog('Starting logout')
|
|
447
|
+
try {
|
|
448
|
+
await signOut(auth)
|
|
449
|
+
debugLog('Logout completed successfully')
|
|
450
|
+
} catch (error) {
|
|
451
|
+
debugLog('Error during logout', error)
|
|
452
|
+
throw error
|
|
275
453
|
}
|
|
276
|
-
|
|
277
|
-
|
|
454
|
+
},
|
|
455
|
+
subscribeToUser: (callback) => {
|
|
456
|
+
debugLog('Setting up user subscription')
|
|
457
|
+
return onAuthStateChanged(auth, (user) => {
|
|
458
|
+
if (!user) {
|
|
459
|
+
debugLog('User state changed: signed out')
|
|
460
|
+
callback(null)
|
|
461
|
+
return
|
|
462
|
+
}
|
|
463
|
+
debugLog('User state changed: user signed in', { uid: user.uid, email: user.email })
|
|
464
|
+
getUserWithRole(user).then((result) => {
|
|
465
|
+
debugLog('User subscription callback completed', { uid: result.uid, role: result.role })
|
|
466
|
+
callback(result)
|
|
467
|
+
}).catch((error) => {
|
|
468
|
+
debugLog('Error in user subscription callback', error)
|
|
469
|
+
callback(null)
|
|
470
|
+
})
|
|
278
471
|
})
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
472
|
+
},
|
|
473
|
+
}
|
|
474
|
+
}
|
|
@@ -25,7 +25,7 @@ const GoogleLogin = ({ provider }: { provider: GoogleAuthProvider }) => {
|
|
|
25
25
|
const { showError } = useToast()
|
|
26
26
|
|
|
27
27
|
const handleSignIn = useCallback(() => {
|
|
28
|
-
auth.loginWithPopup(provider).catch(error => {
|
|
28
|
+
return auth.loginWithPopup(provider).catch(error => {
|
|
29
29
|
showError(error instanceof Error ? error.message : 'An unknown error occurred')
|
|
30
30
|
})
|
|
31
31
|
}, [auth, provider, showError])
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { BaseAuthProvider, GoogleAuthProvider } from '@chem-po/core'
|
|
2
|
+
|
|
3
|
+
interface ValidationResult {
|
|
4
|
+
isValid: boolean
|
|
5
|
+
errors: string[]
|
|
6
|
+
warnings: string[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Validates Firebase authentication configuration to help identify common setup issues
|
|
11
|
+
*/
|
|
12
|
+
export const validateAuthConfiguration = (providers: BaseAuthProvider[]): ValidationResult => {
|
|
13
|
+
const errors: string[] = []
|
|
14
|
+
const warnings: string[] = []
|
|
15
|
+
|
|
16
|
+
// Check for Google provider configuration
|
|
17
|
+
const googleProvider = providers.find(p => p.name === 'google') as GoogleAuthProvider | undefined
|
|
18
|
+
|
|
19
|
+
if (googleProvider) {
|
|
20
|
+
if (!googleProvider.webClientId) {
|
|
21
|
+
errors.push(
|
|
22
|
+
'Google provider is missing webClientId. ' +
|
|
23
|
+
'Get this from Firebase Console > Authentication > Sign-in method > Google > Web SDK configuration',
|
|
24
|
+
)
|
|
25
|
+
} else if (!googleProvider.webClientId.includes('.apps.googleusercontent.com')) {
|
|
26
|
+
warnings.push(
|
|
27
|
+
'Google webClientId format looks incorrect. Expected format: "xxx.apps.googleusercontent.com"',
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check environment
|
|
33
|
+
if (typeof process === 'undefined') {
|
|
34
|
+
warnings.push('Process environment is not available - some features may not work as expected')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Check for Firebase configuration files (platform-specific warnings)
|
|
38
|
+
const isExpo =
|
|
39
|
+
typeof process !== 'undefined' && process.env?.EXPO_PUBLIC_ENVIRONMENT !== undefined
|
|
40
|
+
if (!isExpo) {
|
|
41
|
+
warnings.push(
|
|
42
|
+
'Ensure Firebase configuration files are present:\n' +
|
|
43
|
+
'- iOS: GoogleService-Info.plist in ios/ directory\n' +
|
|
44
|
+
'- Android: google-services.json in android/app/ directory',
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
isValid: errors.length === 0,
|
|
50
|
+
errors,
|
|
51
|
+
warnings,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Logs validation results with appropriate console methods
|
|
57
|
+
*/
|
|
58
|
+
export const logValidationResults = (
|
|
59
|
+
result: ValidationResult,
|
|
60
|
+
packageName = '@chem-po/firebase-native',
|
|
61
|
+
) => {
|
|
62
|
+
if (result.errors.length > 0) {
|
|
63
|
+
console.error(`[${packageName}] Configuration errors found:`)
|
|
64
|
+
result.errors.forEach(error => console.error(` ❌ ${error}`))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (result.warnings.length > 0) {
|
|
68
|
+
console.warn(`[${packageName}] Configuration warnings:`)
|
|
69
|
+
result.warnings.forEach(warning => console.warn(` ⚠️ ${warning}`))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (result.isValid && result.warnings.length === 0) {
|
|
73
|
+
console.log(`[${packageName}] ✅ Configuration validation passed`)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Validates and logs Firebase auth configuration
|
|
79
|
+
* Call this during development to identify setup issues early
|
|
80
|
+
*/
|
|
81
|
+
export const validateAndLogAuthConfig = (providers: BaseAuthProvider[]) => {
|
|
82
|
+
const result = validateAuthConfiguration(providers)
|
|
83
|
+
logValidationResults(result)
|
|
84
|
+
return result
|
|
85
|
+
}
|