I am developing a android aplication with nodejs and postgreSQL, at the moment i just have the login and the register.
When i do a login and everything is fine the server send me a token, that token is stored on the device SharedPreference, now my confusion is, do i need to decode this token on every request, or do i need to do it just 1 time?
in this tutorial at the end, he decodes on every route the token, but i don't need to do that when i do for example a request to register.
What is the best way to implement this?
here is my server code:
//****************************************************Begin of login request **********************************/
router.post('/login', function (req, res, next) {
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Login request body is empty" });
}
if (!req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for login" });
}
// search a user to login
User.findOne({ where: { username: req.body.username } }) // searching a user with the same username and password sended in req.body
.then(function (user) {
if (user && user.validPassword(req.body.password)) {
//return res.status(200).json({ message: "loged in!" }); // username and password match
var payload = { user: user };
// create a token
var token = jwt.sign(payload, 'superSecret', {
expiresIn: 60 * 60 * 24
});
// return the information including token as JSON
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
else {
return res.status(401).json({ message: "Unauthorized" }); // if there is no user with specific fields send
}
}).catch(function (err) {
console.error(err.stack)
return res.status(500).json({ message: "server issues when trying to login!" }); // server problems
});
});
//****************************************************End of Login request **********************************/
//****************************************************Begin of register request******************************/
router.post('/register', function (req, res, next) {
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Register request body is empty" });
}
if (!req.body.email || !req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for registration" });
}
var password = User.generateHash(req.body.password);
User.create({
username: req.body.username,
email: req.body.email,
password: password
}).then(function () {
return res.status(200).json({ message: "user created" });
}).catch(function (err) {
return res.status(400).send({ message: err.message }); //
}).catch(function (err) {
return res.status(400).json({ message: "issues trying to connect to database" });
})
});
//****************************************************End of register request **********************************/
module.exports = router;
If you don't want to use JWT token check for all routes, you can skip those routes.
const url = require('url');
apiRoutes.use((req, res, next) => {
const path = url.parse(req.url).pathname;
console.log(path);
//No JWT token check
if (/^\/register/.test(path)) {
return next();
}
return jwtTokenValidate();
});
function jwtTokenValidate() {
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
}
Related
I'm trying to learn JWT authentication in express and one thing that I'm came across this code from Github
that this guy has initialised an middleware function to authenticate and check expiry of access token as per below:
app.post("/protected", auth, (req, res) => {
return res.json({ message: "Protected content!" });
})
async function auth(req, res, next) {
let token = req.headers["authorization"];
token = token.split(" ")[1]; //Access token
jwt.verify(token, "access", async (err, user) => {
if (user) {
req.user = user;
next();
} else if (err.message === "jwt expired") {
return res.json({
success: false,
message: "Access token expired"
});
} else {
console.log(err);
return res
.status(403)
.json({ err, message: "User not authenticated" });
}
});
}
and a separate route for refreshing the access token with the help of refresh token
app.post("/refresh", (req, res, next) => {
const refreshToken = req.body.token;
if (!refreshToken || !refreshTokens.includes(refreshToken)) {
return res.json({ message: "Refresh token not found, login again" });
}
// If the refresh token is valid, create a new accessToken and return it.
jwt.verify(refreshToken, "refresh", (err, user) => {
if (!err) {
const accessToken = jwt.sign({ username: user.name }, "access", {
expiresIn: "20s"
});
return res.json({ success: true, accessToken });
} else {
return res.json({
success: false,
message: "Invalid refresh token"
});
}
});
});
So, my question is how secure it is and how can I create single middleware function that could do both authentication and refreshing access token without hitting the app.post('/refresh') as in my view it wouldn't be a smooth experience to deal with it in frontend API management within react
Edit
My middleware seems to work well but it doesn't identify the wrong refresh token and then actually getting worked on protected route
app.post('/home', authenticateUser, (req, res) => {
res.send('welcome');
});
async function authenticateUser(req, res, next) {
let token = req.headers['authorization'];
token = token.split(' ')[1];
jwt.verify(token, JWT_AUTH_TOKEN, async (err, phone) => {
if (phone) {
req.phone = phone;
next();
} else if (err) {
const refreshToken = req.body.refreshToken;
if (!refreshToken || !refreshTokens.includes(refreshToken)) {
return res.json({ message: 'Refresh token not found, login again' });
} else {
jwt.verify(refreshToken, JWT_REFRESH_TOKEN, (err, phone) => {
if (!err) {
const accessToken = jwt.sign({ phone }, JWT_AUTH_TOKEN, { expiresIn: '30s' });
return res.json({ success: true, accessToken });
} else {
return res.json({
success: false,
message: 'Invalid refresh token'
});
}
next();
});
}
} else {
console.log(err);
return res.status(403).json({ err, message: 'User not authenticated' });
}
});
}
I am learning how to use NodeJS, and while following an online tutorial I incurred in the error in the title.
I am using the latest version of NodeJS, and the jsonwebtoken authentication.
This is how I generate the token:
router.post('/login', (req, res) => {
// Check if username was provided
if (!req.body.username) {
res.json({
success: false,
message: 'No username was provided'
}); // Return error
} else {
// Check if password was provided
if (!req.body.password) {
res.json({
success: false,
message: 'No password was provided.'
}); // Return error
} else {
// Check if username exists in database
User.findOne({
username: req.body.username.toLowerCase()
}, (err, user) => {
// Check if error was found
if (err) {
res.json({
success: false,
message: err
}); // Return error
} else {
// Check if username was found
if (!user) {
res.json({
success: false,
message: 'Username not found.'
}); // Return error
} else {
const validPassword = User.comparePassword(req.body.password, user.password); // Compare password provided to password in database
// Check if password is a match
if (!validPassword) {
res.json({
success: false,
message: 'Password invalid'
}); // Return error
} else {
const token = jwt.sign({
userId: user._id
}, 'goodsecret', {
expiresIn: '24h'
}); // Create a token for client
res.json({
success: true,
message: 'Success!',
token: token,
user: {
username: user.username
}
}); // Return success and token to frontend
}
}
}
});
}
}
});
And here how I evaluate it:
const jwt = require('jsonwebtoken');
/* ================================================
MIDDLEWARE - Used to grab user's token from headers
================================================ */
router.use((req, res, next) => {
const token = req.headers.authorization; // Create token found in headers
// Check if token was found in headers
if (!token) {
res.json({
success: false,
message: 'No token provided'
}); // Return error
} else {
// Verify the token is valid
console.log(token);
jwt.verify(token, 'goodsecret', (err, decoded) => {
// Check if error is expired or invalid
if (err) {
res.json({
success: false,
message: 'Token invalid: ' + err
}); // Return error for token validation
} else {
req.decoded = decoded; // Create global variable to use in any request beyond
next(); // Exit middleware
}
});
}
});
/* ===============================================================
Route to get user's profile data
=============================================================== */
router.get('/profile', (req, res) => {
// Search for user in database
User.findOne({
_id: req.decoded.userId
}).select('username email').exec((err, user) => {
// Check if error connecting
if (err) {
res.json({
success: false,
message: err
}); // Return error
} else {
// Check if user was found in database
if (!user) {
res.json({
success: false,
message: 'User not found'
}); // Return error, user was not found in db
} else {
res.json({
success: true,
user: user
}); // Return success, send user object to frontend for profile
}
}
});
});
Whenever I try to generate a request from HttpRequester, sending the token, I get the error in the title. The token I am sending is
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1OWRlMTYzNTMwOWY2NDM1YjBjOWRmM2UiLCJpYXQiOjE1MDc3NDMzODYsImV4cCI6MTUwNzgyOTc4Nn0.uJwpYN7IHYg_lmCVCpFg-zfo0QVPglEvWHs7SD9cPkg
and on https://jwt.io/ it works fine, using the secret 'goodsecret' as in the code. What am I missing?
This is a screen on how I am generating requests.
Thank you for your help
I am using node.js express app. jsonwebtoken for authentication. I want to exlude some api url from the jsonwebtoken verification. below is what I have tried and my code
router.use('/authentication', mountAllRoutes(authenticationModule));
// route middleware to verify a token
router.use((req, res, next) => {
const r = req;
const token = req.body.token || req.query.token || req.headers.authorization;
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, (req.app.get('superSecret')), (err, decoded) => {
if (err) {
// res.json({ success: false, message: 'Failed to authenticate token.' });
res.status(401).send({
success: false,
message: 'Failed to authenticate token.'
});
} else {
// if everything is good, save to request for use in other routes
r.decoded = decoded;
next();
// console.log(decoded);
}
return {};
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
return {};
});
router.use('/test', mountAllRoutes(testModule));
router.use('/other', mountAllRoutes(otherModule));
router.use('/data', mountAllRoutes(dataModule));
Here I have placed routes above middleware which I dont want to protect. and I have placed after middleware which I want to protect. But it is protected which I placed above middleware. In authenticationModule, login and user registration api comes. so for user registration it gives response no token provided
Note: I have refrerred this link How-to-ignore-some-request-type-in-Jsonwebtoken
create separate route file for the API you want to exclued.
//Routes
var users = require('./routes/users');
var api = require('./routes/publicApi');
App.js:
// route middleware to verify a token
router.use((req, res, next) => {
const r = req;
const token = req.body.token || req.query.token || req.headers.authorization;
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, (req.app.get('superSecret')), (err, decoded) => {
if (err) {
// res.json({ success: false, message: 'Failed to authenticate token.' });
res.status(401).send({
success: false,
message: 'Failed to authenticate token.'
});
} else {
// if everything is good, save to request for use in other routes
r.decoded = decoded;
next();
// console.log(decoded);
}
return {};
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
return {};
});
app.use('/users', router);//will use Token Authentican
app.use('/publicApi', router);//Dont do this.
login api:
api.post('/login', function(req, res) {
User.findOne({
username: req.body.username
}).select('password').exec(function(err, user) {
if(err) throw err;
if(!user) {
res.send({ message: "User doesn't exist."});
} else if(user) {
var validPassword = user.comparePassword(req.body.password);
if(!validPassword) {
res.send({ message: "Invalid password."});
} else {
/////// token
var token = createToken(user);
res.json({
success: true,
message: "Successfully logged in.",
token: token
});
}
}
});
});
middleware:
api.use(function(req, res, next) {
console.log("somebody just came to our app.");
var token = req.body.token || req.param('token') || req.headers['x-access-token'];
// check if token exists
if(token) {
jsonwebtoken.verify(token, secretKey, function(err, decoded) {
if(err) {
res.status(403).send({ success: false, message: "failed to authenticate user."});
} else {
req.decoded = decoded;
next();
}
});
} else {
res.status(403).send({ success: false, message: "no token provided."});
}
});
authService:
authFactory.login = function(username, password) {
return $http.post('/api/login', {
username: username,
password: password
})
.success(function(data) {
AuthToken.setToken(data.token);
return data;
})
}
authFactory.isLoggedIn = function() {
if(AuthToken.getToken())
return true;
else
return false;
}
Now, if my user is logged in and he tries to access: localhost:3000/login , then he should be redirected to localhost:3000/
only after he logs out, he should be able to access the login page (similar to facebook).
How to do this?
1
In login API store user info in respond object(res).
2
app.get('/',function(req,res){
if(req.user){
res.redirect('/login');
} else {
res.render('home');
}
});
RECOMMENDED:PASSPORT and its Local-Strategy.
exports.loginPage = (req, res) => {
if(req.session.user){
res.redirect("/");
} else {
res.render("loginPage");
}
};
On your login route, add the logic inside the middleware function. You could the same on the registration page as well.
exports.registrationPage = (req, res) => {
if(req.session.user){
res.redirect("/");
} else {
res.render("registrationPage");
}
};
The key takeaway is this: if you want a user not to see a page on your site, go to the page's route, add the if/else logic.
Hope that helps, cheers!
I want to ignore some API URL of being checked against token authentication
I want to protect post and put methods but not get of this url
localhost:3000/api/events/
router.use(function(request, response) {
var token = request.body.token || request.query.token || request.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get(superSecret), function(err, decoded) {
if (err)
return response.json({
sucess: false,
message: "Failed token Authentication"
});
else {
request.decoded = decoded;
next();
}
});
} else {
return response.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
How can I do this using jsonwebtoken in node,express
I want this to apply to only post,put,delete requests but not on get requests.
You can move your anonymous middleware to normal declared function and then pass it to all protected routes (you decide which route you want to protect!)
Your code could look like:
function tokenProtection(request, response, next) {
var token = request.body.token || request.query.token || request.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get(superSecret), function(err, decoded) {
if (err)
return response.json({
sucess: false,
message: "Failed token Authentication"
});
else {
request.decoded = decoded;
next();
}
});
} else {
return response.status(403).send({
success: false,
message: 'No token provided.'
});
}
}
and now your routes could look like (your decision what you want to protect):
router.get('/item', function(req, res) { ... }); // not protected
router.get('/item/:id', function(req, res) { ... }); // not protected
router.post(tokenProtection,'/item', function(req, res) { ... });//protected
router.put(tokenProtection,'/item', function(req, res) { ... });//protected
router.get('/book', function(req, res) { ... });// not protected
router.get('/book/:id', function(req, res) { ... });// not protected
router.post(tokenProtection,'/book', function(req, res) { ... });//protected
router.put(tokenProtection,'/book', function(req, res) { ... });//protected
Put the routes you want to protect below your authentication route and the ones you do not want to protect can above the authentication route. Something like this,
// Require what will be needed
var express = require('express'),
User = require('../models/user'),
usersRouter = express.Router();
var jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
var config = require('./config'); // get our config file
var secret = {superSecret: config.secret}; // secret variable,
// Create a new user and return as json for POST to '/api/users'
usersRouter.post('/', function (req, res) {
var user = new User(req.body);
user.save(function(){ //pre-save hook will be run before user gets saved. See user model.
res.json({user : user, message: "Thank You for Signing Up"});
});
});
usersRouter.post('/authentication_token', function(req, res){
var password = req.body.password;
// find the user
User.findOne({
email: req.body.email
}, function(err, user) {
//If error in finding the user throw the error
if (err) throw err;
//If there is no error and the user is not found.
if (!user) {
res.json({ success: false, message: 'Authentication failed. User not found.' });
//if the user is found
} else if (user) {
// check if password matches
user.authenticate(password, function(isMatch){
if(isMatch){
// if user is found and password is right
// create a token with full user object. This is fine because password is hashed. JWT are not encrypted only encoded.
var token = jwt.sign({email: user.email}, secret.superSecret, {
expiresIn: 144000
});
// set the user token in the database
user.token = token;
user.save(function(){
// return the information including token as JSON
res.json({
success: true,
id: user._id,
message: 'Enjoy your token!',
token: token
});
});
} else {
res.json({ success: false, message: 'Authentication failed. Wrong password.' });
}
});
}
});
});
//***********************AUTHENTICATED ROUTES FOR USERS******************************
// Return ALL the users as json to GET to '/api/users'
usersRouter.get('/', function (req, res) {
User.find({}, function (err, users) {
res.json(users);
});
});
// Export the controller
module.exports = usersRouter;
I actually explained this yesterday itself on my blog because I was struggling to figure it out. If you are still not clear, you can check it out here, Node API Authentication with JSON Web Tokens - the right way.
If there are other resources like in my case it was plans. Below is the code I put above all the routes for plans I wanted to authenticate.
// route middleware to verify a token. This code will be put in routes before the route code is executed.
PlansController.use(function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// If token is there, then decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, secret.superSecret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
// if everything is good, save to incoming request for use in other routes
req.decoded = decoded;
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
//***********************AUTHENTICATED ROUTES FOR PLAN BELOW******************************
PlansController.get('/', function(req, res){
Plan.find({}, function(err, plans){
res.json(plans);
});
});