How to add refresh token? - node.js

I have the following code i would like to add refresh token. How can I add it?
I am also asking for suggestions on how to improve the quality of the authorization method.
Unfortunately, my attempts always ended with an error or the lack of a specific action.
This is my snippet of code:
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const urlencodedParser = bodyParser.urlencoded({ extended: false });
const mysql = require('mysql2');
const jwt = require('jsonwebtoken');
require('dotenv').config();
const TOKEN_SECRET = process.env.TOKEN_SECRET;
const app = express();
app.use(express.json());
const pathPublic = path.join(__dirname, '../public');
app.use(express.static(pathPublic));
app.post('/login', urlencodedParser, (req, res) => {
res.get(req.body.username + req.body.password);
const users = {
username: req.body.username,
password: req.body.password
};
// Connect to MySql
const db = require('./dbconnect');
// Select user:
const post = [users.username, users.password]
const sql = 'SELECT * FROM users WHERE name = ? AND password = ?';
const query = db.query(sql, post, (err, results) => {
if (err) {
throw err;
} else if (results.length >= 1) {
console.log("Successful login!!");
const token = generateAccessToken({ username: users.username });
res.redirect(`/admin?token=${token}`);
} else if (results.length <= 0) {
console.log("Not find user");
}
})
})
function generateAccessToken(username) {
return jwt.sign(username, TOKEN_SECRET, { expiresIn: '600s' });
}
function authenticateToken(req, res, next) {
token = req.query.token;
if (token == null) return res.sendStatus(401);
jwt.verify(token, TOKEN_SECRET, (err, user) => {
if (err) console.log(err);
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
app.get('/admin', authenticateToken, (req, res) => {
res.send('admin panel');
});

There are multiple ways of implementing refresh token in Nodejs, one way that I use a lot is to generate a new JWT with more expiration time than the access token, for instance 30 minutes for the access and 48 hours for the refresh one, to differentiate those tokens I add an additional payload called type, the value that you put in that property is yours but it can be simple text as ACCESS and REFRESH, then I will modify the authentication middleware to validate token and verify that the type property be equal to ACCESS, if the value of the property is different I will send an unauthorized response, when the token access is invalid, I will use the refresh jwt to generate a new access one, for that I would create another middleware which extracts information from the token and verifys that the type be equal to REFRESH if everything is correct then I will generate a two new token one for access and another for refresh.
This is the basic implementation you can improving it in many ways, for instance to have a blacklist to allow only use the refresh token once, but this is the general idea.
An example can be found here.

Related

Where to store access token and how to keep track of user (using JWT token in Http only cookie)

Trying to understand how to get and then save user in client (using JWT token in Http only cookie), so that I can do conditional rendering. What I'm having difficulty with is how to continously know if the user is logged in or not, without having to send a request to the server each time the user changes/refresh page. (Note: the problem is not how do I get the token in the Http only cookie, I know that this is done through withCredentials: true)
So my problem is how do you get/store the access token so that the client will not have to make a request to the server each time the user does something on the website. For example the Navbar should do conditional renderingen depending on if the user is logged in or not, then I don't want to do "ask the server if the user has a access token, then if not check if user has refresh token, then return a new access token if true else redirect to login page" every single time the user switches page.
Client:
UserContext.js
import { createContext } from "react";
export const UserContext = createContext(null);
App.js
const App = () => {
const [context, setContext] = useState(null);
return (
<div className="App">
<BrowserRouter>
<UserContext.Provider value={{ context, setContext }}>
<Navbar />
<Route path="/" exact component={LandingPage} />
<Route path="/sign-in" exact component={SignIn} />
<Route path="/sign-up" exact component={SignUp} />
<Route path="/profile" exact component={Profile} />
</UserContext.Provider>
</BrowserRouter>
</div>
);
};
export default App;
Profile.js
import { GetUser } from "../api/AuthenticateUser";
const Profile = () => {
const { context, setContext } = useContext(UserContext);
return (
<div>
{context}
<button onClick={() => GetUser()}>Change context</button>
</div>
);
};
export default Profile;
AuthenticateUser.js
import axios from "axios";
export const GetUser = () => {
try {
axios
.get("http://localhost:4000/get-user", {
withCredentials: true,
})
.then((response) => {
console.log(response);
});
} catch (e) {
console.log(`Axios request failed: ${e}`);
}
};
Server:
AuthenticateUser.js
const express = require("express");
const app = express();
require("dotenv").config();
const cors = require("cors");
const mysql = require("mysql");
const jwt = require("jsonwebtoken");
const cookieParser = require("cookie-parser");
// hashing algorithm
const bcrypt = require("bcrypt");
const salt = 10;
// app objects instantiated on creation of the express server
app.use(
cors({
origin: ["http://localhost:3000"],
methods: ["GET", "POST"],
credentials: true,
})
);
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
const db = mysql.createPool({
host: "localhost",
user: "root",
password: "password",
database: "mysql_db",
});
//create access token
const createAccessToken = (user) => {
// create new JWT access token
const accessToken = jwt.sign(
{ id: user.id, email: user.email },
process.env.ACCESS_TOKEN_SECRET,
{
expiresIn: "1h",
}
);
return accessToken;
};
//create refresh token
const createRefreshToken = (user) => {
// create new JWT access token
const refreshToken = jwt.sign(
{ id: user.id, email: user.email },
process.env.REFRESH_TOKEN_SECRET,
{
expiresIn: "1m",
}
);
return refreshToken;
};
// verify if user has a valid token, when user wants to access resources
const authenticateAccessToken = (req, res, next) => {
//check if user has access token
const accessToken = req.cookies["access-token"];
// if access token does not exist
if (!accessToken) {
return res.sendStatus(401);
}
// check if access token is valid
// use verify function to check if token is valid
jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
return next();
});
};
app.post("/token", (req, res) => {
const refreshToken = req.cookies["refresh-token"];
// check if refresh token exist
if (!refreshToken) return res.sendStatus(401);
// verify refresh token
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(401);
// check for refresh token in database and identify potential user
sqlFindUser = "SELECT * FROM user_db WHERE refresh_token = ?";
db.query(sqlFindUser, [refreshToken], (err, user) => {
// if no user found
if (user.length === 0) return res.sendStatus(401);
const accessToken = createAccessToken(user[0]);
res.cookie("access-token", accessToken, {
maxAge: 10000*60, //1h
httpOnly: true,
});
res.send(user[0]);
});
});
});
/**
* Log out functionality which deletes all cookies containing tokens and deletes refresh token from database
*/
app.delete("/logout", (req, res) => {
const refreshToken = req.cookies["refresh-token"];
// delete refresh token from database
const sqlRemoveRefreshToken =
"UPDATE user_db SET refresh_token = NULL WHERE refresh_token = ?";
db.query(sqlRemoveRefreshToken, [refreshToken], (err, result) => {
if (err) return res.sendStatus(401);
// delete all cookies
res.clearCookie("access-token");
res.clearCookie("refresh-token");
res.end();
});
});
// handle user sign up
app.post("/sign-up", (req, res) => {
//request information from frontend
const { first_name, last_name, email, password } = req.body;
// hash using bcrypt
bcrypt.hash(password, salt, (err, hash) => {
if (err) {
res.send({ err: err });
}
// insert into backend with hashed password
const sqlInsert =
"INSERT INTO user_db (first_name, last_name, email, password) VALUES (?,?,?,?)";
db.query(sqlInsert, [first_name, last_name, email, hash], (err, result) => {
res.send(err);
});
});
});
/*
* Handel user login
*/
app.post("/sign-in", (req, res) => {
const { email, password } = req.body;
sqlSelectAllUsers = "SELECT * FROM user_db WHERE email = ?";
db.query(sqlSelectAllUsers, [email], (err, user) => {
if (err) {
res.send({ err: err });
}
if (user && user.length > 0) {
// given the email check if the password is correct
bcrypt.compare(password, user[0].password, (err, compareUser) => {
if (compareUser) {
//req.session.email = user;
// create access token
const accessToken = createAccessToken(user[0]);
const refreshToken = createRefreshToken(user[0]);
// create cookie and store it in users browser
res.cookie("access-token", accessToken, {
maxAge: 10000*60, //1h
httpOnly: true,
});
res.cookie("refresh-token", refreshToken, {
maxAge: 2.63e9, // approx 1 month
httpOnly: true,
});
// update refresh token in database
const sqlUpdateToken =
"UPDATE user_db SET refresh_token = ? WHERE email = ?";
db.query(
sqlUpdateToken,
[refreshToken, user[0].email],
(err, result) => {
if (err) {
res.send(err);
}
res.sendStatus(200);
}
);
} else {
res.send({ message: "Wrong email or password" });
}
});
} else {
res.send({ message: "Wrong email or password" });
}
});
});
app.get("/get-user", (req, res) => {
const accessToken = req.cookies["acceess-token"];
const refreshToken = req.cookies["refresh-token"];
//if (!accessToken && !refreshToken) res.sendStatus(401);
// get user from database using refresh token
// check for refresh token in database and identify potential user
sqlFindUser = "SELECT * FROM user_db WHERE refresh_token = ?";
db.query(sqlFindUser, [refreshToken], (err, user) => {
console.log(user);
return res.json(user);
});
});
app.listen(4000, () => {
console.log("running on port 4000");
});
I began experimenting with useContext as you can see in the client code above. My initial idea was to use useEffect in the App component where I make a call to the function GetUser() which makes a request to "/get-user" which will user the refreshToken to find the user (don't know if it is bad practice to use refreshToken to find user in db, maybe I should store access token in db as well and use it to find user in db instead?) and then save things like id, first name, last name and email so that it may be displayed in the navbar or any other component if necessary.
However, I don't know if this is the right thing to do as I have heard a lot about using localStorge, memory or sessionStorage is better for keeping the JWT access token in, while you should keep the refresh token in the server and save it in the mySQL database I have created, only to be used once the user has lost their access token. How should I get access to my access token and how do I keep track of the user logged in? Do I really need to do a request to the server each time the user switches page or refresh page?
Also I have a question about when I should be calling "/token" in the server to create new access tokens. Should I always try to use the access token to do things that require authentication and if it for example returns null at some point then I make request to "/token" and after that repeat what the user was trying to do?
Do I really need to do a request to the server each time the user switches page or refresh page?
That is the safest way. If you want to keep with the current security best practices for SPAs, then using http-only, secure, same-site cookies is the best option. Refreshes won't happen that often on your page, so it shouldn't be a problem.
My initial idea was to use useEffect in the App component where I make a call to the function GetUser() which makes a request to "/get-user" which will user the refreshToken to find the user
What I would do is to first verify the access token, if it's valid then take the userId out of the access token (if you don't have it there you can easily add it as you're creating the tokens manually) and read the user data from the database. If the access token is invalid then return an error to the website and let the user use the refresh token to get a new access token. So I wouldn't mix responsibilities here - I wouldn't use refresh token to get information about the logged in user.
Also I have a question about when I should be calling "/token" in the server to create new access tokens. Should I always try to use the access token to do things that require authentication and if it for example returns null at some point then I make request to "/token" and after that repeat what the user was trying to do?
Yes, that's how it usually is implemented. You make a call with the access token to a protected endpoint. It would be best if the endpoint returned 401 response if the token is expired or invalid. Then your app knows that it should use the refresh token to get a new access token. Once you have a new access token you try to make the call to the protected endpoint again. If you don't manage to get a new access token (e.g. because the refresh token has expired), then you ask the user to log in again.

How to solve JsonWebTokenError "invalid signature" after assigning some infos to the generated token?

Have a problem when trying to verify the token (it was working fine before i added some data to it before generating it) .. but now it does not seem to be working !
This is how i generate the token when user send a POST request (login)
require('dotenv')
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs')
const Role = require('../models/Role');
const Section = require('../models/Section');
const User = require('../models/User');
// Login !
router.post('/', async (req, res) => {
let sections_fetched = [];
// Validate data
// Check username
const user = await User.findOne({username: req.body.username });
if(!user) return res.status(400).send('Wrong user login credentials !');
// Check password
const is_pass_valid = await bcrypt.compare(req.body.password , user.password);
if (!is_pass_valid) return res.status(400).send('Wrong user login credentials !');
// Get role Object:
const _role = await Role.findOne({_id:user.role , is_deleted:false});
if (!_role) res.json("Failed fetching role !");
// loop through sections
for (let index = 0; index < _role.sections.length; index++) {
const tmpRole = await Section.find({_id: _role.sections[index], is_deleted:false});
sections_fetched.push({access:tmpRole[0].access , name:tmpRole[0].name});
}
// create jwt token
const token = jwt.sign({username:user.username, role:{name:_role.name, sections:sections_fetched}}, 'secret', {expiresIn : '24h'}, process.env.JWT_TOKEN_SECRET);
res.json({token:token});
});
this is my JWT verification middleare :
require('dotenv')
const jwt = require('jsonwebtoken');
module.exports = function (req, res, next) {
const token = req.header('auth-token');
if (!token) return res.status(401).send('Access Denied !');
console.log(process.env.JWT_TOKEN_SECRET);
console.log(token);
try
{
const verified = jwt.verify(token, process.env.JWT_TOKEN_SECRET);
req.user = verified;
next();
}
catch (error)
{
res.status(400).send('Invalid token !');
}
}
and this is a simple example of listing users (using the JWT verification middleware ! ) :
const verifyToken = require('../middlewares/verifyToken'); // my jwt middleware to verify !
// Listing All users
router.get('/', verifyToken, async (req, res) =>
{
try
{
const users = await User.find({is_deleted:false});
res.json(users);
}
catch (error)
{
console.log("err ->\n"+error);
res.json({message: error});
}
});
what is 'secret in the below line? seems you are adding a secret key twice, replace the hardcoded word 'secret' with token from env
const token = jwt.sign({username:user.username, role:{name:_role.name, sections:sections_fetched}}, 'secret', {expiresIn : '24h'}, process.env.JWT_TOKEN_SECRET);
send a bearer token and your middleware should like this
require('dotenv')
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1]; // Authorization: 'Bearer TOKEN'
if (!token) {
throw new Error('Authentication failed!');
}
const verified = jwt.verify(token, process.env.JWT_TOKEN_SECRET);
req.user = verified;
next();
} catch (err) {
res.status(400).send('Invalid token !');
}
};
Please make sure then when generating the token you pass a valid algorithm. I stored the algorithm in an environment variable but used none which is not a valid algorithm. Thus even though the token got created, I couldn't verify it using the same secret. Spent hours trying to fix this. I hope this was helpful :D
sign(payload, JWT_SECRET, {
algorithm: JWT_ALGORITHM,
expiresIn: '1d'
});
You described that you have changed the contents.
A signature represents the integrity of the contents, to ensure the content that was signed has not been modified, and you are modifying the contents making the signature invalid. This error you have is accurate for what you have done.
You must create a new signature when the signed contents are changed.
For Sign:
jwt.sign(
{
username:user.username,
role:{
name: _role.name,
sections: sections_fetched
}
}, 'secret', {
expiresIn : '24h'
}, process.env.JWT_TOKEN_SECRET,
function(err,res) {
if (err) {
callback(res);
} else {
callback(res);
}
}
);
To Verify:
jwt.verify(req,'SecretKey', function (err, res) {
if (err) {
callback(res);
} else {
callback(res);
}
});
Hlo
this error occur when secretkey does't match with the key thats provided during when you verifing token
you can check you token on jwt website thats help you to get error main reason

