Run saving user function only after google authentication callback - javascript

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);
});
}
});

Related

How to persist the Firebase Auth state between front-end and backend ? (Firebase Auth + React + Express)

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.

remember me checkbox in login that using token in react js

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>

how to save session using firebase?

I have implemented user registration in my application using
firebase.auth().SignInWithEmailAndPassword (email, password)
And I need that when a user comes to the site, his profile will be synchronized as logged in, even the page will be reloaded or closed and opened again
class App extends Component {
constructor(props) {
super(props);
this.state = {
email: " ",
password: " ",
hasAccount: false,
}
}
componentDidMount() {
const db = firebase.database();
console.log(db)
}
handleChange = ({ target: { value, id } }) => {
this.setState({ [id] : value })
}
createAccount = () => {
const { email, password } = this.state;
firebase.auth().signInWithEmailAndPassword( email, password)
.then(Response => {
console.log(Response)
this.setState({ hasAccount: true })
})
.catch(error => console.log(error))
const auth = firebase.app().auth();
auth.setPersistence(firebase.auth.Auth.Persistence.LOCAL)
}
In many environments Firebase already persists the user's authentication credentials, and you don't need to explicitly enabled that with auth.setPersistence(firebase.auth.Auth.Persistence.LOCAL).
But to pick up the restored authentication state when the app is restarted/page reloaded, you need to use an onAuthStateChanged handler, as shown in the fist snippet in the documentation on getting the current user:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
this.setState({ hasAccount: true })
} else {
// No user is signed in.
}
});
You'll typically put this code in the constructor of your component, so that it starts monitoring authentication state right away.
componentDidMount() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in.
this.setState({ hasAccount: true })
}
});

I want to check if a user is signed in with firebase

I have an React app with authentication and a firebase backend.
How do i check if the user is signed in? I tried it with onAuthStateChanged. But it only returns:
Here is my code:
import * as FirestoreService from '../../firebase/firebase';
useEffect(() => {
let user = FirestoreService.isLoggedIn()
console.log( user)
}, []);
Now in the firebase file:
export const isLoggedIn = () => {
return firebase.auth().onAuthStateChanged(function (user) {
if (user) {
console.log("User signed in");
}
else {
console.log("User signed out");
}
});
}
My Check if Loggin was successfull:
function handleLogin() {
FirestoreService.LoggIn(email, password).then(function (result) {
// result.user.tenantId should be ‘TENANT_PROJECT_ID’.
setloggedIn(true)
}).catch(function (error) {
setloggedIn(false);
});
}
Firebase File:
export const LoggIn = (email, password) => {
return firebase.auth().signInWithEmailAndPassword(email, password)
.then(function (result) {
// result.user.tenantId should be ‘TENANT_PROJECT_ID’.
return result;
})
.catch(function (error) {
return error
});
}
onAuthStateChanged() adds an observer for changes to the user's sign-in state but does not return a user object.
In your case you should use the currentUser property. If a user isn't signed in, currentUser is null:
export const isLoggedIn = () => {
return firebase.auth().currentUser;
}

Cannot read property 'navigate' of undefined in a .then() => { }

Why can't I navigate to the screen "App" in the following code. I get the error:
Cannot read property 'navigate' of undefined
async signIn() {
const { username, password } = this.state
await Auth.signIn(username, password)
.then(user => {
this.setState({ user })
this.props.navigation.navigate("App");
Alert.alert('Signed In Successful!')
})
.catch(err => {
console.log('Error when signing in: ', err)
Alert.alert('Error when signing in: ', err)
})
}
}
You are mixing promises and await. You either await for the sign in, or remove the async/await and treat it like a promise. Also, it could be that you haven't binded the function so "this" points to undefined.
Either:
async signIn = () => {
const { username, password } = this.state
let user = await Auth.signIn(username, password);
this.setState({ user })
this.props.navigation.navigate("App");
Alert.alert('Signed In Successful!')
}
}
Or
signIn = () => {
const { username, password } = this.state
Auth.signIn(username, password)
.then(user => {
this.setState({ user })
this.props.navigation.navigate("App");
Alert.alert('Signed In Successful!')
})
.catch(err => {
console.log('Error when signing in: ', err)
Alert.alert('Error when signing in: ', err)
})
}
}

Categories