I'm trying to make a simple web application and trying to implement google authentication(login) using passport and store the user in firebase authentication.
So, in the passport middleware what i do is check if the user is authenticated.
if yes then pass the user to the passport.serializeUser(user) else create the user in firebase authentication. and then pass the user to passport.serializeUser(user)
here is a pseudo code -
if (user.authenticated){
done(null, user)
} else {
let promise = {'uid': user.uid, 'name': user.displayName, 'picture':user.photos[0].value};
firebase.auth().createUser({
uid: user.uid,
displayName: user.name,
photoURL: user.picture
});
console.log('i have submitted the user')
done(null, promise)
}
everything is going cool the passport.serializeUser() gets and the user calls done(null, user.uid).
the problem hits when the passport.deserializeUser().
i dont do any fancy there but just get the user from the firebase by using firebase.auth().getUser(id) where it gives me an error.
here is my code for passport.deserializeUser().
passport.deserializeUser((id, done) => { // when we get a request
console.log(`deser id ${id}`); // it shows the id we passed in serialize
firebase.auth().getUser(id).then( // gives an error T_T ?
(user) => {
console.log(`deser data ${user}`); // IT SHOULD GO HERE
done(null, user)
}).catch((error) => { // it goes here and throws an error at me
console.log(`here is the error on deser ${error}`);
// here is the error on deser Error: There is no user record corresponding to the provided identifier.
});
});
the magic happens after some time when the passport.deserealizeUser() gets called again at now for some reason it doesn't throw a error at me.
here is the console log..
i have submitted the user
ser // i do this call in passport.serializeUser()
deser id "someid"
here is the error on deser Error: There is no user record corresponding to the provided identifier.
deser id "someid"
deser data [object Object]
my question is that why does the firebase being delayed?
is it because of the reason that first time the firebase didn't loaded and the second time it loaded and was successful to find the user?
but what could be the reason for that?
any guesses?
Alright guys So, after a big nap I've found out what I did wrong, i wasn't handling the promise from the..
firebase.auth().createUser({
uid: user.uid,
displayName: user.name,
photoURL: user.picture
});
so what i did is like ..
firebase.auth().createUser({
uid: user.uid,
displayName: user.name,
photoURL: user.picture
}).then((user) => {
done(null, user);
});
certainly it requires some time to process the user in the auth() ..
that is why.
ALWAYS USE PROMISES.
Related
So whilst learning JS and specifically the MEAN 2 stack i'm trying to build out a basic multi tenanted app. Im building out sign up routes in express and the flow i'm trying to achieve would be:
Sign up with company name, email and password. The info would go to save a new tenant, then return the _id of the new tenant and then use this new id, the email and the password to save a new user.
The closest is:
router.post('/', function (req, res, next) {
var tenant = new Tenant({
name: req.body.name
});
var newTenant;
tenant.save(function (err, tenant) {
if (err) {
return res.status(500).json({
title: 'An error has occured',
error: err
});
}
res.status(201).json({
message: 'Tenant created',
obj: tenant
});
return(tenant._id);
newTenant = tenant;
});
Tenant.findById(newTenant._id, function(err, tenant) {
if (err) {
return res.status(500).json({
title:'An error occured',
error: err
});
}
var user = new User({
email: req.body.email,
password: bcrypt.hashSync(req.body.password, 10),
active: req.body.active,
tenant: tenant
});
user.save(function (err, user) {
if (err) {
return res.status(500).json({
title: 'An error has occured',
error: err
});
}
res.status(201).json({
message: 'User created',
obj: user
});
});
});
});
module.exports = router;
I'm getting an error: cant set headers after they've been sent.
I think I know where i'm wrong, with returning the tenant info. I think Async is the answer but cant figure out how to implement it. Sorry if this is a stupid question or i'm missing something obvious, I'm super new to this and callbacks are doing my head in.
This is happening because res.status() sets headers as soon as it has fired. You try to do this multiple times both when checking for errors, and then you try to set the status code again in Tenant.findById().
You end up with a flow like:
if (err) set headers
set headers (again)
findById()
if (err) set headers
set headers (again)
You have to be careful when writing out your response that you only do it at the last point in your logic flow. You can also could set up a global err handler and throw new Error() and stop the flow of logic and handle the output immediately. If you don't, your code will continue to execute even though it encountered an error.
Another tip: callbacks don't work well with returns. And although you can arrange them to work, or implement a promise architecture instead, the simplest fix (and the easiest to learn) is to make your functions all asynchronous.
Try instead something like:
tenant.save(function (err, tenant, callback) {
// add a callback param to your fn ^
if (err) {
throw({
code: 500,
title: 'An error has occured',
error: err
});
} else {
// ^ add an else statement so you don't set the headers twice
// (because res.status() sets headers)
res.status(201).json({
message: 'Tenant created',
obj: tenant
});
}
callback(err, tenant);
// call your async function instead of return,
// and pass both err and tenant as params
// (one or the other may be undefined though, if it fails/succeeds)
});
... Create additional isolated functions (or even modules) for the rest of your tasks, then you can then call your function like this:
tenant.save(function(err, tenant) {
Tenant.findById(tenant._id, function(err, tenant) {
var user = new User({...})
user.save()
});
});
So, I have the following code:
function SignUp(req, res, next){
const userCreds = {
email: req.body.email,
password: req.body.password
}
//Username and password must exist
if(!userCreds.email || !userCreds.password){
res.status(422).send({ error: 'Email and Password required'});
throw new Error(('Email and Password Required'));
}
//See if email is already being used
Users.findOne({ email: userCreds.email })
.then(function(user){
//If user does exist, return Error
if(user){
res.status(422).send({ error: 'Email is in use'});
throw new Error(('Email and Password Required'));
}
//Else if email is true, create and save user error
const newUser = new Users(userCreds);
//Save the user
return newUser.save(); //Return promise
})
.then(function(doc){
//Respond saying all OK
res.json({
success: true,
email: doc.email
});
})
.catch(function(err){
if(err)
return next(err);
});
}
The function above is passed to an Express route, like this
app.get('/signup', SignUp);
In this code, there are two different 'errors' that can occur and I need to handle. One kind of error is that the user request cannot be processed (Trying to create an account without supplying both Email and Password, or using an Email that already is being used). The second kind of error is one that I have less control in: rejected promises from the Mongoose package.
Let's say that I have received a bad request, an error of type 1. I want to handle it by setting the header of the response to 422, and sending that response with a message detailing why it could not be processed. At that point the execution would end.
If I get an error of type 2, I want to call next(error) and stop execution at that point.
The problem is, by chaining .then() functions, I cannot return from a block of code without jumping into the following .next().
One way to get around this is by throwing an error via throw new Error() when I get an error of either type 1 or 2, and handle the case in .catch(), but I am unsure how much of a good or bad practice this would be.
How can I make it so that I can handle the error in a .then() block and then stop execution? And would that be the best way to do it?
Is there a better way to handle these kind of situations in Express? Am I missing anything?
Thank you!
A solution would be to create an Error subclass (for instance, using error-subclass) and throw instances of those in case you want to signal a processing error.
Subsequently, in the .catch() handler you'd check if the error is an instance of that custom error class, and if so, return a 422 response. If not, pass it to next instead:
const ErrorSubclass = require('error-subclass').default;
class ProcessingError extends ErrorSubclass {}
Users.findOne({ email: userCreds.email })
.then(function(user){
if (user) {
throw new ProcessingError('Email and Password Required');
}
...
}).catch(function(err) {
if (err instanceof ProcessingError) {
return res.status(422).send({ error: err.message });
}
next(err);
});
Im creating an a API using NodeJS with the express framework and mongodb to store my data.
I have a register function which does 3 main things.
Creates the new.
Creates a token and associates it with a user.
Sends an email.
module.exports.register = function(req, res) {
var input = req.body;
var token = uuid.v4();
// Create a new user
var user = new User ({
username: input.username,
email: input.email,
password: input.password,
active: false
});
user.save(function(err) {
if(err) return res.json({success: false, errors: 'Failed To Create User'});
});
// Create a new Token
var newToken = createToken('new', null, user._id);
// Assign New Token To New User
if(newToken) {
user.tokens.push(newToken._id);
user.save(function(err) {
if(err) return res.json({success: false, errors: 'Failed To Save User Token'});
});
}
// Send Email To User
var mailData = {
from: 'deleted#hotmail.com',
to: input.email,
subject: 'Activate Your Account',
text: 'http://localhost:8080/api/auth/activate/' + token
}
mail.messages().send(mailData, function(err, body) {
if(err) return res.json({ success: false, errors: 'Failed To Send Email' });
});
return res.json({
success: true,
status: 'Successfully Registered User, Check Email To Activate'
});
}
Now even if there are errors whilst creating the user or the token or sending an email. It's always going to return that it successfully registered a user. How can i restructure / handle this better?
I also have the problem where if the email fails to send the user and token will have already been created, how do i solve this issue? Would i just create a resend activation function?
You mention that it's always going to return that it successfully registered a user. It will also send the email even if the token creation failed.
One (not very pretty) way to do it would be to continue with the next step inside the callback function of the previous step:
user.save(function(err) {
if(err) {
return res.json({success: false, errors: 'Failed To Create User'});
} else {
// Create a new Token
var newToken = createToken('new', null, user._id);
// Assign New Token To New User
if(newToken) {
user.tokens.push(newToken._id);
user.save(function(err) {
if(err) {
return res.json({success: false, errors: 'Failed To Save User Token'});
} else {
// Send Email To User
var mailData = {
from: 'deleted#example.com',
to: input.email,
subject: 'Activate Your Account',
text: 'http://localhost:8080/api/auth/activate/' + token
}
mail.messages().send(mailData, function(err, body) {
if(err) {
return res.json({ success: false, errors: 'Failed To Send Email' });
} else {
return res.json({
success: true,
status: 'Successfully Registered User, Check Email To Activate'
});
}
});
}
});
}
}
});
As you can see, it looks like a callback-piramyd-of-doom very fast, but it only sends the success response when all the previous steps have completed.
You should also add the else case when the newToken is not created.
You should remove final return statement (from the end of your code) and return at the correct place inside each callback if there is no error.
If you send your response in the body of the function your callbacks will never get the chance to run. Therefore you must nest your callbacks and only call res.send if you are
returning early due to an error or
if everything is complete.
e.g.
// Create a new user
var user = new User ({
username: input.username,
email: input.email,
password: input.password,
active: false
});
user.save(function(err) {
if(err) return res.json({success: false, errors: 'Failed To Create User'});
// Create a new Token
var newToken = createToken('new', null, user._id);
// Assign New Token To New User
if(newToken) {
user.tokens.push(newToken._id);
user.save(function(err) {
if(err) return res.json({success: false, errors: 'Failed To Save User Token'});
// Send Email To User
var mailData = {
from: 'deleted#hotmail.com',
to: input.email,
subject: 'Activate Your Account',
text: 'http://localhost:8080/api/auth/activate/' + token
}
mail.messages().send(mailData, function(err, body) {
if(err) return res.json({ success: false, errors: 'Failed To Send Email' });
return res.json({
success: true,
status: 'Successfully Registered User, Check Email To Activate'
});
});
});
}
});
Asynchronous alternatives
Unfortunately, with node.js you should get used to and understand callbacks; even if you end up using something else most of the time. The way your code was structured was neater and logical but does not work in node.js because you have to wait for the callbacks to complete before you can return from your function.
However, callbacks are the default but one of the worst mechanisms for handling asynchronous logic. If you want to structure the code differently you have quite a few options. Here are just a couple:
Use promises instead of callbacks.
In your case your database library (mongoose? sequelize?) should have something built in that allows you to write your code like this:
user.save()
.then(function () {
// step 1
})
.then(funciton () {
// step 2
})
.done()
This style of programming is well worth learning and will make your code more readable than callbacks. callbacks vs promises
Use Koa instead of express.
Koa, is the next generation of express written by the same people. It uses generators instead of callbacks which means you can write code that looks more like this:
// this is just an example
var result = user.save();
if (result.error) return res.send({success : false, ...});
user.token = getNewToken();
user.update();
if (result.error) return res.send({success : false, ...});
return res.send({success : true, message : "Good news, no errors"});
Generators/(aka async functions) are the direction Javascript is moving in but there is a learning curve to start using. Behind the scenes there is something very complex going on to make asynchronous code appear exactly like synchronous code. Basically, the functions know how to pause execution until they are required again.
Start with callbacks
Like I say, callbacks are not that nice. However, you should get used to using them. They are the basic building blocks of node.js and it take a while to get comfortable with better alternatives. It's also important to get used to them because otherwise you won't appreciate why the alternatives are better.
Good luck and watch out for callbacks inside loops :)
Please , I have setup passport ldapauth which works fine with all parameters, the problem is if the username or password is wrong, the it does not execute further to the verify callback function at all. It just stops. Due to this I cannot give feedback to the users to indicate what is actually wrong. Is there any clue what I am missing?. This is the structure
passport.use('ldapStudent', new LdapStrategy({
usernameField: 'username',
passReqToCallback:true,
server: {
url: '..........',
bindDn: '.............',
bindCredentials: '..........',
searchBase: '..............',
searchFilter: '.............',
searchAttributes: ['givenName','sn'],
tlsOptions: {
ca: [fs.readFileSync('./ssl/server.crt', 'utf8')]
}
}
},
function (req, user, done) {
//now check from the DB if user exist
if(user){
//check if user email exist;
User.findOne({'EmailAddress': user}, function (err, userdata) {
// In case of any error, return using the done method
if (err)
return done(err);
//user exist redirect to home page and send user object to session
if (userdata) {
//userActivity(PostActivity);
console.log(userdata);
return done(null, userdata);
}else {
//new user, add them to the user model
var newUser = new User();
newUser.EmailAddress = req.body.username,
newUser.JoinedDate = Date.now(),
newUser.UserType = 'Student'
newUser.save(function (err, result) {
if (err) {
console.log('Error in Saving NewUser: ' + err);
} else {
console.log(result);
var PostActivity = {
ActivityName: req.res.__('Student Joined'),
ActivityDate: Date.now(),
UserID: result._id,
UserIP: (req.header('x-forwarded-for') || req.connection.remoteAddress ) + ' Port: ' + req.connection.remotePort
};
userActivity(PostActivity);
console.log('User Registration successful');
return done(null, newUser, req.flash('SuccessMessage', req.res.__('You have been successfully Registered')));
}
})
}
});
}else{
return done(null, false, req.flash('ValidationError', req.res.__('Wrong password and/or email address')));
}}));
This is where i actually do the login
router.post('/login', passport.authenticate('ldapStudent', {
successRedirect: '/',
failureRedirect: '/userlogin',
failureFlash: true
}));
The code works well , just as I expect, the parameters for the ldap option object are intentionally omitted.
The problem is when the user credential are not correct, the verify callback does not get executed at all and so, I can not return a flash message for the user to know what is happening
passport-ldapauth does not execute the verify callback if there is nothing to verify which is the case if the credentials are incorrect and the user is not received. This is in general how the strategies tend to work, e.g. passport-local does not execute verify callback if the username or password is missing.
Strategies, passport-ldapauth included, also usually include a (configurable) message for the failure flash. General configurable login failure messages for passport-ldapauth are listed in the documentation. Each of the messages also has a default value so even when not configured the failure flash message is set (given of course that you have flash middleware in use)
Also, you are not supposed to use req.flash() in the callback of the verify function but to supply an info message.
I'm following along in the MEAN machine book and in chapter 9 about Node authentication. I have routes for all users, get, post, put and delete working.
Setup the authenticate route below:
// route to authenticate a user (POST http://localhost:8615/api/authenticate)
apiRouter.post('/authenticate', function(req, res) {
// find the user
// select the name, username and password explicitly
User.findOne({
username: req.body.username
}).select('name username password').exec(function(err, user) {
console.log(user);
if (err) throw err;
// no user with that username was found
if (!user) {
res.json({ success: false, message: 'Authentication failed. User no found.'});
} else {
// if user is found and password is right
// create a token
var token = jwt.sign({
name: user.name,
username: user.username
}, superSecret, {
expiresInMinutes: 1440 // expires in 24 hours
});
}
});
});
My full server.js file here:
https://github.com/leongaban/awesome-test/blob/865714ade6b2f15ffcd8f1fc72ad0ad18836604b/server.js
I created a new user chris / supersecret
Then tried to authenticate him using Postman and it always hangs up :(
Any idea what could be causing it to get stuck?
You're not sending a response when the user is found and the password matches. You create the token but don't do anything (with it) afterwards.