I want to send a verification email after user sign up. I wrote that code but if i want to get the current user with firebase on react native it always return null. How can i fix it?
Here is the sign up function:
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(navigation.navigate("WaitingRoom"))
.catch((err) => console.log(err));
And also email verification function in WaitingRoom:
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
user
.sendEmailVerification()
.then(() => {
console.log(":)");
})
.catch((err) => {
console.log(err);
});
} else {
console.log(":(");
}
});
I also tried firebase.auth().currentUser but it return null too.
From your comment above: "It happens but there is a delay and sometimes i have to reload page. I think i have to wait for response or something like that."
Yes, as explained in the doc, you need to wait that the Auth object has finished initializing.
This is why you should:
Either use the onAuthStateChanged() observer and put the desired code/ business logic in the if (user) {} block, where you are sure user is not null.
OR, manually check that firebase.auth().currentUser is not null before triggering the code/ business logic.
Concretely, based on what I understand from your question, you could/should call navigation.navigate("WaitingRoom") in the onAuthStateChanged() observer, as follows:
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
user
.sendEmailVerification()
.then(() => {
navigation.navigate("WaitingRoom");
})
.catch((err) => {
console.log(err);
});
} else {
console.log(":(");
}
});
Related
I'm building an app and I'm handling the authentication with Firebase. The signIn create user functions work in the intended way, but for some reason, the signOut function doesn't work. When the button for the SignOut is clicked, the user should be signed out, but instead, he stays signed in
const signOutUser = () =>{
console.log(authentication.currentUser.email)//before calling this is test#test.com
signOut(authentication)
.then((result) => {
console.log(result)
})
.catch((error) => {
console.log(error);
})
console.log("entered sign out functions")
console.log(authentication.currentUser.email)//after above code runs this is till test#test.com
}
Could somebody please advise what I'm doing wrong?
const signOutUser = () => {
console.log(authentication.currentUser.email);
signOut(authentication).then((result) => {
console.log(result);
console.log("entered sign out functions");
console.log(authentication.currentUser.email);
}).catch((e) => { console.log(e) })
}
or
const signOutUser = async() => {
// add try catch if you want
console.log(authentication.currentUser.email);
const result = await signOut(authentication);
console.log("entered sign out functions");
console.log(authentication.currentUser.email);
}
You should await the signOut or write your code in then(). If this is still not working, you probably made a mistake with your signOut(authentication) function.
I'm making an authenticate function. It actually works with hardcoded users, but when I start getting users from Firebase, things start getting asynchronous and issues with timing start happening.
I've got a kind of long-winded Javascript function here that I believe returns a promise.
function authenticate({ username, password }) {
return users.then((querySnapshot) => {
return querySnapshot.forEach(doc => {
let user = doc.data();
if (user.username.toUpperCase() == username.toUpperCase())
return bcrypt.compare(password, user.password).then(function (result) {
console.log(password);
console.log(user.password);
console.log(result);
if (result) {
const token = jwt.sign({ sub: user.id }, config.secret);
const { password, ...userWithoutPassword } = user;
return {
...userWithoutPassword,
token
};
}
})
})
})
}
Console logging seems to confirm that this is a promise. I'll be honest, I copy-pasted a lot of the code inside, so I'm still not entirely sure how it works, but the promise syntax is at least something I'm confident in. After I go through a list of users pulled from Firebase and check that both username and password match, the guts of if (result) should run. result does come back as true, which is correct for what I'm trying to log in with, but my password form rejects me because it continues processing before the authenticate method is finished.
In another Javascript file, I have the method that calls this one.
function authenticate(req, res, next) {
console.log(req.body);
userService.authenticate(req.body)
.then(user => console.log(user))
//.then(user => user ? res.json(user) : res.status(400).json({ message: 'Username or password is incorrect' }))
.catch(err => next(err));
}
I'm learning a lot about asynchronous programming recently but this is defying my expectations a bit. Surely doing .then() on authenticate() should run authenticate(), get a promise, even if it's unresolved, then wait for it to resolve before executing the rest of the statements? The current issue is that the method goes ahead, finds no value for user, then throws a 400, which I think is an issue with asynchronicity. Can anyone explain why the outer authenticate function isn't waiting and how I could make it do that?
There are two possible issues:
Result of forEach
The forEach function returns undefined, see Array.prototype.forEach(). If you need the result of the iteration, you can use Array.prototype.map()
Waiting for the Promise
The following statement sounds like the code does not await the result properly:
my password form rejects me because it continues processing before the authenticate method is finished.
If you have a javascript promise, you can use the await keyword in order to continue the function execution only if the promise is either resolved or rejected. Have a look at the examples here: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/await
In your example, the authenticate function would look like this:
async function authenticate(req, res, next) {
console.log(req.body);
await userService.authenticate(req.body)
.then(...)
.catch(...);
}
Note the async and await keywords. That way it only returns after the userService.authenticate(..) call is fully processed.
Firebase QuerySnapshot has a docs property of type Array<QueryDocumentSnapshot<T>>. You can use that and the Array.find to search for the user. You should also await for bcrypt.compare while you search for user.
function authenticate({ username, password }) {
return users.then(async (querySnapshot) => {
const { usersDocs: docs } = querySnapshot;
const userDoc = usersDocs.find(doc => {
return doc.data().username === username;
});
if (userDoc) {
let user = doc.data();
const pwdCompareResult = await bcrypt.compare(password, user.password);
console.log(password);
console.log(user.password);
console.log(pwdCompareResult );
if (pwdCompareResult ) {
const token = jwt.sign({ sub: user.id }, config.secret);
const { password, ...userWithoutPassword } = user;
return {
...userWithoutPassword,
token
}
}
}
})
}
Please consider using Firebase Authentication instead
Your auth implementation is not reliable and it transfers sensitive data to every users device, like usersname and password hashes. Firebase has a very solid authentication system that you should be using.
I'm using firebase .signInWithEmailAndPassword(email, password).then() for authentication in a react-native Android project.
I have the function called on an onPress button event. The authentication does take place but for some reason, the .then() does not fire unless I tap somewhere else on the screen. It will happily wait 5 mins until I tap somewhere other than the button to fire.
I can see that the auth is taking place. It's just the .then() promise that hangs until focus is shifted away from the button.
I'm using react-native 0.59.5 and firebase 5.1.0 node libraries. I've tried console.logging each step and it's clear the then() is where it fails. Strangely catch() works immediately.
export const loginUser = ({ email, password }) => {
return dispatch => {
dispatch({ type: LOGIN_USER })
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user))
.catch(() => {
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user))
.catch(loginUserFail(dispatch))
})
}
}
const loginUserFail = dispatch => {
dispatch({ type: LOGIN_USER_FAIL })
}
const loginUserSuccess = (dispatch, user) => {
console.log('Firing success')
dispatch({
type: LOGIN_USER_SUCCESS,
payload: user
})
}
In the above example, loginUserFail will run immediately if auth fails but loginUserSuccess will wait indefinitely until I tap somewhere else in the application.
Do you have your remote debugger open in a Chrome browsers?
Close it (debugger), reload app in simulator and it will work as expected.
Just stop the Remote Debugger in your application, hope this will help
try to remove the "then" promisse:
firebase.auth().signInWithEmailAndPassword(email, password)
.catch(error => {
dispatch(loginUserFail(error));
});
After that try to create an action with this comand:
firebase.auth().onAuthStateChanged(user => {
if (user) {
console.log('success sign in');
dispatch(loginUserSuccess(user));
} else {
// No user is signed in.
}
});
I am fairly new to React and the use of Firebase. Right now I have a Firebase.js file where I have stored and exported all of my functions and consts. This is what it looks like:
export const auth = firebase.auth();
export function performLogin(email, password) {
auth.signInWithEmailAndPassword(email, password).then(function(data) {
//Works
}).catch(function(error) {
//Does not work
});
}
So, I have this login.js that I am calling performLogin from, and I was wondering what would be the best way of doing this? How can I create a callback, or at least read any kind of return message? This is how I call performLogin:
clickLogin(e){
e.preventDefault();
performLogin(this.state.email, this.state.password);
}
And this works, as the console output tells me if the login was successful or not. However, I want to be able to use this function in order to retrieve the login status, and then determine wether or not I should prompt an error message or push the user to the admin dashboard. How would I do this?
Is it possible to call performLogin like this?
performLogin(this.state.email,this.state.password,(callback)){
if (callback == true) { //Success } else { //Error }
}
Your performLogin function could take a third parameter, a callback function that lives where your clickLogin() method lives:
export function performLogin(email, password, callback) {
auth.signInWithEmailAndPassword(email, password).then(function(data) {
//Works
callback(data);
}).catch(function(error) {
//Does not work
callback({ error });
});
}
loginResult(result) {
if(result.error) {
//failed
} else {
//logged in
}
}
clickLogin(e){
e.preventDefault();
performLogin(this.state.email, this.state.password, this.loginResult);
}
Or you could return the promise that signInWithEmailAndPassword() returns and handle it in the component.
export function performLogin(email, password) {
return auth.signInWithEmailAndPassword(email, password);
}
clickLogin(e){
e.preventDefault();
performLogin(this.state.email, this.state.password)
.then(result => //logged in)
.catch(error => //failed)
};
Take a look at the function onAuthStateChanged from "firebase/auth" package.
Here is a link to the docs:
https://firebase.google.com/docs/auth/web/manage-users
In a backend API I have a login route which should perform the following sequence of actions:
Given an username and password, try to authenticate the user against an Active Directory. If authentication has failed reply with status 401. If success, continue.
Look for an user with the given username in the database. If not found reply with status 403, otherwise continue.
Find if the user document has some details like email, display name, etc (in case this is not the first time logging in). If yes reply with the user object, otherwise continue.
Get user details from the Active Directory and update the user object in the database. Reply with the updated object.
Code:
router.post('/login', (req, res, next) => {
// capture credentials
const username = req.body.username;
const password = req.body.password;
let user = null;
// authenticate
ad.authenticate(username, password)
.then((success) => {
if (!success) {
res.status(401).send(); // authentication failed
next();
}
return User.findOne({ username }).exec();
})
.then((found) => {
if (!found) {
res.status(403).send(); // unauthorized, no account in DB
next();
}
user = found;
if (user.displayName) {
res.status(201).json(user); // all good, return user details
next();
}
// fetch user details from the AD
return ad.getUserDetails(username, password);
})
.then((details) => {
// update user object with the response details and save
// ...
return user.save();
})
.then((update) => {
res.status(201).json(update); // all good, return user object
next();
})
.catch(err => next(err));
});
Now I had this running with callbacks but it was really nested. So I wanted to give Bluebird promises a try, but I have two problems:
Looks chaotic, any better way to chain the calls and handle responses?
Whenever I call next() to stop the request after replying, the execution continues to the other .then(). Although the client receives the correct response, in the server log I find that the execution have continued. For example, if there is no account in DB for a given user, the client receives the 403 response but in the server log I see an exception failed to read property displayName of null, because there was no user and it should have stopped in the next() after res.status(403).send();.
Best use if/else to make clear what branches will execute and which won't:
ad.authenticate(username, password).then((success) => {
if (!success) {
res.status(401).send(); // authentication failed
} else {
return User.findOne({ username }).exec().then(user => {
if (!user) {
res.status(403).send(); // unauthorized, no account in DB
} else if (user.displayName) {
res.status(201).json(user); // all good, return user details
} else {
// fetch user details from the AD
return ad.getUserDetails(username, password).then(details => {
// update user object with the response details and save
// ...
return user.save();
}).then(update => {
res.status(201).json(update); // all good, return user object
});
}
});
}
}).then(() => next(), err => next(err));
The nesting of then calls is quite necessary for conditional evaluation, you cannot chain them linearly and "break out" in the middle (other than by throwing exceptions, which is really ugly).
If you don't like all those then callbacks, you can use async/await syntax (possibly with a transpiler - or use Bluebird's Promise.coroutine to emulate it with generator syntax). Your whole code then becomes
router.post('/login', async (req, res, next) => {
try {
// authenticate
const success = await ad.authenticate(req.body.username, req.body.password);
if (!success) {
res.status(401).send(); // authentication failed
} else {
const user = await User.findOne({ username }).exec();
if (!user) {
res.status(403).send(); // unauthorized, no account in DB
} else if (user.displayName) {
res.status(201).json(user); // all good, return user details
} else {
// fetch user details from the AD
const details = await ad.getUserDetails(username, password);
// update user object with the response details and save
// ...
const update = await user.save();
res.status(201).json(update); // all good, return user object
}
}
next(); // let's hope this doesn't throw
} catch(err) {
next(err);
}
});
To answer your second point, you have to reject your promise after calling next() (or at least return something, otherwise the line after will be executed). Something like
next();
return Promise.reject()
and change your catch so it works if you do not have an error
.catch(err => {
if (err)
next(err)
});
To your second question first: there is no way to break/stop a promise chain, unless your callback throw err like
doAsync()
.then(()=>{
throw 'sth wrong'
})
.then(()=>{
// code here never runs
})
You can simply try below demos to verify the second callback still runs.
doAsync()
.then(()=>{
res.end('end')
})
.then(()=>{
// code here always runs
})
doAsync()
.then(()=>{
return;
})
.then(()=>{
// code here always runs
})
To your first question: to use the second parameter in then(), which means reject. And each time split the logic to two parts.
var p = new Promise(function(resolve, reject) {
return
ad.auth(username, password).then(()={
// check if 401 needed. If needed, return reject
if (dont needed 401 in your logic)
resolve(username)
else
reject({ msg: 'authentication has failed', status: 401 })
})
});
p
.then( (username)=>{
// this only runs when the previous resolves
return User.findOne({ username }).exec()
}, (data)=>{
// in fact in your case you dont even have to have the reject callback
return data
} )
.then( (found)=>{
return
new Promise(function(resolve, reject) {
if (found && /*your logic to determine it's not 403*/)
resolve(user)
else
reject({ msg: 'unauthorized, no account in DB', status: 403 })
})
} )
.then( (found)=>{
return
new Promise(function(resolve, reject) {
if (found && /*your logic to determine it's not 403*/)
resolve(user)
else
reject({ msg: 'unauthorized, no account in DB', status: 403 })
})
} )
.then( (user)=>{
return
new Promise(function(resolve, reject) {
if (/*your logic to determine it has the full info*/)
resolve(user)
else
return ad.getUserDetails(username, password)
})
} )
.then( (user)=>{
// all is good, do the good logic
}, (data)=>{
// something wrong, so here you can handle all the reject in one place
res.send(data)
} )