rn-swiftauth-sdk 1.0.1 → 1.0.3
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 +229 -370
- package/dist/components/LoginForm.js +19 -11
- package/dist/components/SignUpForm.js +17 -16
- package/dist/core/AuthProvider.js +56 -110
- package/dist/errors/errorMapper.d.ts +10 -2
- package/dist/errors/errorMapper.js +61 -99
- package/dist/errors/exceptions.d.ts +82 -0
- package/dist/errors/exceptions.js +134 -0
- package/dist/errors/index.d.ts +2 -1
- package/dist/errors/index.js +19 -15
- package/dist/types/auth.types.d.ts +12 -4
- package/dist/types/auth.types.js +0 -1
- package/dist/types/error.types.d.ts +19 -9
- package/dist/types/error.types.js +35 -13
- package/package.json +1 -1
- package/src/components/LoginForm.tsx +21 -12
- package/src/components/SignUpForm.tsx +20 -31
- package/src/core/AuthProvider.tsx +84 -146
- package/src/errors/errorMapper.ts +97 -105
- package/src/errors/exceptions.ts +174 -0
- package/src/errors/index.ts +22 -1
- package/src/types/auth.types.ts +20 -17
- package/src/types/error.types.ts +42 -15
|
@@ -40,13 +40,12 @@ const useAuth_1 = require("../hooks/useAuth");
|
|
|
40
40
|
const PasswordInput_1 = require("./PasswordInput");
|
|
41
41
|
const validation_1 = require("../utils/validation");
|
|
42
42
|
const LoginForm = ({ styles: userStyles }) => {
|
|
43
|
-
const { signInWithEmail, signInWithGoogle, signInWithApple, isLoading,
|
|
44
|
-
error, config // ✅ We need this to check if social buttons are enabled
|
|
45
|
-
} = (0, useAuth_1.useAuth)();
|
|
43
|
+
const { signInWithEmail, signInWithGoogle, signInWithApple, isLoading, error, config } = (0, useAuth_1.useAuth)();
|
|
46
44
|
const [email, setEmail] = (0, react_1.useState)('');
|
|
47
45
|
const [password, setPassword] = (0, react_1.useState)('');
|
|
48
|
-
// ✅ Validation Error State
|
|
49
46
|
const [validationErrors, setValidationErrors] = (0, react_1.useState)({});
|
|
47
|
+
//Check if form is filled to enable button
|
|
48
|
+
const isFormFilled = email.length > 0 && password.length > 0;
|
|
50
49
|
const handleLogin = async () => {
|
|
51
50
|
// 1. Reset previous errors
|
|
52
51
|
setValidationErrors({});
|
|
@@ -58,14 +57,17 @@ const LoginForm = ({ styles: userStyles }) => {
|
|
|
58
57
|
email: emailErr || undefined,
|
|
59
58
|
password: passErr || undefined
|
|
60
59
|
});
|
|
61
|
-
return; //
|
|
60
|
+
return; //Stop if invalid
|
|
62
61
|
}
|
|
63
62
|
// 3. Attempt Login
|
|
64
63
|
try {
|
|
65
|
-
|
|
64
|
+
//UPDATED: Clean Object Syntax
|
|
65
|
+
await signInWithEmail({ email, password });
|
|
66
66
|
}
|
|
67
67
|
catch (e) {
|
|
68
68
|
// Auth errors handled by global state
|
|
69
|
+
// DX: Log it for the developer (Optional but helpful for debugging)
|
|
70
|
+
console.log('Login failed:', e);
|
|
69
71
|
}
|
|
70
72
|
};
|
|
71
73
|
const handleGoogleSignIn = async () => {
|
|
@@ -89,7 +91,7 @@ const LoginForm = ({ styles: userStyles }) => {
|
|
|
89
91
|
react_1.default.createElement(react_native_1.TextInput, { style: [
|
|
90
92
|
defaultStyles.input,
|
|
91
93
|
userStyles?.input,
|
|
92
|
-
validationErrors.email ? { borderColor: 'red' } : {}
|
|
94
|
+
validationErrors.email ? { borderColor: 'red' } : {}
|
|
93
95
|
], placeholder: "Email", value: email, onChangeText: (text) => {
|
|
94
96
|
setEmail(text);
|
|
95
97
|
if (validationErrors.email)
|
|
@@ -104,9 +106,12 @@ const LoginForm = ({ styles: userStyles }) => {
|
|
|
104
106
|
validationErrors.password && (react_1.default.createElement(react_native_1.Text, { style: defaultStyles.validationText }, validationErrors.password)),
|
|
105
107
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [
|
|
106
108
|
defaultStyles.button,
|
|
107
|
-
|
|
109
|
+
// Disable style if loading OR form is incomplete
|
|
110
|
+
(isLoading || !isFormFilled) && defaultStyles.buttonDisabled,
|
|
108
111
|
userStyles?.button
|
|
109
|
-
], onPress: handleLogin,
|
|
112
|
+
], onPress: handleLogin,
|
|
113
|
+
// Disable interaction if loading OR form is incomplete
|
|
114
|
+
disabled: isLoading || !isFormFilled }, isLoading ? (react_1.default.createElement(react_native_1.ActivityIndicator, { color: userStyles?.loadingIndicatorColor || "#fff" })) : (react_1.default.createElement(react_native_1.Text, { style: [defaultStyles.buttonText, userStyles?.buttonText] }, "Sign In"))),
|
|
110
115
|
(config.enableGoogle || config.enableApple) && !isLoading && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
111
116
|
react_1.default.createElement(react_native_1.View, { style: defaultStyles.dividerContainer },
|
|
112
117
|
react_1.default.createElement(react_native_1.View, { style: defaultStyles.divider }),
|
|
@@ -130,7 +135,7 @@ const defaultStyles = react_native_1.StyleSheet.create({
|
|
|
130
135
|
backgroundColor: '#f5f5f5',
|
|
131
136
|
padding: 15,
|
|
132
137
|
borderRadius: 8,
|
|
133
|
-
marginBottom: 8,
|
|
138
|
+
marginBottom: 8,
|
|
134
139
|
borderWidth: 1,
|
|
135
140
|
borderColor: '#e0e0e0',
|
|
136
141
|
fontSize: 16,
|
|
@@ -142,7 +147,10 @@ const defaultStyles = react_native_1.StyleSheet.create({
|
|
|
142
147
|
alignItems: 'center',
|
|
143
148
|
marginTop: 8,
|
|
144
149
|
},
|
|
145
|
-
buttonDisabled: {
|
|
150
|
+
buttonDisabled: {
|
|
151
|
+
backgroundColor: '#a0cfff',
|
|
152
|
+
opacity: 0.7
|
|
153
|
+
},
|
|
146
154
|
buttonText: { color: '#fff', fontWeight: '600', fontSize: 16 },
|
|
147
155
|
errorText: { color: 'red', marginBottom: 12, fontSize: 14, textAlign: 'center' },
|
|
148
156
|
validationText: { color: 'red', fontSize: 12, marginBottom: 10, marginLeft: 4, marginTop: -4 },
|
|
@@ -40,22 +40,20 @@ const useAuth_1 = require("../hooks/useAuth");
|
|
|
40
40
|
const PasswordInput_1 = require("./PasswordInput");
|
|
41
41
|
const validation_1 = require("../utils/validation");
|
|
42
42
|
const SignUpForm = ({ styles: userStyles, showHints = true }) => {
|
|
43
|
-
const { signUpWithEmail, signInWithGoogle, signInWithApple, isLoading,
|
|
44
|
-
error, config // ✅ Use config for conditional rendering
|
|
45
|
-
} = (0, useAuth_1.useAuth)();
|
|
43
|
+
const { signUpWithEmail, signInWithGoogle, signInWithApple, isLoading, error, config } = (0, useAuth_1.useAuth)();
|
|
46
44
|
const [email, setEmail] = (0, react_1.useState)('');
|
|
47
45
|
const [password, setPassword] = (0, react_1.useState)('');
|
|
48
46
|
const [confirmPassword, setConfirmPassword] = (0, react_1.useState)('');
|
|
49
|
-
// ✅ Proper Validation State
|
|
50
47
|
const [validationErrors, setValidationErrors] = (0, react_1.useState)({});
|
|
51
|
-
//
|
|
48
|
+
// 1. Smart Button Logic: Check if all fields have content
|
|
49
|
+
const isFormFilled = email.length > 0 && password.length > 0 && confirmPassword.length > 0;
|
|
50
|
+
// Password Requirements Logic
|
|
52
51
|
const requirements = [
|
|
53
52
|
{ label: "At least 6 characters", met: password.length >= 6 },
|
|
54
53
|
{ label: "Contains a number", met: /\d/.test(password) },
|
|
55
54
|
{ label: "Passwords match", met: password.length > 0 && password === confirmPassword }
|
|
56
55
|
];
|
|
57
56
|
const handleSignUp = async () => {
|
|
58
|
-
// 1. Reset Errors
|
|
59
57
|
setValidationErrors({});
|
|
60
58
|
// 2. Validate Inputs
|
|
61
59
|
const emailErr = (0, validation_1.validateEmail)(email);
|
|
@@ -64,7 +62,6 @@ const SignUpForm = ({ styles: userStyles, showHints = true }) => {
|
|
|
64
62
|
if (password !== confirmPassword) {
|
|
65
63
|
confirmErr = "Passwords do not match.";
|
|
66
64
|
}
|
|
67
|
-
// 3. Check if any errors exist
|
|
68
65
|
if (emailErr || passErr || confirmErr) {
|
|
69
66
|
setValidationErrors({
|
|
70
67
|
email: emailErr || undefined,
|
|
@@ -73,12 +70,12 @@ const SignUpForm = ({ styles: userStyles, showHints = true }) => {
|
|
|
73
70
|
});
|
|
74
71
|
return;
|
|
75
72
|
}
|
|
76
|
-
// 4. Attempt Sign Up
|
|
77
73
|
try {
|
|
78
|
-
|
|
74
|
+
// ✅ UPDATED: New Object Syntax
|
|
75
|
+
await signUpWithEmail({ email, password });
|
|
79
76
|
}
|
|
80
77
|
catch (e) {
|
|
81
|
-
|
|
78
|
+
console.error('Sign Up Failed:', e);
|
|
82
79
|
}
|
|
83
80
|
};
|
|
84
81
|
const handleGoogleSignIn = async () => {
|
|
@@ -130,9 +127,12 @@ const SignUpForm = ({ styles: userStyles, showHints = true }) => {
|
|
|
130
127
|
] }, req.label)))))),
|
|
131
128
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [
|
|
132
129
|
defaultStyles.button,
|
|
133
|
-
|
|
130
|
+
// Disable style if loading OR form incomplete
|
|
131
|
+
(isLoading || !isFormFilled) && defaultStyles.buttonDisabled,
|
|
134
132
|
userStyles?.button
|
|
135
|
-
], onPress: handleSignUp,
|
|
133
|
+
], onPress: handleSignUp,
|
|
134
|
+
// Disable interaction if loading OR form incomplete
|
|
135
|
+
disabled: isLoading || !isFormFilled }, isLoading ? (react_1.default.createElement(react_native_1.ActivityIndicator, { color: userStyles?.loadingIndicatorColor || "#fff" })) : (react_1.default.createElement(react_native_1.Text, { style: [defaultStyles.buttonText, userStyles?.buttonText] }, "Create Account"))),
|
|
136
136
|
(config.enableGoogle || config.enableApple) && !isLoading && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
137
137
|
react_1.default.createElement(react_native_1.View, { style: defaultStyles.dividerContainer },
|
|
138
138
|
react_1.default.createElement(react_native_1.View, { style: defaultStyles.divider }),
|
|
@@ -150,7 +150,7 @@ const defaultStyles = react_native_1.StyleSheet.create({
|
|
|
150
150
|
backgroundColor: '#f5f5f5',
|
|
151
151
|
padding: 15,
|
|
152
152
|
borderRadius: 8,
|
|
153
|
-
marginBottom: 8,
|
|
153
|
+
marginBottom: 8,
|
|
154
154
|
borderWidth: 1,
|
|
155
155
|
borderColor: '#e0e0e0',
|
|
156
156
|
fontSize: 16,
|
|
@@ -162,11 +162,13 @@ const defaultStyles = react_native_1.StyleSheet.create({
|
|
|
162
162
|
alignItems: 'center',
|
|
163
163
|
marginTop: 8,
|
|
164
164
|
},
|
|
165
|
-
buttonDisabled: {
|
|
165
|
+
buttonDisabled: {
|
|
166
|
+
backgroundColor: '#9ce4ae',
|
|
167
|
+
opacity: 0.7
|
|
168
|
+
},
|
|
166
169
|
buttonText: { color: '#fff', fontWeight: '600', fontSize: 16 },
|
|
167
170
|
globalError: { color: 'red', marginBottom: 12, fontSize: 14, textAlign: 'center' },
|
|
168
171
|
validationText: { color: 'red', fontSize: 12, marginBottom: 10, marginLeft: 4, marginTop: -4 },
|
|
169
|
-
// OAuth Styles
|
|
170
172
|
dividerContainer: {
|
|
171
173
|
flexDirection: 'row',
|
|
172
174
|
alignItems: 'center',
|
|
@@ -190,7 +192,6 @@ const defaultStyles = react_native_1.StyleSheet.create({
|
|
|
190
192
|
googleButtonText: { color: '#000', fontSize: 16, fontWeight: '500' },
|
|
191
193
|
appleButton: { backgroundColor: '#000' },
|
|
192
194
|
appleButtonText: { color: '#fff', fontSize: 16, fontWeight: '600' },
|
|
193
|
-
// Password Hint Styles
|
|
194
195
|
hintContainer: { marginBottom: 15, paddingLeft: 5 },
|
|
195
196
|
hintRow: { flexDirection: 'row', alignItems: 'center', marginBottom: 4 },
|
|
196
197
|
hintText: { color: '#666', fontSize: 12 },
|
|
@@ -40,33 +40,26 @@ exports.AuthProvider = void 0;
|
|
|
40
40
|
const react_1 = __importStar(require("react"));
|
|
41
41
|
const react_native_1 = require("react-native");
|
|
42
42
|
const app_1 = require("firebase/app");
|
|
43
|
-
|
|
44
|
-
const auth_1 = require("firebase/auth");
|
|
45
|
-
// The Hack: Import for getReactNativePersistence
|
|
46
|
-
const firebaseAuth = __importStar(require("firebase/auth"));
|
|
47
|
-
// @ts-ignore
|
|
48
|
-
const getReactNativePersistence = firebaseAuth.getReactNativePersistence;
|
|
43
|
+
const FirebaseAuth = __importStar(require("firebase/auth"));
|
|
49
44
|
const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
|
|
50
|
-
// PROPER GOOGLE SIGN-IN
|
|
51
45
|
const google_signin_1 = require("@react-native-google-signin/google-signin");
|
|
52
|
-
// Apple Sign-In (Expo)
|
|
53
46
|
const AppleAuthentication = __importStar(require("expo-apple-authentication"));
|
|
54
47
|
const Crypto = __importStar(require("expo-crypto"));
|
|
55
48
|
const errors_1 = require("../errors");
|
|
56
49
|
const AuthContext_1 = require("./AuthContext");
|
|
57
50
|
const types_1 = require("../types");
|
|
51
|
+
const getReactNativePersistence = FirebaseAuth.getReactNativePersistence;
|
|
58
52
|
const AuthProvider = ({ config, children }) => {
|
|
59
53
|
const [user, setUser] = (0, react_1.useState)(null);
|
|
60
54
|
const [status, setStatus] = (0, react_1.useState)(types_1.AuthStatus.LOADING);
|
|
61
55
|
const [error, setError] = (0, react_1.useState)(null);
|
|
62
56
|
const [firebaseAuthInstance, setFirebaseAuthInstance] = (0, react_1.useState)(null);
|
|
63
|
-
// ✅ NEW: Explicit loading state for initial app load vs action loading
|
|
64
57
|
const [isDataLoading, setIsDataLoading] = (0, react_1.useState)(true);
|
|
58
|
+
// --- INITIALIZATION ---
|
|
65
59
|
(0, react_1.useEffect)(() => {
|
|
66
60
|
let app;
|
|
67
61
|
let auth;
|
|
68
62
|
if (!(0, app_1.getApps)().length) {
|
|
69
|
-
// 1. Initialize App
|
|
70
63
|
app = (0, app_1.initializeApp)({
|
|
71
64
|
apiKey: config.apiKey,
|
|
72
65
|
authDomain: config.authDomain,
|
|
@@ -75,39 +68,35 @@ const AuthProvider = ({ config, children }) => {
|
|
|
75
68
|
messagingSenderId: config.messagingSenderId,
|
|
76
69
|
appId: config.appId,
|
|
77
70
|
});
|
|
78
|
-
// 2. Select Persistence Strategy
|
|
79
71
|
const selectedPersistence = config.persistence === 'memory'
|
|
80
|
-
?
|
|
72
|
+
? FirebaseAuth.inMemoryPersistence
|
|
81
73
|
: getReactNativePersistence(async_storage_1.default);
|
|
82
|
-
|
|
83
|
-
auth = (0, auth_1.initializeAuth)(app, {
|
|
74
|
+
auth = FirebaseAuth.initializeAuth(app, {
|
|
84
75
|
persistence: selectedPersistence
|
|
85
76
|
});
|
|
86
77
|
}
|
|
87
78
|
else {
|
|
88
79
|
app = (0, app_1.getApp)();
|
|
89
|
-
auth =
|
|
80
|
+
auth = FirebaseAuth.getAuth(app);
|
|
90
81
|
}
|
|
91
82
|
setFirebaseAuthInstance(auth);
|
|
92
|
-
// 4. Configure Google Sign-In if enabled
|
|
93
83
|
if (config.enableGoogle && config.googleWebClientId) {
|
|
94
84
|
try {
|
|
95
85
|
google_signin_1.GoogleSignin.configure({
|
|
96
86
|
webClientId: config.googleWebClientId,
|
|
97
87
|
offlineAccess: true,
|
|
98
|
-
iosClientId: config.googleIOSClientId,
|
|
88
|
+
iosClientId: config.googleIOSClientId,
|
|
99
89
|
});
|
|
100
|
-
console.log('
|
|
90
|
+
console.log('Google Sign-In configured successfully');
|
|
101
91
|
}
|
|
102
92
|
catch (err) {
|
|
103
|
-
console.error('
|
|
93
|
+
console.error('Google Sign-In configuration failed:', err);
|
|
104
94
|
}
|
|
105
95
|
}
|
|
106
|
-
const unsubscribe =
|
|
96
|
+
const unsubscribe = FirebaseAuth.onAuthStateChanged(auth, async (fbUser) => {
|
|
107
97
|
try {
|
|
108
98
|
if (fbUser) {
|
|
109
99
|
try {
|
|
110
|
-
// Force token refresh to ensure validity on load
|
|
111
100
|
const token = await fbUser.getIdToken(true);
|
|
112
101
|
setUser({
|
|
113
102
|
uid: fbUser.uid,
|
|
@@ -121,11 +110,14 @@ const AuthProvider = ({ config, children }) => {
|
|
|
121
110
|
}
|
|
122
111
|
catch (tokenError) {
|
|
123
112
|
console.error('Token retrieval error:', tokenError);
|
|
124
|
-
if (tokenError.code ===
|
|
113
|
+
if (tokenError.code === types_1.ProviderErrorCodes.USER_TOKEN_EXPIRED ||
|
|
114
|
+
tokenError.code === types_1.ProviderErrorCodes.NULL_USER) {
|
|
125
115
|
setStatus(types_1.AuthStatus.TOKEN_EXPIRED);
|
|
116
|
+
setError((0, errors_1.mapFirebaseError)(tokenError));
|
|
126
117
|
}
|
|
127
118
|
else {
|
|
128
119
|
setStatus(types_1.AuthStatus.UNAUTHENTICATED);
|
|
120
|
+
setError((0, errors_1.mapFirebaseError)(tokenError));
|
|
129
121
|
}
|
|
130
122
|
setUser(null);
|
|
131
123
|
}
|
|
@@ -138,60 +130,50 @@ const AuthProvider = ({ config, children }) => {
|
|
|
138
130
|
catch (err) {
|
|
139
131
|
console.error("Auth State Error:", err);
|
|
140
132
|
setStatus(types_1.AuthStatus.UNAUTHENTICATED);
|
|
133
|
+
setError((0, errors_1.mapFirebaseError)(err));
|
|
141
134
|
}
|
|
142
135
|
finally {
|
|
143
|
-
// ✅ Stop initial loading spinner once Firebase has checked storage
|
|
144
136
|
setIsDataLoading(false);
|
|
145
137
|
}
|
|
146
|
-
}, (err) => {
|
|
147
|
-
console.error("Auth State Error:", err);
|
|
148
|
-
setStatus(types_1.AuthStatus.UNAUTHENTICATED);
|
|
149
|
-
setIsDataLoading(false);
|
|
150
138
|
});
|
|
151
139
|
return () => unsubscribe();
|
|
152
140
|
}, [config]);
|
|
153
|
-
//
|
|
154
|
-
const signInWithEmail = async (email,
|
|
141
|
+
// --- ACTIONS ---
|
|
142
|
+
const signInWithEmail = async ({ email, password }) => {
|
|
155
143
|
if (!firebaseAuthInstance)
|
|
156
144
|
return;
|
|
157
145
|
try {
|
|
158
146
|
setError(null);
|
|
159
147
|
setStatus(types_1.AuthStatus.LOADING);
|
|
160
|
-
await
|
|
148
|
+
await FirebaseAuth.signInWithEmailAndPassword(firebaseAuthInstance, email, password);
|
|
161
149
|
}
|
|
162
150
|
catch (err) {
|
|
163
|
-
const
|
|
164
|
-
setError(
|
|
151
|
+
const mappedException = (0, errors_1.mapFirebaseError)(err);
|
|
152
|
+
setError(mappedException);
|
|
165
153
|
setStatus(types_1.AuthStatus.UNAUTHENTICATED);
|
|
166
|
-
throw
|
|
154
|
+
throw mappedException;
|
|
167
155
|
}
|
|
168
156
|
};
|
|
169
|
-
|
|
170
|
-
const signUpWithEmail = async (email, pass) => {
|
|
157
|
+
const signUpWithEmail = async ({ email, password }) => {
|
|
171
158
|
if (!firebaseAuthInstance)
|
|
172
159
|
return;
|
|
173
160
|
try {
|
|
174
161
|
setError(null);
|
|
175
162
|
setStatus(types_1.AuthStatus.LOADING);
|
|
176
|
-
await
|
|
163
|
+
await FirebaseAuth.createUserWithEmailAndPassword(firebaseAuthInstance, email, password);
|
|
177
164
|
}
|
|
178
165
|
catch (err) {
|
|
179
|
-
const
|
|
180
|
-
setError(
|
|
166
|
+
const mappedException = (0, errors_1.mapFirebaseError)(err);
|
|
167
|
+
setError(mappedException);
|
|
181
168
|
setStatus(types_1.AuthStatus.UNAUTHENTICATED);
|
|
182
|
-
throw
|
|
169
|
+
throw mappedException;
|
|
183
170
|
}
|
|
184
171
|
};
|
|
185
|
-
// PROPER GOOGLE SIGN-IN using @react-native-google-signin/google-signin
|
|
186
172
|
const signInWithGoogle = async () => {
|
|
187
|
-
if (!firebaseAuthInstance)
|
|
173
|
+
if (!firebaseAuthInstance)
|
|
188
174
|
throw new Error('Firebase not initialized');
|
|
189
|
-
}
|
|
190
175
|
if (!config.enableGoogle || !config.googleWebClientId) {
|
|
191
|
-
const configError =
|
|
192
|
-
code: types_1.AuthErrorCode.CONFIG_ERROR,
|
|
193
|
-
message: 'Google Sign-In is not enabled or configured. Please add googleWebClientId to your AuthConfig.',
|
|
194
|
-
};
|
|
176
|
+
const configError = new errors_1.ConfigurationException('Google Auth not configured. Missing googleWebClientId.');
|
|
195
177
|
setError(configError);
|
|
196
178
|
throw configError;
|
|
197
179
|
}
|
|
@@ -201,67 +183,37 @@ const AuthProvider = ({ config, children }) => {
|
|
|
201
183
|
await google_signin_1.GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true });
|
|
202
184
|
const userInfo = await google_signin_1.GoogleSignin.signIn();
|
|
203
185
|
const idToken = userInfo.data?.idToken;
|
|
204
|
-
if (!idToken)
|
|
186
|
+
if (!idToken)
|
|
205
187
|
throw new Error('No ID token received from Google Sign-In');
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
console.log('✅ Google Sign-In successful');
|
|
188
|
+
const credential = FirebaseAuth.GoogleAuthProvider.credential(idToken);
|
|
189
|
+
await FirebaseAuth.signInWithCredential(firebaseAuthInstance, credential);
|
|
190
|
+
console.log('Google Sign-In successful');
|
|
210
191
|
}
|
|
211
192
|
catch (err) {
|
|
212
|
-
console.error('
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
code: types_1.AuthErrorCode.GOOGLE_SIGN_IN_CANCELLED,
|
|
217
|
-
message: 'Google Sign-In was cancelled',
|
|
218
|
-
originalError: err
|
|
219
|
-
};
|
|
220
|
-
// Reset status if cancelled, don't leave it loading
|
|
193
|
+
console.error('Google Sign-In Error:', err);
|
|
194
|
+
if (err.code === types_1.ProviderErrorCodes.GOOGLE_CANCELLED) {
|
|
195
|
+
const cancelError = new errors_1.GoogleSignInCancelledException(err);
|
|
196
|
+
setError(cancelError);
|
|
221
197
|
setStatus(types_1.AuthStatus.UNAUTHENTICATED);
|
|
222
198
|
return;
|
|
223
199
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
code: types_1.AuthErrorCode.GOOGLE_SIGN_IN_IN_PROGRESS,
|
|
227
|
-
message: 'Google Sign-In is already in progress',
|
|
228
|
-
originalError: err
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
else if (err.code === 'PLAY_SERVICES_NOT_AVAILABLE') {
|
|
232
|
-
mappedError = {
|
|
233
|
-
code: types_1.AuthErrorCode.GOOGLE_PLAY_SERVICES_NOT_AVAILABLE,
|
|
234
|
-
message: 'Google Play Services are not available. Please update Google Play Services.',
|
|
235
|
-
originalError: err
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
mappedError = (0, errors_1.mapFirebaseError)(err);
|
|
240
|
-
}
|
|
241
|
-
setError(mappedError);
|
|
200
|
+
const mappedException = (0, errors_1.mapFirebaseError)(err);
|
|
201
|
+
setError(mappedException);
|
|
242
202
|
setStatus(types_1.AuthStatus.UNAUTHENTICATED);
|
|
243
|
-
throw
|
|
203
|
+
throw mappedException;
|
|
244
204
|
}
|
|
245
205
|
};
|
|
246
|
-
// Apple Sign-In using expo-apple-authentication
|
|
247
206
|
const signInWithApple = async () => {
|
|
248
|
-
if (!firebaseAuthInstance)
|
|
207
|
+
if (!firebaseAuthInstance)
|
|
249
208
|
throw new Error('Firebase not initialized');
|
|
250
|
-
}
|
|
251
209
|
if (react_native_1.Platform.OS !== 'ios') {
|
|
252
|
-
const platformError =
|
|
253
|
-
code: types_1.AuthErrorCode.APPLE_SIGN_IN_NOT_SUPPORTED,
|
|
254
|
-
message: 'Apple Sign-In is only available on iOS devices',
|
|
255
|
-
};
|
|
210
|
+
const platformError = new errors_1.AppleSignInNotSupportedException();
|
|
256
211
|
setError(platformError);
|
|
257
212
|
throw platformError;
|
|
258
213
|
}
|
|
259
214
|
const isAvailable = await AppleAuthentication.isAvailableAsync();
|
|
260
215
|
if (!isAvailable) {
|
|
261
|
-
const availabilityError =
|
|
262
|
-
code: types_1.AuthErrorCode.APPLE_SIGN_IN_NOT_SUPPORTED,
|
|
263
|
-
message: 'Apple Sign-In is not available on this device (requires iOS 13+)',
|
|
264
|
-
};
|
|
216
|
+
const availabilityError = new errors_1.AppleSignInNotSupportedException();
|
|
265
217
|
setError(availabilityError);
|
|
266
218
|
throw availabilityError;
|
|
267
219
|
}
|
|
@@ -278,36 +230,30 @@ const AuthProvider = ({ config, children }) => {
|
|
|
278
230
|
nonce: hashedNonce,
|
|
279
231
|
});
|
|
280
232
|
const { identityToken } = appleCredential;
|
|
281
|
-
if (!identityToken)
|
|
233
|
+
if (!identityToken)
|
|
282
234
|
throw new Error('No identity token received from Apple');
|
|
283
|
-
|
|
284
|
-
const provider = new auth_1.OAuthProvider('apple.com');
|
|
235
|
+
const provider = new FirebaseAuth.OAuthProvider('apple.com');
|
|
285
236
|
const credential = provider.credential({
|
|
286
237
|
idToken: identityToken,
|
|
287
238
|
rawNonce: nonce,
|
|
288
239
|
});
|
|
289
|
-
await
|
|
290
|
-
console.log('
|
|
240
|
+
await FirebaseAuth.signInWithCredential(firebaseAuthInstance, credential);
|
|
241
|
+
console.log('Apple Sign-In successful');
|
|
291
242
|
}
|
|
292
243
|
catch (err) {
|
|
293
|
-
console.error('
|
|
294
|
-
if (err.code ===
|
|
295
|
-
const cancelError =
|
|
296
|
-
code: types_1.AuthErrorCode.APPLE_SIGN_IN_CANCELLED,
|
|
297
|
-
message: 'Apple Sign-In was cancelled',
|
|
298
|
-
originalError: err
|
|
299
|
-
};
|
|
244
|
+
console.error('Apple Sign-In Error:', err);
|
|
245
|
+
if (err.code === types_1.ProviderErrorCodes.APPLE_CANCELLED) {
|
|
246
|
+
const cancelError = new errors_1.AppleSignInCancelledException(err);
|
|
300
247
|
setError(cancelError);
|
|
301
248
|
setStatus(types_1.AuthStatus.UNAUTHENTICATED);
|
|
302
249
|
return;
|
|
303
250
|
}
|
|
304
|
-
const
|
|
305
|
-
setError(
|
|
251
|
+
const mappedException = (0, errors_1.mapFirebaseError)(err);
|
|
252
|
+
setError(mappedException);
|
|
306
253
|
setStatus(types_1.AuthStatus.UNAUTHENTICATED);
|
|
307
|
-
throw
|
|
254
|
+
throw mappedException;
|
|
308
255
|
}
|
|
309
256
|
};
|
|
310
|
-
// Sign Out
|
|
311
257
|
const signOut = async () => {
|
|
312
258
|
try {
|
|
313
259
|
if (firebaseAuthInstance) {
|
|
@@ -321,10 +267,12 @@ const AuthProvider = ({ config, children }) => {
|
|
|
321
267
|
console.log('Google sign-out skipped or failed:', googleSignOutError);
|
|
322
268
|
}
|
|
323
269
|
}
|
|
324
|
-
console.log('
|
|
270
|
+
console.log('Sign out successful');
|
|
325
271
|
}
|
|
326
272
|
catch (err) {
|
|
327
|
-
console.error('
|
|
273
|
+
console.error('Sign out error:', err);
|
|
274
|
+
const signOutError = (0, errors_1.mapFirebaseError)(err);
|
|
275
|
+
setError(signOutError);
|
|
328
276
|
setUser(null);
|
|
329
277
|
setStatus(types_1.AuthStatus.UNAUTHENTICATED);
|
|
330
278
|
}
|
|
@@ -333,10 +281,8 @@ const AuthProvider = ({ config, children }) => {
|
|
|
333
281
|
const value = (0, react_1.useMemo)(() => ({
|
|
334
282
|
user,
|
|
335
283
|
status,
|
|
336
|
-
// ✅ NEW: Combine internal loading with AuthStatus
|
|
337
284
|
isLoading: isDataLoading || status === types_1.AuthStatus.LOADING,
|
|
338
285
|
error,
|
|
339
|
-
// ✅ NEW: Expose config for UI to read
|
|
340
286
|
config,
|
|
341
287
|
signInWithEmail,
|
|
342
288
|
signUpWithEmail,
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const mapFirebaseError: (error: any) =>
|
|
1
|
+
import { AuthException } from './exceptions';
|
|
2
|
+
export declare const mapFirebaseError: (error: any) => AuthException;
|
|
3
|
+
/**
|
|
4
|
+
* Helper function to check if an error is a specific exception type
|
|
5
|
+
*/
|
|
6
|
+
export declare const isAuthException: (error: any, exceptionType: new (...args: any[]) => AuthException) => boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Helper to extract user-friendly message from any error
|
|
9
|
+
*/
|
|
10
|
+
export declare const getErrorMessage: (error: any) => string;
|