I am quite new in backend, and its my first time using a middleware with nodejs. Until now, my register function and format password are functionning on postman, since when I register a new user, I find on my DB a hashed password.
however, when I want to log in with the same user, I receive my message (wrong password or mail).
my console.log(user) read properly the user profil on node, but after, it seems like bcrypt.compareSync is not functionning. id my console.log(token), returns nothing.
I surely have a problem on my generateToken function or on my login function but I cannot figure it out.
you can see below my code.
login (req,res){
Users.find({mail:req.body.mail})
.then(users =>{
console.log(users);
if(users.length > 0 && bcrypt.compareSync(req.body.mail+req.body.password, users[0].password)){
const token= generateToken(users[0]);
console.log(token);
res.status(200).send('operation succeed: \n' + token);
//res.statut(200).redirect('/logged');
}
else{
res.status(500).send('wrong password or mail');
}
})
.catch(err =>{
res.send(err);
});
},
}
my generateToken function:
function generateToken(user){
const payload={
iat:moment().unix(),
exp:moment().add(14,'days').unix(),
iss:user.mail,
sub:user.password
}
return jsonwebtoken.sign(payload,'app_secret');
}
thank you for your help.
Related
I am trying to send http requests using axios to my node backend. For some reason, axios keeps returning a 500 (Internal Server Error) even when thunder client (dollar store version of postman) is able to send the request and get a proper response.
index.js (server)
app.get('/api/login', async (req, res) => {
try {
const user = await User.findOne({ email: req.body.email })
if(user===undefined) { res.status(404).json("user not found"); }
const validPassword = await bcrypt.compare(req.body.password, user.password)
!validPassword && res.status(400).json("wrong password")
res.status(200).json(user)
} catch (err) {
res.status(500).json(err)
}
})
Login.js (frontend)
const login = (email, password) => {
console.log(email + ': ' + password)
axios.get('http://localhost:8800/api/login', { email: email, password: password })
.then((response) => console.log(response))
.catch((err) => console.log(err.response))
}
err.response returns no useful data and err.response.data is a blank object. I've tried to edit the request header, but it is already 'application/json'. Again, this request works on thunder client and I made sure that the data I passed in was correct through the console.log(email + ': ' + password . I've been trying to fix this issue for hours so please help. Thank you in advance.
Update: I had previously binded the login function to an onClick to a button, but I put the axios function directly into the brackets instead of login(email, password). The issue persists.
Second Update: I followed the comments' advice and console logged the error on the terminal. It returned TypeError: Cannot read properties of null (reading 'password'). This was strange because in the function, I had console logged password and it returned the proper text. It also says that it cannot find a user within my database that uses the email I am currently using, but even when I pass in the exact email I use in thunder client requests, I still get the error. I think the data is not getting there properly.
Third Update: My hypothesis is confirmed. In index.js, I made the route console log req.body.email and it returned undefined. I passed in an object that I JSON stringified and when console logged in the browser, it returns a proper object. The request is sending an object with undefined properties although I am passing in an object with values
In this case, the issue was that the request was a get request, not a post request. Get requests do not take in data while post requests do. Here is the fix:
index.js (server)
app.post('/api/login', async (req, res) => {
try {
const user = await User.findOne({ email: req.body.email })
if(user===undefined) { res.status(404).json("user not found"); }
const validPassword = await bcrypt.compare(req.body.password, user.password)
!validPassword && res.status(400).json("wrong password")
res.status(200).json(user)
} catch (err) {
res.status(500).json(err)
}
})
If you have to receive the request parameters in body (mainly in json format) then you have to go with POST type request.
In the GET type request, you can get request parameters in the form of params and query string.
Parameters may be either part of path:
myapi/customers/123
or a query string:
myapi?customer=123
More details can be found here:
https://www.restapitutorial.com/lessons/httpmethods.html
What I want to achieve
A user, who logged in or signed up should not re-login after one hour. The restriction of one hour comes from firebase authentication, if not prevented (what I try to accomplish).
Problem
After a user is logged in via firebase authentication (signInWithEmailAndPassword) I always get null for currentUser and onAuthStateChanged.
What I tried
I'm using React (v17.0.2) using 'Create React App'. On server side I'm using NodeJS (v12). The communication between both is accomplished using axios (v0.21.1)
First I tried to send the token stored in localStorage, which came from firebase (server side), back to the server. But the server tells me, that the token is no longer valid. Server side code as follows:
module.exports = (request, response, next) => {
let idToken;
if (request.headers.authorization && request.headers.authorization.startsWith('Bearer ')) {
idToken = request.headers.authorization.split('Bearer ')[1];
console.log("idToken:", idToken);
} else {
console.error('No token found');
return response.status(403).json({ error: 'Unauthorized' });
}
admin
.auth()
.verifyIdToken(idToken)
.then((decodedToken) => {
console.log('decodedToken', decodedToken);
request.user = decodedToken;
return db.collection('users').where('userId', '==', request.user.uid).limit(1).get();
})
.catch((err) => {
console.error('Error while verifying token', err);
return response.status(403).json(err);
});
};
After that I tried the following code on client side.
handleSubmit = () => {
const userData = {
email: this.state.email,
password: this.state.password
};
axios
.post(firestoreUrl() + '/login', userData)
.then((resp) => {
console.log("token:", resp.data); //here I get a valid token
localStorage.setItem('AuthToken', `Bearer ${resp.data.token}`);
console.log("firebase.auth().currentUser:", firebase.auth().currentUser); //but this is null
})
.then((data) => {
console.log(data);
console.log("firebase.auth().currentUser:", firebase.auth().currentUser); //still null
})
.catch((error) => {
console.log("Error:", error);
});
};
What irritates me is that I get a token from firebase (server side), the token is then stored in localStorage (client side) but firebase then tells me, that the currentUser is null. But presumably they are not mutually dependent =/.
I'm able to access all secured sites in my app. I can log out and in again. But whatever I do the currentUser is null.
I also tried to run the code above in componentDidMount()-method. But no success.
I tried an approach from this link (hopefully in a way it should be), but it didn't work. Still getting null for both currentUser and onAuthStateChanged if I implement following code.
firebase.auth().onAuthStateChanged(user => {
if (user) {
console.log("state = definitely signed in")
}
else {
console.log("state = definitely signed out")
}
})
I always get logged to the console, that the user is 'definitely signed out'.
During research I noticed that the point at which I should try to get the currentUser-Status is kind of tricky. So I guess that one solution is to implement the currentUser-code at another/the right place. And here I'm struggling =/.
As I found out at a similar question here on SO, I did a bad mistake. Apparently, it's not a good idea to perform the signIn- or createUser-functionality on server side. This should be done on client side. In the question mentioned above are some good reasons for doing that on server side but in my case it's quite ok to run it on client side.
Thanks to Frank van Puffelen for leading the way (see one of the comments in the question mentioned above).
router.post("/login", async (req, res) =>
{
try
{
console.log(req.body.email)
const user = await User.findOne({email: req.body.email, password: req.body.password})
if(user)
{
return res.redirect("/")
}
console.log(user)
res.status(200).send(user)
}
catch(e)
{
res.status(400).send(e)
}
})
I am trying to look for a user in MongoDB but the user variable returns null and I am getting status code 200 with an empty object.
Thanks for answers. I found the missing point. I stored the hashed password in database but I forget to search for hashed password
First of all, you should consider not storing passwords as plaintext in your database and when finding a user querying them. I recommend using a hashing algorithm like bcrypt to validate them.
Regarding your question, you have to know that mongoose does not throw an error when it finds no entries and you only send 404 when an error is thrown. I would recommend you changing your code to the following:
router.post("/login", async (req, res) =>
{
try
{
console.log(req.body.email)
const user = await User.findOne({email: req.body.email})
if (user) {
// TODO: Use hashing algorithm to verify password
console.log(user)
res.status(200).send(user)
return res.redirect("/")
} else {
res.status(400).send("User not found")
}
}
catch(e)
{
res.status(500).send("Server error: " + e.message)
}
})
This didn't match the query. Are you sure the e-mail and password are correct? The syntax is correct. It shows status code of 200, that means your code is perfect, but your email pr password is wrong or there is no data in the database with that email and password. Like #Adwirawien said you should hash your password.
looks like your condition:
if(user)
should be written
if(!user)
Or am I wrong ?
This is a weird one.
What im trying to do
Create an authentication server in node.js using Passportjs local strategy, and JWT. Allowing for account registration with email & password, with passwords hashed with 'crypto'
What's happening
So when I login with the right password, to a pre existing model, authentication fails in the APi for having a wrong password. Though theres some weird stuff going on.
What I've tried
Essentially when I make the post request:
OPTIONS /api/login calls
It goes through my passport config, and in the typical function where you check if the password is correct
side note: POST api/login is logged to the console
The function in my passport config:
if (!profileController.passMatch(username, password)) {
console.log('pass was wrong');
return done(null, false, {
message: 'Password is wrong'
});
}
The 'pass was wrong' thing calls, failing the authentication with done(). Though in passMatch, as you'll see below, it does show the correct password
passMatch function in profile controller:
module.exports.passMatch = (email, password) => {
User.findOne({email: email}, (err, user) => {
if (err) { console.log ("error at passMatch: " + err); }
var hash = crypto.pbkdf2Sync(password, user.salt, 1000, 64, 'sha512').toString('hex');
console.log(user.hash == hash);
return (user.hash == hash);
});
return false;
};
Though if you notice the console log where I check if the hash comparison is correct. That log statement is printed to the console after 'pass was wrong' is logged. It's also printed after the passport.authenticate call in my login function concludes a failed authentication at the console.log(info)
Login function:
module.exports.login = (req, res) => {
console.log('beginning to authenticate');
passport.authenticate('local', (err, user, info) => {
console.log ("authenticating");
var token;
// If passport throws an error
if (err) {
res.status(404).json(err);
console.log("error logging in");
return;
}
// If a user is found
if (user) {
// Respond with JWT
token = createJwt(user)
res.status(200);
res.json({
"token": token
})
console.log("user logged in");
// If a user wasn't found
} else {
res.status(401).json(info);
console.log(info);
}
})(req, res);
};
Error logging in isn't called, but the console.log(info) is called with the error message from the done() message in the config.
What's going wrong here?
In the "passMatch" function, I query for the user again (which is just inefficient), but since this operation was asynch, it was being skipped to the "return false" statement after, and in the passport authentication config process, it recieved that false, causing authentication to fail, but the "log" to be returned after cause it took longer.
How I fixed it
I passed in the user object that passport already queried instead of the username into passMatch, then had two operations to check if the hash was the same and returned that, and now it works.
The new code
module.exports.passMatch = (user, password) => {
var hash = crypto.pbkdf2Sync(password, user.salt, 1000, 64, 'sha512').toString('hex');
return user.hash == hash;
};
Also the necessary change in the passport config to pass in the user instead of the username as the first param to that function.
I'm using passport-jwt to authenticate some routes and I'm creating my jwts with node-jwt-simple/jwt-simple but facing some difficulties cause it looks like my passport-jwt authenticate middleware is not being called at all.
Here is my
passport-jwt-strategy
const jwtOpts = {
jwtFromRequest: ExtractJwt.fromHeader('Authorization'),
secretOrKey: secret,
};
passport.use(new jwtStrategy(jwtOpts, (payload, done) => {
console.log('payload ', payload.sub);
User.findById(payload.sub, (err, user) => {
if(err) { return done(err); }
if(!user) { console.log('didnt find!'); return done(null, false); }
done(null, user);
});
}));
which i'm then integrating it over here.
routes file
router.get('/success',
passport.authenticate('jwt', {session: false}),
async (ctx, next) => ctx.body = await "success!");
Here is also the way I make my jwt.
function tokenForUser(user) {
const timeStamp = new Date().getTime;
return jwt.encode({sub: user._id, iat: timeStamp}, secret);
}
//- Later in signup process
userToSave.save(async(err, user) => {
if(err) { return next(err); }
const token = await tokenForUser(user);
next(token);
});
//- If this helps, here is how my secret file looks like.
const secret = "JKAha23ja1ddHdjjf31";
export default secret;
Problem comes, when I hit that route i only get Unauthorized and in the console nothing gets logged out not even the 'payload' key I specified first.
I should also say that I have the token at ctx.request.get('Authorization') (Koa based) i think it's something like req.header('Authorization') with express in all routes.
Also The exact express based problem can be found on the github issues of node-jwt-simple here incase there is any problem with my code samples.
Thank you.
After I wrapped my head right i knew that this has been my horrible understanding of how the whole authentification process works.
When I decoded the token from ctx.get('Authorization') I got a different _id than the one stored in the db Because I had hardcoded Authorization header in postman and thought "If I ctx.set('Authorization', token); It will replace the one I hardcoded on postman".
Less did I think that this jwt will be included in a header of requests when I make http calls on front end.
I naively thought jwts are passed directly from the server to the browser (Something like how render works) and Not from the server to an ajax process which later embeds it in request made which is the correct way.
The whole code is awesome, except now I have to just pass the token ctx.body = token; after I created it when I signed up.
Thank You.