Issue with setting Authorization header using Express JS implemenitng Using Cognito User Pool Authentication

I am implementing authentication using Cognito User Pool. I am only using NodeJS + Express JS, so whole coding is on server side and client side I just render html file using templates.
I am using below code to autheticate the user passing username and password.
const cognitoProvider = new AWS.CognitoIdentityServiceProvider();
cognitoProvider.adminInitiateAuth(params, function (err, result) {})
If authentication is sucessfull, result object has the accesstoken, tokentype and refresh token. Now I am not clear on how to set Authorzation header with this result which has the token. After this code I redirect user to the home page where I welcome the user. In subsequent request I want to retrieve token from header to check if user has logged in. But issue is I am not able to set the authorization header.
Below is the code:
cognitoProvider.adminInitiateAuth(params, function (err, result) {
If (err == null)
{
res.set('Authorization', result.AuthenticationResult); // Is this correct ? Even If i use this in next request I cannot retrieve Authorization header using req.header['Authorization']. I am not sure if I am coding this correctly.
//Below is my next line of code
res.redirect('/home'); // I redirect user to home page. I am not using render here as I was to avoid the post back message in case user tries to refresh the page, so I use redirect.
}
})
I have spend hours but not able to make this work. May be I am missing some basic concept here. Code Below of /users where I pass username & Password:
var express = require('express');
var router = express.Router();
const AWS = require('aws-sdk');
AWS.config.region = "REGION COMES HERE";
const cognitoProvider = new AWS.CognitoIdentityServiceProvider();
router.post('/', function (req, res, next) {
const username = req.body['InputEmail'];
const password = req.body['InputPassword'];
const params = {
"AuthFlow": "ADMIN_USER_PASSWORD_AUTH",
"AuthParameters": {
"USERNAME": username,
"PASSWORD": password
},
"UserPoolId": "USERPOOLID",
"ClientId": "CLIENTID"
}
cognitoProvider.adminInitiateAuth(params, function (err, result) {
if (err) {
var errormessage;
switch (err.code) {
case 'NotAuthorizedException':
errormessage = err.message
break;
default:
errormessage = "Issues encountered. Try Again";
}
res.render('index', { title: 'Express', message: errormessage });
return;
}
if (result.ChallengeName) {
switch (result.ChallengeName) {
case 'NEW_PASSWORD_REQUIRED':
res.render('firsttimelogin', { title: 'Express', username:username, message: '' });
break;
default:
res.render('index', { title: 'Express', message: '' });
}
} else {
res.set('Authorization', result.AuthenticationResult);
res.redirect('/home');
}
});
});
module.exports = router;
Code of /Home
var express = require('express');
var router = express.Router();
router.use('/', function(req, res, next) {
var token = req.header['Authorization'];
if (token) {
res.render('home', { title: 'Express'});
}
else{
res.redirect('/');
}
});
module.exports = router;

