I'm making my own small API, and I've coded out the following POST request to my MongoDB database:
api.post("/account/login", async (req, res) => {
const user = {
username: req.body.username,
password: req.body.password
};
const username = JSON.stringify(user.username);
const hashedPassword = await logincollection.find(user.username).toArray();
const hardCodedPassword = "$2b$10$q0iOBFTqqZ3vnp5oqDQUqejdS7UD/ayw4Q4qgi5hs1pfFI.xfipDS"
console.log(hashedPassword)
// Search for matching login credentials
logincollection.find(user, (err, result) => {
try {
if (bcrypt.compare(req.body.password, hardCodedPassword)) {
// Return credentials back to the client
const sendObject = {
username: result.username,
password: result.password
};
console.log(sendObject);
// Return code 200 (success) to the client
res.status(200).send(sendObject);
// Log to console when user logs in
console.log("User " + username + " logged in");
}
} catch(error) {
// If matching credentials were not found, return code 404 (wrong credentials) to the client
res.status(404).send()
}
})
})
I have set a valid hardcoded password for purely testing purposes. When this request is processed, console.log(sendObject) prints undefined results to the console and bcrypt returns true, no matter what password I put in. What's the problem here?
As #jonrsharpe said, bcrypt.compare returns a Promise not a value. You must use a callback function, Promise.then() or async/await to handle the asynchronous result.
// Search for matching login credentials
logincollection.find(user, (err, result) => {
bcrypt.compare(req.body.password, hardCodedPassword)
.then(match => {
if (match) {
// Return credentials back to the client
const sendObject = {
username: result.username,
password: result.password
};
console.log(sendObject);
// Return code 200 (success) to the client
res.status(200).send(sendObject);
// Log to console when user logs in
console.log("User " + username + " logged in");
} else {
// If matching credentials were not found, return code 404 (wrong credentials) to the client
res.status(404).send()
}
})
.catch(error => {
res.status(500).send()
})
})
Related
This is a todo list web app, I have used nodejs and reactjs in it
I am not able to use the login feature , It shows me the error : invalid token
I have tried hard coding the token (which generates on the sign up) and that way it worked. But with the below code it doesnt work.
Using JWT for Authentication token generation
Funtion that handles the Login Click (user puts email and password)
const handleSubmit = async (e) => {
e.preventDefault();
const response = await fetch('http://localhost:5000/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email: credentials.email, password: credentials.password })
});
const json = await response.json();
if (json.success) {
localStorage.setItem('token', JSON.stringify(json.authToken));
showAlert('Successfully Logged in');
navigate("/");
} else {
alert("Invalid credentials");
}
}
Backend Api Call (Using Nodejs and Express)
router.post("/login", fetchUser,
[
body("email", "Enter a valid email").isEmail(),
body("password", "Password cannot be blank").exists(),
], async (req, res) => {
let success = false;
// if there are errors, handle them with bad requests
const errors = validationResult(req);
if (errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const { email, password } = req.body;
// Check if the user with requested email exists in the database
let user = await User.findOne({ email });
if (!user) {
success = false;
return res.status(400).json({ success, error: "Please enter the correct credentials" });
}
// Check if the user with requested passwork exists in the database
const comparePassword = await bcrypt.compare(password, user.password);
if (!comparePassword) {
success = false;
return res.status(400).json({ success, error: "Please enter the correct credentials" });
}
// Auth Token Generation using jwtToken
const data = {
user: {
id: user.id,
},
};
success = true;
let authToken = jwt.sign(data, JWT_Secret);
res.json({ success, authToken });
} catch (error) {
res.status(500).send("Internal error occured");
}
});
When I tried Hardcording the auth-token it worked
By clicking on login the new auth-token should be generated and set as 'token' in the local storage. Through which data will be accessed using different end points.
At this line json.authToken is a string already. You don't need to stringify it again.
localStorage.setItem('token', JSON.stringify(json.authToken))
Just remove the function and it'll be fine.
localStorage.setItem('token', json.authToken)
I am writing code for basic backend authentication, specifically registering user. I am testing my route by REST Client in VS code.
But the problem is that my route is not getting access to res.body ( the JSON data I am sending).
Here is my route
router.post("/", async (req, res) => {
console.log(req.body);
const { name, email, password } = req.body;
try {
// check if user exists
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({ errors: [{ msg: "User already Exists" }] });
}
const avatar = gravatar.url(email, { s: "200", r: "pg", d: "mm" });
user = new User({
name,
email,
avatar,
password,
});
//encrypt password
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
This is the error and empty req.body
*Server started
Database connected Successfully!
{}
Illegal arguments: undefined, string
*
And this is JSON data i am sending
What seems to be the problem here?
POST http://localhost:5000/api/register
Content-Type: application/json
{
"name": "hello",
"email":"hello#gmail.com",
"password": "0987654321"
}
I'm working on a Node backend that uses MongoDB as its database. When I should send my JWT token within the response header, I get an error that the headers cannot be set after they are sent to the client. Here is my POST request:
api.post("/account/create", async (req, res) => {
// Hash the password using bcrypt
const hashedPassword = await bcrypt.hash(req.body.password, 10);
// Store new login credentials
const user = {
username: req.body.username,
password: hashedPassword
};
// Create a new JWT token
const token = jwt.sign({ username: req.body.username }, secret);
// Search for a matching username from the database
await logincollection.findOne(user, (err, result) => {
// If username was not found, add the credentials to the database
if (result == null) {
// Insert the credentials to the login credentials collection
logincollection.insertOne(user, (err, result) => {
// If an error occurred, return code 404 to the client
if (err) {
res.status(404).send();
}
})
// Create personal collection for the user
userdb.createCollection(JSON.stringify(user.username), (err, result) => {
// If an error occurred, return code 404 to the client
if (err) {
res.status(404).send();
}
})
// Return code 200 (success)
res.status(200).send({ auth: true, token: token });
} else {
// If username was found, return code 400 to the client
res.status(400).send();
}
})
})
When I try to get the token value in another POST request, it returns undefined:
api.post("/account/login", async (req, res) => {
// User object
const user = {
username: req.body.username,
password: req.body.password
};
const token = req.headers["token"];
console.log(token);
// Get username as a string
const username = JSON.stringify(user.username);
// Get hashed password from the collection
const hashedPassword = await logincollection.findOne({ username: req.body.username });
console.log(hashedPassword);
// Search for matching login credentials
await logincollection.find(user, (err, result) => {
// If no token was given
if (!token) {
// Return code 401 to the client
res.status(401).send();
}
// Verify the given JWT token
jwt.verify(token, secret, (err, decoded) => {
// If verification failed
if (err) {
// Return code 500 to the client
res.status(500).send();
}
// Return code 200 and decoded token to the client
res.status(200).send(decoded);
})
// Use bcrypt to compare the passwords and authenticate login
bcrypt.compare(req.body.password, hashedPassword).then(match => {
// If the credentials match
if (match) {
// Return the result as an object
const sendObject = {
username: result.username,
password: result.password
};
// Return code 200 to the client
res.status(200).send(sendObject);
// Log to console when user logs in
console.log("User " + username + " logged in");
// If the credentials do not match
} else {
// Return code 404 to the client
res.status(404).send();
}
// If comparing fails
}).catch(error => {
// Return coe 500 to the client
res.status(500).send();
})
})
})
I'm pretty sure the solution is something incredibly simple, but I just can't seem to solve this, although I've done a lot of research already.
Here's the response returned to the client from the /account/create request:
{
"auth": true,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QyIiwiaWF0IjoxNjMzNzg3MjE2fQ.duo5R9wXpk2Gj-iPHFMaDgKK0p3h6WZf5vnXrZViePo"
}
EDIT: Turns out that the token does not go the header, but to the body. What do I need to do differently to get it passed to the header?
Okay, it looks like I had understood this wrong.
I thought that the token has to be always returned and received in a header. This is not the case. The token response from /account/create has to be in the body. The token has to be set to the header only, when triggering the /account/login request (authenticating the login).
Hopefully this might help someone having the same question in the future.
In the result == null case, you have three statements that send a response: two asynchronous res.status(404) statements and one synchronous res.status(200) statement. Only of them may be executed per request.
But with the current code, the status 200 statement is executed for every request, even if a failed database operation later leads to an additional status 404 statement, which then causes the observed error.
I'm making a Node backend using express (also using bcrypt for password hashing and jsonwebtoken for authorization). My requests are in separate files and they all use module.exports. I have this login request that is in a file called login.js:
router.post("/account/login", async (req, res) => {
// User object
const user = {
username: req.body.username,
password: req.body.password
};
// Get user name only
const username = {
username: req.body.username
};
// Create a new JWT token
const token = jwt.sign(username, secret);
// Get username as a string
const usernameString = JSON.stringify(user.username);
// Get hashed password from the collection
const hashedPassword = await logincollection.findOne({ username: req.body.username });
// Search for matching login credentials
await logincollection.findOne(username, (err, result) => {
// If username was not found, return code 400
if (result == null) {
res.status(400).send();
}
// Proceed if username was found
else {
// If no token was given, return code 401
if (!token) {
res.status(401).send();
}
// Verify the given JWT token
jwt.verify(token, secret, (err, decoded) => {
// If verification failed, return code 500
if (err) {
res.status(500).send();
}
})
// Use bcrypt to compare the passwords and authenticate login
bcrypt.compare(req.body.password, hashedPassword.password).then(match => {
// If the credentials match
if (match) {
// Insert the token into a cookie
res.cookie("token", token, { httpOnly: true });
// Return code 200
res.status(200).send({ auth: true, token: token });
// Log to console when user logs in
console.log("User " + usernameString + " logged in");
}
// If the credentials do not match
else {
// Return code 404
res.status(400).send();
}
// If comparing fails
}).catch(error => {
// Return code 500
res.status(500).send();
})
}
})
})
Every user has their own collection in a MongoDB database that is created, when the account is created. These collections are named after the username, since usernames are unique here. In this load.js file I have a request that loads all the objects in the collection:
router.post("/note/load", async (req, res) => {
// User object
const username = {
username: req.body.username
};
// Get usercollection depending on username
const usercollection = userdb.collection(JSON.stringify(username));
// Load the notes
const loadNote = await usercollection.find().toArray();
// Return code 200 and sendObject
res.status(200).send(loadNote);
})
Obviously, the code above will result in an error, because the username is not given in the request. Is there a way to pass the username to any request, when needed? Or can I use JWT for this, and if so, how? I have tried storing usernames in a variable, but that doesn't work, since the variable gets overwritten, when two users are logged in at the same time.
PS. The title is poor, because I don't know how to put this shortly. If you have any suggestions or corrections regarding the question or the title, please comment below.
Currently my project takes sign up requests correctly, and stores emails and encrypted passwords into my MongoDB database. However, when I take the same e-mail and password I signed up with, I get an error telling me that my password is incorrect
I have the following sign in request set up:
module.exports.login_post = async (req, res) => {
// shows us the data that was sent.
// console.log(req.body)
// destructure to just get the email and password from the body
const { email, password } = req.body;
console.log('login called')
try {
const user = await User.login(email, password);
// creates and sends a jwt cookie
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge: maxAge * 1000 });
res.status(200).json({ user: user._id });
} catch (err) {
const errors = handleErrors(err);
res.status(400).json({ errors });
}
console.log(email, password)
res.send('user login')
};
In this code I reference User.login, which is imported from my User Model
userSchema.statics.login = async function (email, password){
const user = await this.findOne({ email });
if(user){
const auth = await bcrypt.compare(password, user.password)
if(auth){
return user
}
throw Error('incorrect password')
}
throw Error('incorrect email')
}
Right now, I can register a user, but when I turn around and use the same login and password in Postman I get the error "incorrect password " from my User.login function.
At this point, I am not sure what steps I should be taking in problem solving this. Is there a way to console.log the encyrpted version of the password I try to log in with, so I can make sure I have Bcrypt set up to correctly encrypt the user's password?
Edit: My signup code was requested:
module.exports.signup_post = async (req, res) => {
// destructure to just get the email and password from the body
const { eMail, password } = req.body;
// console.log(email, password)
// add a try catch block
try {
const user = await User.create({
eMail,
password,
words: [
]
});
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge: maxAge * 1000 });
// (max age is in seconds and this take milliseconds)
res.status(201).json({ user: user._id });
} catch (err) {
console.log(err)
const errors = handleErrors(err);
// console.log(err);
res.status(400).json({ errors });
}
res.send('new signup')
};
2nd Edit: and this is my middleware the fires before I save a new user and encrypts the password
// hash passwords using SALT
userSchema.pre('save', async function (next){
const salt = await bcrypt.genSalt()
this.password = await bcrypt.hash(this.password, salt)
console.log(this.password)
next()
})
Use below code as your password compare you chnage saltRounds with your bcrypt salt rounds it will compare it with password in database
const comparePassword = (hashedPassword, password) => {
return bcrypt.compareSync(password, hashedPassword);
};
let validations = {
comparePassword,
}
module.exports = validations;