I'm having trouble figuring out why postman keeps returning empty curly braces with 500 internal server error whenever in use Bearers token in authorization for POST 'http://localhost:3000/api/v1/products' isAdmin true. This is my jwt.js file
const { expressjwt: expressJwt } = require('express-jwt');
function authJwt() {
const secret = process.env.secret
const api = process.env.API_URL
return expressJwt({
secret,
algorithms: ['HS256'],
isRevoked: isRevoked
}).unless({
path: [
{ url: /\/api\/v1\/products(.*)/, methods: ['GET', 'OPTIONS'] },
{ url: /\/api\/v1\/categories(.*)/, methods: ['GET', 'OPTIONS'] },
`${api}/users/login`,
`${api}/users/register`,
]
})
}
async function isRevoked(req, payload, done) {
if(!payload.isAdmin) {
done(null, true);
}
done();
};
module.exports = authJwt
Upon introducing this lines of codes, Postman returns authorization error even with the Bearers token. My good developers, come through for me here. I've been stuck for a whole week.
My aim is the API should post the new product using isAdmin [true] bearer's token.
async function isRevoked(req, token) {
if(!token.payload.isAdmin) {
return true
}
return undefined;
}
The error-handler file
function errorHandler(err, req, res, next) {
if (err.name === 'UnauthorizedError') {
return res.status(401).json({message: 'The user is not authorized'})
}
if (err.name === 'ValidationError') {
return res.status(401).json({message: err})
}
return res.status(500).json(err);
}
module.exports = errorHandler
In your app.js use authJwt() instead of just authJwt
use following
async function isRevoked(req, token){
if(!token.payload.isAdmin) {
return true;
}
}
and comment out
async function isRevoked(req, payload, done) {
if(!payload.isAdmin) {
done(null, true);
}
done();
};
and comment out
async function isRevoked(req, token) {
if(!token.payload.isAdmin) {
return true
}
return undefined;
}
in your jwt.js
Increase your token time from 1d to more days say 10d
Get your fresh Token
Related
How to make user redirect after authentication based on user.role ?
I'm getting the following error: UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
const jwt = require('jsonwebtoken')
const { COOKIE_NAME, SECRET } = require('../config/config')
module.exports = function() {
return (req, res, next) => {
let token = req.cookies[COOKIE_NAME]
if(token) {
jwt.verify(token, SECRET, function(err, decoded){
if (err) {
res.clearCookie(COOKIE_NAME)
} else {
if(decoded.user.role === 'admin') {
res.redirect('http://localhost:4000')
}
req.user = decoded;
}
})
}
next();
}
}
Login Fetch:
fetch(`${API}/auth/login`,{
method: 'POST',
credentials: 'include',
withCredentials: true,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(user)
})
.then((response) => {
if(response.status === 302) {
window.location = 'http://localhost:4000'
}
else if(response.status === 200) {
onSuccess()
setTimeout(() => {
window.location = '/'
}, 1000)
} else if (response.status === 401) {
onError()
}
})
.catch((error) => {
console.log(error)
})
}
Here is my authService:
const jwt = require('jsonwebtoken')
const User = require('../models/User');
const bcrypt = require('bcrypt')
const { SALT_ROUNDS, SECRET } = require('../config/config');
const register = async ({name, username, email, password, cart}) => {
let salt = await bcrypt.genSalt(SALT_ROUNDS);
let hash = await bcrypt.hash(password, salt);
const user = new User({
name,
username,
email,
password: hash,
cart
});
return await user.save()
}
const login = async ({email, password}) => {
let user = await User.findOne({email})
if (!user) {
throw {message: 'User not found!'}
}
let isMatch = await bcrypt.compare(password, user.password)
if (!isMatch) {
throw {message: 'Password does not match!'}
}
let token = jwt.sign({user}, SECRET)
return token;
}
And my authController:
const { Router } = require('express');
const authService = require('../services/authService');
const { COOKIE_NAME } = require('../config/config');
const router = Router();
router.post('/login', async (req, res) => {
const {email, password} = req.body
try {
let token = await authService.login({email, password})
res.cookie(COOKIE_NAME, token)
res.status(200).json(token)
} catch (error) {
res.status(401).json({ error: error })
}
})
Here is my server if this will help:
app.use((req, res, next) => {
const allowedOrigins = ['http://localhost:3000', 'http://localhost:4000'];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', true)
}
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
Since you're using jwt.verify with a callback, it is being executed asynchronously. Due to this, immediately after calling verify but before getting the decoded token, your next() function is called which passes the control to the next middleware (which probably would be synchronous) which then returns the request.
The flow of events would be something like this:
if(token) { ... starts
jwt.verify(token, ... is called asynchronously. It registers the callback function(err, decoded) { ... but doesn't execute it yet.
You exit the if(token) { ... } block and call next().
The next middleware in line starts executing and probably returns the request if it is the last middleware in chain. So the client has already been sent the response by this time.
jwt.verify(token ... succeeds and calls your registered callback.
It sees that there is no error at line if (err) ... so it moves to the else block.
It decodes the user role and tries to redirect (which internally would try to insert a header on the response). But this fails because the user was already sent the response (and hence your error message).
So the simple solution to this is to not call next() UNTIL jwt verifies and decodes your token and you know the role. In the code below, I've moved the next() function call a few lines upwards.
const jwt = require('jsonwebtoken')
const { COOKIE_NAME, SECRET } = require('../config/config')
module.exports = function() {
return (req, res, next) => {
let token = req.cookies[COOKIE_NAME]
if(token) {
jwt.verify(token, SECRET, function(err, decoded){
if (err) {
res.clearCookie(COOKIE_NAME)
} else {
if(decoded.user.role === 'admin') {
res.redirect('http://localhost:4000')
}
req.user = decoded;
}
next();
})
}
}
}
I'm building a small application where a user logs in and gets redirected to /profile. Right now, I fetch the JWT from localstorage and check it via the server. The server then sends it back to the client to tell me if it's a valid session or not.
jQuery/Client:
UserController.initPanel = () => {
if (session === null) {
window.location = "/";
} else {
UserController.requestAuth(session);
}
};
UserController.requestAuth = (sessionToken) => {
var settings = {
"url": "/api/auth",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": `Bearer ${sessionToken}`,
},
"data": ""
}
$.ajax(settings).done(function (response) {
console.log(response);
});
};
Node.js/auth.js route:
router.post("/", (req, res) => {
const authHeader = req.headers.authorization;
if (typeof authHeader !== 'undefined') {
const bearerToken = authHeader.split(' ')[1];
verifyToken(bearerToken, (authData) => {
tokenRequest(authData, (authResponse) => {
handleAuthResponse(req, res, authResponse);
})
});
}
});
const handleAuthResponse = (req, res, authResponse) => {
console.log(authResponse);
return res.status(200).json(authResponse);
}
const verifyToken = (token, cb) => {
jwt.verify(token, 'mysecret', (err, authData) => {
if (err) {
res.sendStatus(403)
} else {
cb(authData);
}
});
}
const tokenRequest = (authHeader, cb) => {
//console.log(authHeader);
var config = {
headers: {'Authorization': `bearer ${authHeader.token}`}
};
axios.get('https://myapi.dev/api/session/me', config)
.then((res) => {
if (res.data.error) {
return response.data
} else {
cb(res.data);
}
})
.catch((error) => {
console.log('error', error);
});
}
I feel like this isn't the correct way to do it. I'm rendering templates with ejs:
router.get("/profile", (req, res) => {
const settings = {
title: "Profile",
revslider: false
};
res.render("profile/profile", { settings: settings } );
});
And if for some reason, JS is disabled, /profile is still accessible. Which isn't that big of a problem, it just feels wrong.
So, is it possible to access /profile route, securely checking for authorization server-side first, before rendering?
Also, auth.js returns some user data I could use in the .ejs template. So that's another reason I'd like to try check auth before rendering as well.
EDIT:
Auth middleware, which I didn't use because I wasn't sure how to pass in the token?
module.exports = (req, res, next) => {
try {
const decoded = jwt.verify(req.body.token, 'mysecret');
req.token = decoded;
} catch (error) {
console.log(error);
return res.status(401).json({
message: 'Auth Failed'
});
}
next();
}
Very basic middleware implementation below which leverages express and express-session.
We basically create a simple function to check req.session exists, within that object, you could have something that identifies whether the user has actually authenticated. I'd recommend you add your own logic here to further check the user status.
const authCheckMiddleware = (req, res, next) => {
// Perform auth checking logic here, which you can attach
// to any route.
if(!req.session) {
return res.redirect('/');
}
next();
};
The authCheckMiddleware can be attached to any route, with app.use or router.use. The req object is passed to all middleware.
// Use the authCheckMiddleware function
router.use('/profile', authCheckMiddleware);
Your router.get('/profile') call is now protected by the above middleware.
// Route protected by above auth check middleware
router.get("/profile", (req, res) => {
const settings = {
title: "Profile",
revslider: false
};
res.render("profile/profile", { settings: settings } );
});
I want to store information in the database when user is authenticated. The information is coming form the client in the request. The following code throws error, saying req is not defined.
Controller:
exports.verifySession = async function(req, res, next) {
let responses = [];
passport.authenticate('jwt', async (error, result) => {
if (error) {
email.sendError(res, error);
} else if (result === false) {
responses.push(new CustomResponse(1).get());
return res.status(422).json({ data: { errors: responses } });
}
if (result.SessionToken) {
return res.status(200).json('valid');
} else {
return res.status(401).json();
}
})(req, res, next);
};
And passport.js:
passport.use(
new JWTstrategy(
{
// We expect the user to send the token as a query paramater with the name 'token'
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
// Secret we used to sign our JWT
secretOrKey: config.jwtkey
},
async (token, done) => {
console.log(req.body);
try {
const user = new User();
user.UserID = token.user.UserID;
user.SessionToken = token.user.SessionToken;
user.SessionDate = token.user.SessionDate;
user.ProviderID = token.user.ProviderID;
// Verify session token
await user.verifySessionToken(user, async (error, result) => {
if (error) {
return done(error);
} else if (result.returnValue === 0) {
return done(null, token.user);
} else if (result.returnValue !== 0) {
return done(null, result);
}
});
} catch (error) {
done(error);
}
}
)
);
You can use passReqToCallback feature of passport to pass your request body to passport.
From passport.js official docs :
The JWT authentication strategy is constructed as follows:
new JwtStrategy(options, verify)
options is an object literal
containing options to control how the token is extracted from the
request or verified.
...
...
passReqToCallback: If true the request will be passed to the verify
callback. i.e. verify(request, jwt_payload, done_callback).
You can try this:
passport.use(new JWTstrategy({
// We expect the user to send the token as a query paramater with the name 'token'
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
// Secret we used to sign our JWT
secretOrKey: config.jwtkey,
//this will help you to pass request body to passport
passReqToCallback: true
}, async (req, token,done) => {
//req becomes the first parameter
// now you can access req.body here
})
Note: req becomes the first parameter of callback function instead of token, when you use passReqToCallback
I am persistently getting 'unauthorized' error while authenticating using a JWT. Below is my controller code:
exports.loginPost = async (req, res) => {
winston.info('Calling loginPost()...');
passport.authenticate('local', { session: false }, (err, user, info) => {
if (err) {
return utils.errorHandler(res, err);
} else if (!user) {
return utils.errorHandler(res, {
statusCode: 403,
message: 'Incorrect username or password.'
});
}
const token = jwt.sign(user, sharedSecret, { expiresIn: '24h' });
//req.user = user;
return res.json({ user, token });
// req.login(user, { session: false }, (err) => {
// if (err) {
// res.send(err);
// }
// // generate a signed json web token with the contents of user object and return it in the response
// const token = jwt.sign(user, sharedSecret, { expiresIn: '24h' });
// //req.user = user;
// return res.json({ user, token });
// });
})(req, res);
};
exports.isUserLoggedIn = async (req, res) => {
let login = {"message": "all good !"}
console.log(req)
return res.status(200).json(login);
//return res.status(200).json(req.user);
};
and passport.js strategy script is as follows:
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
}, async function (username, password, cb) {
//this one is typically a DB call. Assume that the returned user object is pre-formatted and ready for storing in JWT
try {
let user = await userService.getUserWithPassword(username, password);
console.log("passport.js: ",user);
if (!user || user.walletKey !== password) {
throw { statusCode: 403, message: 'Incorrect username or password.' };
}
// // purge password field
// delete user.currentPassword;
return cb(null, user, { message: 'Logged In Successfully' });
} catch (err) {
cb(err);
}
}));
passport.use(new JWTStrategy({
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey: sharedSecret,
passReqToCallback: true
},
async function (req, jwtPayload, cb) {
// Return user object from the JWTPayload
try {
let user = await userService.getUserWithPassword(jwtPayload.walletName, jwtPayload.walletKey);
console.log("passport.js: ",user);
req.user = user
return cb(null, user); //jwtPayload
} catch(err){
return cb(err,false);
}
}
));
I am able to generate token successfully, however, on calling isUserLoggedIn method using Bearer Token, it's prompting me unauthorized error. I am not making an traditional db call to login, instead I am just creating account in a Hyperledger-Indy pool nodes. Using swagger express middleware on a Node.js app.
Adding isUserLoggedIn method script below:
exports.isUserLoggedIn = async (req, res) => {
//let login = {"message": "all good !"}
console.log(req)
return res.status(200).json(req.user);
//return res.status(200).json(req.user);
};
I have an handler
public ensureAuthenticated(req: express.Request, res: express.Response, next: Function) {
// check header or url parameters or post parameters for token
var token = req.body.token || req.param('token') || req.headers['x-access-token'];
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, config.secret, function(err, decoded) {
if (err) {
return res.status(404).json({ success: false, message: 'Failed to authenticate token.' });
} else {
// if everything is good, save to request for use in other routes
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
}
And here the route
app.post('/api/article/create', AuthenticationHelper.ensureAuthenticated, this.create);
In unit test, how can I mock the ensureAuthenticated to make sure it is authenticated.
sinon.stub(AuthenticationHelper, 'ensureAuthenticated').returns(true);
I will give you an example where I test it without using sinon.
This is my authentication-helper.js:
'use strict';
module.exports = function(jwt, config) {
return {
ensureAuthenticated: function (req, res, next) {
var token = req.body.token ||
req.param('token') ||
req.headers['x-access-token'];
if (token) {
jwt.verify(
token,
config.secret,
function(err, decoded) {
if (err) {
res
.status(404)
.json({
success: false,
message: 'Failed to auth.'
});
} else {
next();
}
}
);
} else {
res
.status(403)
.send({
success: false,
message: 'No token provided.'
});
}
}
};
}
And this is my test file:
'use strict';
var jwt = {};
jwt.verify = function (token, secret, fn) {
fn(null, 'something');
};
var config = {};
config.secret = 'shh';
var req = {};
req.body = {};
req.body.token = 'mytoken';
var res = {};
var AuthenticationHelper = require('./authentication-helper.js')(jwt, config);
describe('Test Express Middleware', function() {
it('should call next on middlware', function(done) {
var next = function () {
console.log('next was called');
done();
};
AuthenticationHelper.ensureAuthenticated(req, res, next);
});
});