How to get session data for API requests?

I want to get authorized user data. But instead I get the data of a completely different user. How to write a function getProfile to display the data of the current user?
controllers/auth.js:
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const db = require('../config/db.config.js')
const User = db.user
module.exports.login = async function(req, res) {
const candidate = await User.findOne({
where: {
username: req.body.username
}
})
if (candidate) {
const passwordResult = bcrypt.compareSync(req.body.password, candidate.password)
if (passwordResult) {
const token = jwt.sign({
username: candidate.username,
userId: candidate._id
}, process.env.SECRET_OR_KEY, {expiresIn: 60 * 60})
res.status(200).json({
token: `Bearer ${token}`
})
} else {
res.status(401).json({
message: 'Passwords do not match. Try again.'
})
}
} else {
res.status(404).json({
message: 'User with this login was not found.'
})
}
}
module.exports.getProfile = async function(req, res) {
try {
const user = await User.findOne({id: req.body.id})
res.status(200).json(user)
} catch(e) {
errorHandler(res, e)
}
}
routes/auth.js:
const express = require('express')
const router = express.Router()
const controller = require('../controllers/auth')
const passport = require('passport')
router.post('/login', controller.login)
router.get('/profile', passport.authenticate('jwt', {session: false}), controller.getProfile)
module.exports = router
You should attach a signed token in each HTTP req from client, either by custom HTTP header or set in cookie. This token is sent only after successful login which contains user's id and other info.
After you start receiving that token you can validate it (checking for expiry or some manual change) using a middleware and that token data will be the actual user data belongs to the user loggedin.
Now, you read that header/cookie to get requester user's info and you can then send their respective data only.
Let's say if client is sending you token info in header called tkn. Your token validation can be as follows:
var jwt = require('jsonwebtoken');
const SECRET = 'whatulike';
function verifyToken(req, res, next) {
var token = req.headers.tkn || "";
if (!token.length)
return unauthorised(res, 'Token absent');
jwt.verify(token, SECRET, function(err, decoded) {
if (err)
return unauthorised(res, 'Failed to authenticate token.');
req.tkn = decoded.id;
next();
});
}
function unauthorised(res, msg){
const sc = 401;
logger.warn(`${sc} - Unauthorised request ${res.req.originalUrl}`);
res.status(sc).send({msg});
}
module.exports.verifyToken = verifyToken;
And at handler side you can read tkn data like:
module.exports.getProfile = async function(req, res) {
try {
const user = await User.findOne({id: req.tkn.userId})
res.status(200).json(user)
} catch(e) {
errorHandler(res, e)
}
}

Node.js: Send new request with token(JWT) in URL after logging in

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.

Resources