I have created a project in nodejs to check how jwt works. I have put authentication on a route I am able to validate with postman. Although I want that validated route to render a page if the user is logged in. I am using handlebars.js for templating. Can anyone help me how to make this happen.
index.js (Route)
var ctrlUsers = require('../controllers/users.controllers.js');
router
.route('/users/login')
.post(ctrlUsers.login);
app.use(ctrlUsers.authenticate);
.get(ctrlUsers.authenticate,ctrlUsers.showaddress);
module.exports = router
users.controller.js
module.exports.login = function(req, res) {
console.log('logging in user');
var username = req.body.username;
var password = req.body.password;
User.findOne({
username: username
})
.exec(function(err, user) {
if (err) {
console.log(err);
res.status(400).ender('error');
}
else {
if (bcrypt.compareSync(password, user.password)) {
console.log('User found', user);
var token = jwt.sign({ username: user.username }, 's3cr3t', { expiresIn: 3600 });
console.log("Token1" + token);
res
.status(200)
.redirect('/');
} else {
res
.status(401)
.render('error');
}
}
});
};
module.exports.showaddress = function(req, res) {
res.render('index');
console.log("I am in");
console.log(req.user);
}
module.exports.authenticate = function(req, res, next) {
var headerExists = req.headers.authorization;
console.log(headerExists);
if (headerExists) {
var token = req.headers.authorization.split(' ')[1];
jwt.verify(token, 's3cr3t', function(error, decoded) {
if (error) {
console.log(error);
console.log("Token 2" + token);
res.status(401).json('Unauthorized');
}
else {
req.user = decoded.username;
console.log("Here");
next();
}
});} else { console.log("Token 3" + token) res.status(403).json('No token provided'); } };
On checking with Postman this works fine. I am just not sure how to make it work on browser. Like it should show me a page when I hit "http://localhost:3000/users/showaddress" when logged in.
Can anyone please help with this.
Quick answer is, send the JWT via a cookie instead of a header, and the browser will do the job for you.
Note that this is only possible if you only have web clients. To support native clients too, you basically need to either airways send the JWT in a header, or let the client specify whether it wants it in a header or cookie. Native clients doesn't handle cookies that well.
If you want to stick with the header, you need a network layer in your client (JavaScript) application that adds the JWT as a header to each outgoing request to the server that issued the JWT. (Make sure to store the JWT in a secure storage in the client. Using cookies, you get this for free too.)
You probably med this layer anyway depending on how the backend behaves when a JWT expires. A common setup is that the client needs to detect when the JWT is expired (backend typically responds 401) and (try to) login again in the background.
Related
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 have been following the tutorial on creating a test API application from this article. At the end of the article i see a mention its best to encrypt the jwt token for added security so i wen searching for a way to do that as well. I ran into this article and it gives examples of how to encrypt the jwt token with RSA private/public keys.
THIS is where im getting stuck. After i have successfully signed up using the /signup route, i can then use the /login route to get my token. So im assuming this is where i use my private key to encrypt the token before sending it back to the user?
**Made repo public for testing - you will need only to provide a mongoDB connection string in app.js
Im stuck at the encrypt/decrypt portion of the process, any help appreciated.
router.post("/login", async (req, res, next) => {
passport.authenticate("token", async (err, user, info) => {
try {
if (err || !user) {
const error = new Error("An Error occurred");
return next(error);
}
req.login(user, { session: false }, async error => {
if (error) return next(error);
//We don't want to store the sensitive information such as the
//user password in the token so we pick only the email and id
const body = { _id: user._id, email: user.email };
//Sign the JWT token and populate the payload with the user email and id
const token = jwt.sign({ user: body }, PRIV_KEY, { algorithm: 'RS256' });
//Send back the token to the user
return res.json({ token });
});
} catch (error) {
return next(error);
}
})(req, res, next);
});
And then when making calls to the "secure" routes i need to decrypt the token and verify it against the public key?
router.get("/profile", (req, res, next) => {
//We'll just send back the user details and the token
jwt.verify(req.query.token, PUB_KEY, { algorithms: ['RS256'] }, function(err, decoded) {
if (err.name === "TokenExpiredError") {
console.log("Whoops, your token has expired!");
}
if (err.name === "JsonWebTokenError") {
console.log("That JWT is malformed!", err); <------ GET ERROR HERE
}
if (err === null) {
console.log("Your JWT was successfully validated!");
}
// Both should be the same
console.log(decoded);
res.json({
message: "You made it to the secure route",
user: req.user
});
});
});
I don’t have the time to reproduce this. Your login part seems correct. However, you should try to setup protected routes like this, copied and tailored to your needs from your first article:
Setting up middleware to handle jwt decryption, make sure to require it in your app.js or wherever you need to, if you set it up in a separate file. This can be used as a middleware later on in your controllers:
const JWTstrategy = require('passport-jwt').Strategy;
//We use this to extract the JWT sent by the user
const ExtractJWT = require('passport-jwt').ExtractJwt;
//This verifies that the token sent by the user is valid
passport.use(new JWTstrategy({
//secret we used to sign our JWT
secretOrKey : PUB_KEY,
algorithms: ['HS256'],
//we expect the user to send the token as a query parameter with the name 'token'
jwtFromRequest : ExtractJWT.fromUrlQueryParameter('token')
}, async (token, done) => {
try {
//Pass the user details to the next middleware
return done(null, token.user);
} catch (error) {
done(error);
}
}));
Setup protected route, note that you don’t need to manually call jwt.verify, middleware handles it and populates req.user:
const express = require('express');
const router = express.Router();
//Let's say the route below is very sensitive and we want only authorized users to have access
//Displays information tailored according to the logged in user
router.get('/profile', passport.authenticate('jwt', { session: false }), (req, res, next) => {
//We'll just send back the user details and the token
res.json({
message : 'You made it to the secure route',
user : req.user,
token : req.query.token
})
});
module.exports = router;
**Update based on your comment:
I cloned your repo and it is working for me, although I changed some things:
I added
app.use(bodyParser.json()); to app.js so that I could send the request bodies as json - this is not necessary if you prefer urlencoded
the problem is that secureRoute that you export is another router and you try to use it as a controller in app.js:
...
const secureRoute = require('./routes/secure-routes');
...
app.use('/user', passport.authenticate('jwt', { session: false }), secureRoute);`
*note that it will be /user route, if you want /profile please change it in like app.use('/profile', ...)
so instead of
router.get("/profile", (req, res, next) => {
//We'll just send back the user details and the token
res.json({
message: "You made it to the secure route",
user: req.user,
token: req.query.secret_token
});
});
it should be just a controller function:
...
module.exports = (req, res, next) => {
//We'll just send back the user details and the token
res.json({
message: 'You made it to the secure route',
user: req.user,
token: req.query.token // note that you don't use secret_token but token as a name
});
};
the third thing is to not forget to add token to the query params, when you call the API, like http://localhost:3000/user?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7Il9pZCI6IjVlODc2Yjc1YTVlNTk3MTRlOGFjMmI4NyIsImVtYWlsIjoiYUBiLmNvbSJ9LCJpYXQiOjE1ODU5MzMyNjR9.lcLuQeCMRy7Ef9zNkIt_rn4S22t2cm7YLRE7Jgp1Mpw
you should get the response:
{
"message": "You made it to the secure route",
"user": {
"_id": "5e876b75a5e59714e8ac2b87",
"email": "a#b.com"
}
}
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.
My node JS application involves setting a Jason Web Token to the response header at login so that I can use this JWT to verify the user when accessing restriced pages.
My question is how to get this header to persist. Currently I am setting the header at registration and login routes using code such as this:
// Login Handle
router.post("/login", async (req, res, next) => {
const user = await User.findOne({ email: req.body.email });
if (!user) return res.status(400).send("Email or password is incorrect");
const validPassword = await bcrypt.compare(req.body.password, user.password);
if (!validPassword) return res.status(400).send("Invalid email or password.");
const token = user.generateAuthToken();
res.set('x-auth-token', token).redirect('/dashboard');
});
Here is my authentication middleware that I am trying to get working:
function auth(req, res, next) {
const token = req.header('x-auth-token');
if(!token) return res.status(401).send("Access denied. No token provided");
// if the token is valid it will return the decoded payload
try {
const decoded = jwt.verify(token, jwtPrivateKey);
req.user = decoded;
next();
}
catch(ex) {
res.status(400).send("Invalid token.");
}
I can see in the network tab on chrome dev tools that the header is correctly being set with the JWT. However, as soon as the page is refreshed or I visit a different page the header disappears.
Do I need to use res.set('x-auth-token', token).render('foo') on every route? It seems like this wouldn't be a great solution.
Can anyone tell me what I am doing wrong?
After the user has logged in and generated a token, I want to send it automatically in the header or something similar.
So far I managed to generate the token, and check if it exists and if it's valid, it seems to work fine as long as I copy paste it the url as "?token = generated token".
I wasn't able to understand how to send it without writing it myself in the URL of Postman.
I'm using these modules:
Express
Body-parser
Mongoose
JsonWebToken
So I'm curious if it's ok that I choose to generate the token only at the login of if I need to add it in the user's Schema.
I don't want to use Passport for now because I want to understand the basics first.
After searching for a while (the jwt documentation included) I didn't really managed to find something that I can understand and implement.
So here I am, if someone could guide me in the right direction, that'd be great.
Sorry for the bad indentation and thanks in advance.
Here is some code:
jwt-middleware.js
var jwt = require('jsonwebtoken');
var secret = 'mySecret';
module.exports = function (req, res, next) {
var token = req.body.token || req.headers['x-access-token'] || req.query.token;
if(!token) {
return res.status(404).send({error:'No token found'});
} else {
jwt.verify(token, secret, function(err, decode) {
if(err) {
return res.status(500).send({error:'Invalid token'});
} else {
// req.decode = decode;
decode = jwt.decode(token, {complete:true});
//console.log(req.headers);
// req.headers.authorization = token;
// console.log(req.headers.authorization);
console.log(decode.header);
console.log(decode.payload);
next();
}
});
}
}
routes/user.js
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var jwt = require('jsonwebtoken');
var expressJwt = require('express-jwt');
var verifyToken = require('../config/jwt-middleware');
var secret = 'mySecret';
//Import models
var User = require('../models/users');
router.get('/', verifyToken, function (req, res) {
User.find({}, function (err, storedUsers) {
if (err) {
return res.status(500).send({ error: 'No users found' });
} else {
return res.status(200).send(storedUsers);
}
});
});
router.post('/login', function (req, res) {
User.find().lean().exec(function (err, doc) {
for (var i = 0; i < doc.length; i++) {
if (req.body.username == doc[i].username && req.body.password == doc[i].password) {
var token = jwt.sign({username:req.body.username}, secret, {expiresIn:'1h'});
return res.status(200).send('You\'re now logged in ' + 'with the username: ' + doc[i].username + ' and password: ' + doc[i].password + ' \nJSON token: \n' + token);
}
}
return res.status(404).send({ error: 'Invalid username or password: ' + req.body.username });
});
});
Some screenshots:
No token
Login
Manually inserted token
OK, so I'll try and answer your question even though I'm not 100% sure I understand what you're asking. The basic flow of a JWT is that the user logs in, and you issue it. You don't store it because the whole point of a JWT is that there's no overhead on the server for storing it (allowing for a more distributed approach to user management). The exception is if you want to do a logout feature, but that doesn't look like it's one of your requirements.
From the standpoint of responsibilities, you should have a Login function or module which is responsible for verifying a user's credentials and issuing a token. You should have a Verification function or module that validates the token and places the decoded token on the request object for later use (no need to repeatedly decode). And you may (or may not) have an Authorization module that validates that a given user is allowed to perform a given task.
So, from the top. Note that you can let the DB do the query work rather than doing your own loop. I'm also assuming that your User schema will include a verifyPassword method that takes care of comparing salted and hashed passwords.
// login
router.post('/login', function (req, res, next) {
// note I didn't use lean() because I want access to User methods. Performance is less of an issue in my version, b/c the DB is only retrieving one user at most.
User.findOne({ username: req.body.username }).exec(function (err, user) {
if(err) return next(err);
if(!user) return res.status(401).send();
if (user.verifyPassword(req.body.password)) {
// maybe add more info about the user, like display name
var token = jwt.sign({username:user.username}, secret, {expiresIn:'1h'});
return res.status(200).send({message: 'You are now signed in', token: token});
}
}
return res.status(404).send({ error: 'Invalid username or password: ' + req.body.username });
});
});
Now the client will have access to the token more easily, and can send it on further requests.
// verify
module.exports = function (req, res, next) {
// this is fine, though I think the standard is that the token should be sent in the Authorization header with the format Bearer {token}
var token = req.body.token || req.headers['x-access-token'] || req.query.token;
if(!token) {
return next(); // this middleware just is responsible for decoding, other middleware checks authorization
} else {
jwt.verify(token, secret, function(err, decode) {
if(err) {
return next(); // again, a bad token doesn't necessarily mean anything for some application pages, put the logic elsewhere.
} else {
req.user = decode; // this will be { username } based on above
req.token = token; // generally you don't need it but just in case.
next();
}
});
}
}
Ok, so now further middleware will include a req.user that you can use to check if a given user should be allowed to see a resource or not. For example:
function userRequired(req, res, next) {
if (!req.user) return res.status(401).send({message: 'You must be logged in to view this page' });
return next();
}
This scales well to other checks, you could have one for various roles, etc.