I am trying to use Firebase Auth in backend, but I can't seem to be able to have the same Auth instance in the front-end as well.
The back-end:
'use strict';
import { firebaseAdmin, auth } from '../firebase.js';
import deleteCollection from '../helpers/deleteCollection.js';
import User from '../models/user.js';
import {
createUserWithEmailAndPassword,
updateProfile,
signInWithEmailAndPassword,
signOut,
setPersistence,
browserLocalPersistence,
} from 'firebase/auth';
const firestore = firebaseAdmin.firestore();
const register = async (req, res, next) => {
try {
// name, email, password
const { name, email, password, avatar } = req.body;
console.log('sent from frontend', { name, email, password });
// Check if email or password were sent
if (!email || !password) {
return res.status(422).json({
email: 'Email is required !',
password: 'Password is required !',
});
}
const usersCollection = firestore.collection('users');
// Reference to a QuerySnapshot whith all users that have the requested name
const userSnapshot = await usersCollection.where('name', '==', name).get();
// Check if user already exists:
if (!userSnapshot.empty) {
throw new Error('Username is taken !');
} else {
await setPersistence(auth, browserLocalPersistence);
// Firebase Auth Create User
await createUserWithEmailAndPassword(auth, email, password);
// User is signed in
const user = auth.currentUser;
if (user) {
await updateProfile(user, {
displayName: name,
});
const setUser = {
id: user.uid,
name: user.displayName,
avatar: avatar,
};
await usersCollection.doc(setUser.id).set(setUser);
res.status(201).send(setUser);
} else {
throw new Error('No user');
}
}
} catch (error) {
const errorCode = error.code;
const errorMessage = error.message;
res.status(400).send(errorMessage);
console.log(errorCode, errorMessage);
}
};
const login = async (req, res, next) => {
try {
const { email, password } = req.body;
await setPersistence(auth, browserLocalPersistence);
const userCred = await signInWithEmailAndPassword(auth, email, password);
const usersCollection = firestore.collection('users');
const userSnapshot = await usersCollection
.where('name', '==', userCred.user.displayName)
.get();
if (userSnapshot.empty) {
throw new Error('User does not exist !');
} else {
let user;
userSnapshot.forEach((doc) => (user = { ...doc.data() }));
res.status(200).send(user);
}
} catch (error) {
res.status(404).send(error.message);
console.log(error);
}
};
const logout = async (req, res, next) => {
try {
// const { name, email, password, avatar } = req.body;
await signOut(auth);
res.sendStatus(200);
} catch (error) {
const errorCode = error.code;
const errorMessage = error.message;
res.status(404).send(errorMessage);
console.log(error);
}
};
I call Register, Login and Logout using Redux thunkAPI:
const register = async (userData) => {
const response = await axios.post(API_REGISTER, userData, {
headers: {
// Overwrite Axios's automatically set Content-Type
'Content-Type': 'application/json',
},
});
if (response.data) {
// localStorage.setItem('user', JSON.stringify(response.data));
}
return response.data;
};
const login = async (userData) => {
const response = await axios.post(API_LOGIN, userData, {
headers: {
// Overwrite Axios's automatically set Content-Type
'Content-Type': 'application/json',
},
});
if (response.data) {
// localStorage.setItem('user', JSON.stringify(response.data));
}
return response.data;
};
const logout = async () => {
const response = await axios.get(`${API_LOGOUT}`);
if (response.data) {
localStorage.removeItem('user');
}
return response.data;
};
export const register = createAsyncThunk(
'user/register',
async (user, thunkAPI) => {
try {
return await userService.register(user);
} catch (error) {
return thunkAPI.rejectWithValue(error.response.data);
}
}
);
export const login = createAsyncThunk('user/login', async (user, thunkAPI) => {
try {
return await userService.login(user);
} catch (error) {
return thunkAPI.rejectWithValue(error.response.data);
}
});
export const logout = createAsyncThunk('user/logout', async (_, thunkAPI) => {
try {
return await userService.logout();
} catch (error) {
return thunkAPI.rejectWithValue(error.response.data);
}
});
I am able to Register a user, to login and to logout, but if I hit refresh I get logged out.
I am not able to persist the Firebase Auth state between front-end and backend.
This is the Private Route component
import { useSelector } from 'react-redux';
import { Navigate, useLocation } from 'react-router-dom';
import { auth } from '../../firebase';
import { useAuthState } from 'react-firebase-hooks/auth';
import { useEffect } from 'react';
import { useState } from 'react';
let isAuth;
export default function PrivateRoute({ children }) {
const location = useLocation();
const [user, setUser] = useState();
// const isAuth = useSelector((state) => state.user.user);
// const [user, loading, error] = useAuthState(auth);
// useEffect(() => {
// if (loading) return;
// if (user) {
// isAuth = true;
// console.log(user);
// }
// }, [user, loading]);
useEffect(() => {
auth.onAuthStateChanged(setUser);
}, []);
return user ? (
children
) : (
<Navigate
replace={true}
to='/login'
state={{ from: `${location.pathname}${location.search}` }}
/>
);
}
As you can see from the commented code, I've tried multiple things before posting here but nothing works.
I don't want to move the Authentication logic from back-end to front-end.
I only want to have access to the same Auth state between back-end to front-end.
The approach you're using is not supported by Firebase. You're supposed to authenticate the user only on the frontend and never on the backend. The frontend SDK will persist a token that identifies the user. You then pass that token to the backend on each call and use it to verify the user so that the backend can decide if the operation they are trying to perform is allowed. This scheme is described in the documentation, and I strongly suggest reviewing that:
If your Firebase client app communicates with a custom backend server, you might need to identify the currently signed-in user on that server. To do so securely, after a successful sign-in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity and authenticity of the ID token and retrieve the uid from it. You can use the uid transmitted in this way to securely identify the currently signed-in user on your server.
Again, don't try to sign the user in on your backend using the frontend SDK - that is not supported and it does not scale. Only use the Firebase Admin SDK on the backend to validate the user ID tokens passed from the frontend.
I want to add a Clickable “Remember Me” checkbox in my login page that tells the browser to save a cookie so that if you close out the window for the site without signing out, the next time you go back, you will be signed back in automatically.that can save username and password
export const getUser = () => {
const userStr = sessionStorage.getItem("user");
if (userStr) return JSON.parse(userStr);
else return null;
};
export const getToken = () => {
return sessionStorage.getItem("token") || null;
};
export const setUserSession = (token, user) => {
sessionStorage.setItem("token", token);
sessionStorage.setItem("user", JSON.stringify(user));
};
export const removeUserSession = () => {
sessionStorage.removeItem("token");
sessionStorage.removeItem("user");
};
export const handleSuccessfulLogin = async (token, rememberMe) => {
localStorage.setItem("token", token);
localStorage.setItem("rememberme", rememberMe);
};
export const handleLogout = () => {
localStorage.clear();
};
This is my login that work with api
const handelLogin = () => {
setError(null);
setLoading(true);
axios
.post("https://www.mecallapi.com/api/login", {
username: username,
password: password,
})
.then((response) => {
setLoading(false);
setUserSession(response.data.token, response.data.user);
navigate("/Dashboard");
})
.catch((error) => {
setLoading(false);
if (error.response.status === 401 || error.response.status === 400) {
setError(error.response.data.message);
} else {
setError("somthing went wrong ,please try again");
}
});
};
This is my remember me checkbox
<div className="login-bottom">
<Checkbox {...label} />
</div>
I am using firebase with ReactJS, after creating a user I am sending a confirmation email, after clicking the link, the variable emailVerified remains false no matter what I try.
Here's the code where I create the user and send the email:
const createUserWithEmailAndPasswordHandler = async (event, email, password) => {
event.preventDefault();
try{
const {user} = await auth.createUserWithEmailAndPassword(email, password);
user.sendEmailVerification().then(function() {
user.reload()
}).catch(function(error) {
console.log(error);
});
generateUserDocument(user, {displayName});
}
catch(error){
setError('Error Signing up with email and password');
}
}
And this is the generateDocument code:
export const generateUserDocument = async (user, additionalData) => {
if (!user) return;
const userRef = firestore.doc(`users/${user.uid}`);
const snapshot = await userRef.get();
if (!snapshot.exists) {
const { email,emailVerified, displayName, photoURL } = user;
try {
await userRef.set({
displayName,
email,
emailVerified,
photoURL,
...additionalData
});
} catch (error) {
console.error("Error creating user document", error);
}
}
return getUserDocument(user.uid);
};
Even if I logout and login, refresh the page, it still doesn't change to true.
Thanks for any help!
I have built a google authentication in React + Firebase. Successfully, I added this feature but currently this calls firebase.auth().getRedirectResult() every time when they come to home. I want to call this only after google authentication callback.
class Home extends Component {
constructor(props) {
super(props);
this.state = {
user: null
};
}
async componentWillMount() {
firebase.auth().onAuthStateChanged(user => {
this.setState({ user });
if (user) {
this.props.history.push("/user/settings");
}
});
// Want to call this only after google authentication callback
firebase
.auth()
.getRedirectResult()
.then(result => {
if (result.credential) {
var token = result.credential.accessToken;
console.log("token", token);
}
var user = result.user;
this.props.createUser(user);
})
.catch(function(error) {
console.log("error", error);
});
onLogin = () => {
const provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithRedirect(provider);
};
render() {
return (
// Signin with Google
<button className="btn btnPrimary" onClick={this.onLogin}>
<span>Google Signin</span>
</button>
);
}
}
function mapStateToProps({ user }) {
return { user: user };
}
I set firebase.auth().getRedirectResult() into if (user) { }.
like this:
firebase.auth().onAuthStateChanged(user => {
this.setState({ user });
if (user) {
this.props.history.push("/user/settings");
firebase
.auth()
.getRedirectResult()
.then(result => {
if (result.credential) {
var token = result.credential.accessToken;
console.log("token", token);
}
var user = result.user;
this.props.createUser(user);
})
.catch(function(error) {
console.log("error", error);
});
}
});
I am Creating an expo app in which a user can login using Gmail.
I followed this firebase documentation to implement that functionality but whenever I click on login, it doesn't neither save the data in the database nor return any error.
This is my firebase function:
isUserEqual = (googleUser, firebaseUser)=> {
if (firebaseUser) {
var providerData = firebaseUser.providerData;
for (var i = 0; i < providerData.length; i++) {
if (providerData[i].providerId === firebase.auth.GoogleAuthProvider.PROVIDER_ID &&
providerData[i].uid === googleUser.getBasicProfile().getId()) {
// We don't need to reauth the Firebase connection.
return true;
}
}
}
return false;
}
onSignIn = (googleUser)=> {
console.log('Google Auth Response', googleUser);
// We need to register an Observer on Firebase Auth to make sure auth is initialized.
var unsubscribe = firebase
.auth()
.onAuthStateChanged(function(firebaseUser) {
unsubscribe();
// Check if we are already signed-in Firebase with the correct user.
if (!this.isUserEqual(googleUser, firebaseUser)) {
// Build Firebase credential with the Google ID token.
var credential = firebase.auth.GoogleAuthProvider.credential(
googleUser.idToken,
googleUser.accessToken
);
// Sign in with credential from the Google user.
firebase.auth()
.signInAndRetrieveDataWithCredential(credential)
.then(function(result) {
console.log('User signed in');
})
.catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
});
} else {
console.log('User already signed-in Firebase.');
}
}.bind(this)
);
};
signInWithGoogleAsync = async() => {
try {
const result = await Expo.Google.logInAsync({
behavior: 'web',
androidClientId: '929952027781-5ao9pp7n5n0sj2n70i5tp7klfro88bgp.apps.googleusercontent.com',
iosClientId: '929952027781-7obs66o3kr59kdhp6ll0c9598ue3u8aa.apps.googleusercontent.com',
scopes: ['profile', 'email'],
});
if (result.type === 'success') {
this.onSignIn(result);
return result.accessToken;
} else {
return {cancelled: true};
}
} catch(e) {
return {error: true};
}
}
And this is my login button:
<TouchableOpacity style={styles.AuthOptionGmail} onPress={() => signInWithGoogleAsync()}>
<Ionicons color='#ffffff' style = {styles.BtnIcon} name="logo-google" size={25}/>
<Text style={{fontSize:16,color:'#ffffff', textAlign:'center'}}>Login with Gmail</Text>
</TouchableOpacity>
Can anyone tell me where I messed up please????
with Expo sdk 32 and above following worked for me , just install "expo-google-app-auth"
import * as Google from "expo-google-app-auth";
signInWithGoogleAsync = async () => {
console.log("signInWithGoogleAsync");
try {
//clientId
const { type, accessToken, user, idToken } = await Google.logInAsync({
behavior: "web",
androidClientId:
"your id",
iosClientId:
"your id",
scopes: ["profile", "email"]
});
if (type === "success") {
console.log("accessToken" + accessToken);
console.log("idToken" + idToken);
console.log(user);
return accessToken;
} else {
return { cancelled: true };
}
} catch (e) {
console.log(e);
return { error: true };
}
};