I have two routes in my node js app: users & records
In the users route I am getting the token on /login path:
users.post('/login', function(req, res) {
const email = req.body.email;
const password = req.body.password;
var token = '';
connection.query(`SELECT * FROM ${process.env.USER_TBL} WHERE EMAIL = ?`, [email], function(err, results, fields) {
if (err) {
res.status(200).json({'message' : err, 'token' : token})
}
if (results.length > 0) {
bcrypt.compare(password, results[0].PASSWORD).then(function(match) {
if (match == true) {
token = jwt.sign(JSON.parse(JSON.stringify(results[0])), process.env.SECRET_KEY, {
expiresIn: 5000
})
res.status(200).json({'message' : 'User verified', 'token' : token})
} else {
res.status(200).json({'message' : 'Email or Password does not match', 'token' : token})
}
})
} else {
res.status(200).json({'message' : 'Email does not exists', 'token' : token})
}
})
connection.end(err => {
if(err) console.log(err);
})
})
In my second route records I am making a request to path /addNewRecord,
records.post('/addNewRecord', function(req, res) {
//need the token to authenticate the request
});
here I need the token generated from /login route to send in the hrader and authenticate my request.
but I am understand how can I pass token value from users route to records route.
When you login you transmit your token to the user, capture that token and put it into local storage on the client. Then it available for the next call to the server.
localstorage.setItem('token', token)
When making your next server request you would use the stored token in your header.
$.ajax({
url: "URL",
method: "POST",
headers: {
'authorization': localstorage.getItem('token'), //YOUR TOKEN GOES HERE
'anotherHeader': 'headerValue2'
}
});
Then you can capture the token from the next request to verify it is valid
records.post('/addNewRecord', function(req, res) {
let token = req.headers['authorization']
});
You can't pass a token between server side code in that manner. It has to be sent from the client. Whenever /login is called from the client side and it receives a response, you can then store the token from that response on the client side. When calling /addNewRecord simply add the token in the header of the request on the client side and it can be accessed by inspecting req.get(headerName) or req.headers on the server side.
Related
I wanted to know what is the best way to add authorization header in nodejs app so that client req header gets that header set from the server on login. That's, once a user logs in, server should set an authorization header with the token, hence let that header persist on all routes, so that if it is present on the client, the server can allow user to visit routes.
I have seen alot of talk about adding it as a cookie but I prefer authorization header for this scenario. In between I would want to make sure on logout token is destroyed.
I am using JWT, Passport, express, nodejs for this app. Any help would be appreciated.
let access;
/\*\*
* #route POST api/auth/login
* #desc Login user and return token
* #access public
\*/
exports.login = async (req, res, next) =\> {
try {
// Make sure this account exists
const user = await Users.findOne({ email: req.body.email }).select(
'+password',
);
if (!user)
return res.status(401).json({
message: 'Invalid email or password',
});
// Validate password
const validate = await bcrypt.compare(req.body.password, user.password);
if (!validate)
return res.status(401).json({ message: 'Invalid email or password' });
// Make sure user has been verified
if (!user.isVerified)
return res.status(401).json({
type: 'no-verified',
message: 'Your account has not been verified.',
});
const token = user.generateJWT();
access = token;
// Login user, write token, and send back user
const { password, ...data } = user._doc;
res.status(200).json({ hash: token });
} catch (err) {
res.status(500).json({ success: false, message: err.message });
}
};
exports.addHeader = (req, res, next) =\> {
console.log(access);
if (!access) {
console.log('token: undefined');
}
req.headers.authorization = 'Bearer ' + access;
next();
};
// In index.js
app.all('\*', authController.addHeader);
Tried that, could not get it to work.
I am creating a blog using NodeJS and Express framework. To add extra security, I have separated the Sign Up & Login from the application. For this, I have created a REST API using Express in PORT 5000 (just for authentication of protected routes) and the actual application runs on 5001.
When user POSTS from the Login page in browser to ”http://www.example.com:5001” i.e., to my application. I authenticate the values from the REST API running on PORT 5000.
//First user posts login data into my application
router.post('/login', async (req, res) => {
try{
let payload = {
'username': req.body.username,
'password': req.body.password,
}
//sending the parameters for authentication to PORT 5000 (i.e., REST API)
const response = await axios.post('http://www.example.com:5000/login', payload);
if(response)
{
console.log(response.data);
newToken = {
token: response.data.token,
refreshToken: response.data.refreshToken
}
res.redirect('/');
}
}
catch(err){
console.log(err);
}
})
The REST API responds after receiving the request.
app.post('/login', async (req, res, next) => {
try{
let token, refreshToken;
await connectDB();
let result = await findOneUser(req);
if(result)
{
//Initialize JWT Token Signature
try {
//Creating jwt token
token = jwt.sign(
{ username: req.body.username },
"secretkeyappearshere",
{ expiresIn: "1h" }
);
refreshToken = jwt.sign({ username: req.body.username },
"some-secret-refresh-token-shit",
{ expiresIn: "3h" }
);
} catch (err) {
console.log(err);
const error = new Error("Error! Something went wrong.");
return next(error);
}
}
const response = {
"status": "Logged in",
"token": token,
"refreshToken": refreshToken,
}
res.send(response);
return next(false);
}
catch(err){
console.log(err);
tokenList[refreshToken] = response
res.status(200).json(response);
next(false);
}
});
Everything goes fine and I receive the token in my application.
But, problem starts when I resend the token. Suppose I have to authenticate a route (say) the settings route. I want users should again authenticate. So, I send back the token which I received during my first call to the REST API again, but now I don't receive anything back from the API to my application.
The first piece of code above has a variable called "newtoken", which I have set as global in my application.js file. I store the token that I received in the first call in this variable. Now, I use this to send another request to authenticate for the settings page/ route.
So, I again send this token in my next call like this
router.get('/settings', async (req, res) => {
let data = {
params: {
from: "2022-03-12",
to: "2022-03-13"
},
headers: {
"X-Auth-Token": newToken,
"content-type": "application/json"
}
};
try{
const call = await axios.post('http://www.example.com:5000/validate', data);
if(call){
console.log(call.data);
res.render('settings', { name: "Some test data" });
}
}
catch(err)
{
console.log(err);
}
});
As, you can see I am resending the token data with headers. Actually, I have tried all others ways of sending, but nothings works. I do not get any response from my next call. ANy help would be highly appreciated. Thanks in advance.
Your router will handle many requests (from different users) in parallel, but your global variable newToken exists only once, this cannot work.
Your router could either send the token to the client (browser) in a session cookie, or use express-session and store the token in req.session.token:
// newToken = {
// token: response.data.token,
// refreshToken: response.data.refreshToken
// }
req.session.token = response.data.token;
req.session.refreshToken = response.data.refreshToken;
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 am struggling to make a login system using JSON web tokens.
I have made the login (client side) that calls to my server.js file.
This is the login through the client side Below is my handle submit function that calls the server.js login route.How would I use a token here?
handleSubmit(e) {
e.preventDefault();
if (this.state.email.length < 8 || this.state.password.length < 8) {
alert(`please enter the form correctly `);
} else {
const data = { email: this.state.email, password: this.state.password };
fetch("/login", {
method: "POST", // or 'PUT'
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(data => {
console.log("Success:", data);
})
.catch(error => {
console.error("Error:", error);
});
}
}
catch(e) {
console.log(e);
}
This is the login route for my server.js. As you can see I have assigned a jwt but how would I send this back to my login form and utilise it for protected routes.
app.post("/login", async (req, response) => {
try {
await sql.connect(config);
var request = new sql.Request();
var Email = req.body.email;
var Password = req.body.password;
console.log({ Email, Password });
request.input("Email", sql.VarChar, Email);
request.input("Password", sql.VarChar, Password);
const result = await request.execute("dbo.LoginUser");
if (result.recordsets[0].length > 0) {
console.info("/login: login successful..");
console.log(req.body);
const token = jwt.sign({ user: Email }, "SECRET_KEY", {
expiresIn: 3600000
});
var decoded = jwt.verify(token, "SECRET_KEY");
console.log(decoded);
response.status(200).json({
ok: true,
user: Email,
token: token
});
console.log(token);
} else {
console.info("/login: bad creds");
response.status(400).send("Incorrect email and/or Password!");
}
} catch (err) {
console.log("Err: ", err);
response.status(500).send("Check api console.log for the error");
}
});
Essentially all I want is for my submit handler to be called for login. Server returns a jwt token which can then be used to verify other routes.
There are two ways to route:
Use React-Redux and react-router.
Save the fetched JWT token into localStorage and use to validate route within your routes component.
I would recommend in using React-Redux / React-router for protected routing.
Here is a video link to Build Real Web App with React by
Rem Zolotykh
This will help you.
I am trying to understand how app.get() works in calling functions when trying to switch between web pages.
I've created a user-login page that assigns a token to the user and checks it in a function.
I use app.post('/login', login); to call the login function which sends the user object to the server. After creating the token I'm hoping to then render the next page in a function after checking the token. (See code below)
However, I don't really understand how app.get('/', checkToken, getProfilePage) is then called. As I don't think it ever gets called.
I've looked at some websites that explain about HTTP requests but I'm struggling to find out, how it all links together inside app.js.
App.js:
app.post('/login', login);
app.get('/', authorize.checkToken, getProfilePage);
function login(req, res, next) {
userService.login(req.body, (err, user) => {
if (err) {
res.redirect('error.ejs');
console.log(error.message);
}
console.log(user);
if (!user) {
res.status(400).json({ success: false, message: 'Username or
password is incorrect' });
}
else {
res.json(user);
}
})
}
The next login function assigns the token and is used above as middleware:
function login({ username, password }, callback) {
grabUsers((err, users) => {
let user = users.find(u => u.username === username && u.password
=== password);
if (user) {
const token = jwt.sign({ username: username }, config.secret,
{ expiresIn: '24h'
}
);
const { password, ...userWithoutPassword } = user;
user = {
...userWithoutPassword,
success: true,
message: 'Authentication successful!',
token: token
}
};
callback(null,user);
})
}
Inside authorize.js:
let jwt = require('jsonwebtoken');
const config = require('./config.js');
let checkToken = (req, res, next) => {
console.log("check token running...");
let token = req.headers['x-access-token'] ||
req.headers['authorization']; // Express headers are auto
converted to lowercase
if (token.startsWith('Bearer ')) {
// Remove Bearer from string
token = token.slice(7, token.length);
}
if (token) {
jwt.verify(token, config.secret, (err, decoded) => {
if (err) {
return res.json({
success: false,
message: 'Token is not valid'
});
} else {
req.decoded = decoded;
next();
}
});
} else {
return res.json({
success: false,
message: 'Auth token is not supplied'
});
}
};
module.exports = {
checkToken: checkToken }
getProfilePage function:
module.exports = {
getProfilePage: (req, res) => {
res.render('profile.ejs');
}
}
So my login form posts to /login and then once it has been verified I would like to check the token and then call getProfilePage. But how do I call the app.get() after the login data has been posted and authenticated?
You don't call a route from your app, what you need to do is redirect to it : res.redirect('/');
I believe there is a problem with how you try to authenticate a user. Seems to me you send a token in authorization header which is a common practice when you accessing API. Not sure how/when you generate the token and set this header though...
Anyway, this approach is good for authorization but not so good for authentication.
After successful /login request you should set the authentication cookie (user session). To simplify, you can just generate a JWT with userId encoded into it and use it as the value for this cookie (let's call it user-session).
Now after that each time user makes a request the cookie will be sent with it and you can decode the userId from this JWT. (Same thing, but now you'll take token from req.cookies['user-session'] instead of req.headers['authorization']).
But how do I call the app.get() after the login data has been posted and authenticated?
You can either navigate to this page from the client right after you receive successful /login response if you're using AJAX (i.e. window.location.replace('/')) or you can do res.redirect('/') instead of res.json(user) on successful login if you submit the HTML form without AJAX.
Redirect forces a browser to immediately make another request to the URL you specify and by that time you'll have user-session cookie set, i.e. you'll be able to retrieve userId and return correct profile page.