How to use jwt in koa2 & nodejs - node.js

I have a page written in nodejs and koa, and I'm a little confused about JWT, please check following code.
'use strict';
const Router = require("koa-router");
const router = new Router({prefix: '/login'});
const jwt = require('jsonwebtoken');
const jwtkey = "123456";
router
.get('/', async (ctx, next) => {
await ctx.render("login", {
});
})
.post('/', async (ctx, next) => {
try {
let email = ctx.request.body.email || "";
let password = ctx.request.body.password || "";
//check user
if (islogin) {
let payload = {
email: email,
password: sha1(password).toString()
}
let token = jwt.sign(payload, jwtkey, {expiresIn: '3h'});
ctx.body = {"error": false, "msg": token};
} else {
throw "Wrong";
}
} catch (e) {
ctx.body = {error:true, msg: e instanceof Error ? e.message : e};
}
})
module.exports = router;
I want to implement that when accessing the login page, if the token generated by jwt exists on the server side and is correct, then console.log("logined"),if not, show the login page.
.get('/', async (ctx, next) => {
//how to verify the token?
if(token) {
console.log("logined");
} else {
await ctx.render("login", {
});
}
})
Thank you.

After generating the token you should set that token in user's browser as cookie or some other way,
Then while requesting / page check for the token and verify the validity
var decoded = jwt.verify(token, key);

Related

Node.js when I update a user it un-hash's the password

So im trying to make an admin login and I can get it to hash the password. But when I update the password or even the username it will un-hash the passwor for me when i need it to stay hashed. When I check my MongoDB itll be un-hashed for some reason. Any help will be appreciated. Thank you.
This is my user route
var express = require("express");
var router = express.Router();
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const { isAuthenticated } = require("../middleware/auth");
const Admin = require("../models/Admin");
const saltrounds = 10;
/* GET users listing. */
router.get("/", function (req, res, next) {
res.send("respond with a resource");
});
router.post("/login", async (req, res) => {
if (!req.body.username || !req.body.password) {
return res.json({ message: "Please enter username and password" });
}
try {
const foundUser = await Admin.findOne({ username: req.body.username });
if (!foundUser) {
return res
.status(400)
.json({ message: "Username or password is incorrect" });
}
const isMatch = bcrypt.compareSync(req.body.password, foundUser.password);
if (!isMatch) {
return res
.status(400)
.json({ message: "Username or password incorrect" });
}
const payload = {
username: foundUser.username,
id: foundUser._id,
};
const token = jwt.sign(payload, process.env.SECRET, {
algorithm: "HS256",
expiresIn: "6h",
});
res.json({ token: token, id: foundUser.id });
} catch (err) {
res.status(400).json(err.message);
}
});
router.post("/update", isAuthenticated, async (req, res) => {
try {
const updateUser = await Admin.findByIdAndUpdate(
req.user.id,
{ ...req.body },
{ new: true }
);
res.json(updateUser);
} catch (err) {
res.json(err.message);
}
});
module.exports = router;
This is my authenticated folder
const jwt = require("jsonwebtoken");
const isAuthenticated = async (req, res, next) => {
// const token = req.headers.authorization;
// NOTE: if your token authentication is failing in Postman, uncomment the line below, and comment out the line above
const token = req.headers.authorization?.split(" ")[1];
if (!token || token === "null") {
console.log("NO TOKEN");
return res.status(400).json({ message: "Token not found" });
}
try {
const tokenInfo = jwt.verify(token, process.env.SECRET);
console.log(tokenInfo);
//If you have req.payload, change line 12 to:
// req.payload = tokenInfo;
req.user = tokenInfo;
next();
} catch (error) {
return res.status(440).json(error);
}
};
// Export the middleware so that we can use it to create a protected routes
module.exports = {
isAuthenticated,
};
I think the issue is located at the (function)
Admin.findByIdAndUpdate
May be the find unhash the password fisrt and the update do not hash it again.
Check at this function.

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

How to send cookies in express?

router.post('/login', async (req, res) => {
const logInEnvironment = browser(req.headers['user-agent']);
const ipAddress = requestIp.getClientIp(req);
const userEmail = req.body.userEmail.toLowerCase();
const password = req.body.password;
// Form validation
const validation = await validateLoginInput(req.body);
// Check validation
if (!validation.isValid) {
return res.status(400).json(validation.errors);
}
// Find user by email
const auth = await Auth.findOne({ userEmail }, { userLoginInfo: 0 });
//Check if user exists
if (auth === null) {
console.log('aaaa');
res.cookie('send2', 'shhhhhent!');
res.status(200);
return res
.status(400)
.json({ authFailedMessage: 'Email or password is incorrect' });
} else if (auth !== null) {
bcrypt.compare(password, auth.password).then((isMatch) => {
if (isMatch) {
return res.cookie('failed', 'yess!').status(200).json({succeeded: 'hiii'})
} else if (!isMatch) {
return res.cookie('succeeedd', 'noooo!').status(400).json({failed: 'hiii'})
}
});
}
});
I have this code. But, the res.json has been returned without cookies.
Moreover, I added these commands in my index.js file
const cookieParser = require('cookie-parser');
app.use(cookieParser());
I also tested if res.cookie() was working in another route
router.get('/cookie', (req, res) => {
res.cookie('hellooo', 'hiiiiii')
res.send('woooorked?')
})
This was returning cookie and I can see that in the dev-panel on Chrome. What have I done wrong in the first code that cookies are not sent to the browser?
Did you try to split the code like
router.get('/cookie', (req, res) => {
res.cookie('hellooo', 'hiiiiii')
res.send('woooorked?')
})
Can you test with this code
router.post('/login', async (req, res) => {
const logInEnvironment = browser(req.headers['user-agent']);
const ipAddress = requestIp.getClientIp(req);
const userEmail = req.body.userEmail.toLowerCase();
const password = req.body.password;
// Form validation
const validation = await validateLoginInput(req.body);
// Check validation
if (!validation.isValid) {
return res.status(400).json(validation.errors);
}
// Find user by email
const auth = await Auth.findOne({ userEmail }, { userLoginInfo: 0 });
res.cookie('send2', 'shhhhhent!');
return res.json({ authFailedMessage: 'Email or password is incorrect' });
});
Then you check the header of the request and find the header name set-cookie, if it visible, you have got it

JWT must be provided - Delete method returning token as null instead of user token

I have issue as title say. I will show you code in NodeJS. Request is blogRouter.delete
controllers/blog.js (only delete method)
const blogsRouter = require('express').Router()
const jwt = require('jsonwebtoken');
const Blog = require('../models/blog')
const User = require('../models/user')
blogsRouter.delete('/:id', async (request, response, next) => {
const token = getTokenFrom(request)
console.log('token: ',token)
try {
const decodedToken = jwt.verify(token, process.env.SECRET)
if (!token || !decodedToken.id) {
return response.status(401).json({ error: 'token missing or invalid' })
}
const userid = await User.findById(decodedToken.id)
const blogs = await Blog.findById(request.params.id)
if(blogs.user.toString() === userid.toString()) {
await Blog.findByIdAndRemove(request.params.id)
response.status(204).end()
} else {
response.status(404).end()
}
}catch(exception){next(exception)}
})
When i console log token i get null via helper function getTokenFrom
getTokenFrom
const getTokenFrom = request => {
const authorization = request.get('authorization')
if (authorization && authorization.toLowerCase().startsWith('bearer ')) {
return authorization.substring(7)
}
return null
}
In post request token working perfectly fine. Im able to create a blog. But when i do the same thing with delete method it wont show token. It says its null. So it returning me my getTokenFrom function correctly but i want to be able to access token in delete method so i can be able to delete certain blog.
controller/login
const jwt = require('jsonwebtoken')
const bcrypt = require('bcryptjs')
const loginRouter = require('express').Router()
const User = require('../models/user')
loginRouter.post('/', async (request, response) => {
const body = request.body
const user = await User.findOne({username: body.username})
const passwordCorrect = user == null ?
false : await bcrypt.compare(body.password, user.passwordHash)
if(!(user && passwordCorrect)) {
return response.status(401).json({
error: "Invalid username or passowrd"
})
}
const userForToken = {
username: user.username,
id: user._id,
}
const token = jwt.sign(userForToken, process.env.SECRET)
response.status(200).send({token, username: user.username, name: user.name})
})
module.exports = loginRouter
https://prnt.sc/qfjgka --> This is a picture. I send http.delete request and i get token null. JWT must be provided. I dont know were is my mistake. I tried a lot of things but it wont work.
I tried to define token with token.request.jwt but then i get it undifined.
I just need to access that token somehow in blogRoute.delete method.
Thanks in forward
EDIT : This is my post method and when i console log token here it returns me value of the token but when i do same things in delete method it wont work
blogsRouter.post('/', async (request, response, next) => {
const body = request.body
console.log('body', body)
const token = getTokenFrom(request)
console.log('token: ', token)
try {
const decodedToken = jwt.verify(token, process.env.SECRET)
if (!token || !decodedToken.id) {
return response.status(401).json({ error: 'token missing or invalid' })
}
const user = await User.findById(decodedToken.id)
const blog = new Blog({
title: body.title,
author: body.author,
url: body.url,
likes: body.likes,
user: user._id
})
const savedBlog = await blog.save()
user.blogs = user.blogs.concat(savedBlog._id)
await user.save()
response.json(savedBlog.toJSON())
} catch(exception) {
next(exception)
}
})
In the screenshot where you logged the request headers, there seems no authorization header, so you are getting error. If you can succesfully be able to send the authorization header, the problem will be resolved.
By the way, checking the token and validating it in every route is not a good solution.
You had better to use an authentication middleware for token validation.
1-) Create an auth middleware like this:
middleware\auth.js
const jwt = require("jsonwebtoken");
module.exports = function(req, res, next) {
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith('Bearer')
) {
token = req.headers.authorization.split(' ')[1];
}
if (!token) {
return res.status(401).json({ error: 'token missing' })
}
try {
const decoded = jwt.verify(token, process.env.SECRET);
req.user = decoded;
next();
} catch (ex) {
return res.status(400).json({ error: 'token invalid' })
}
};
2-) use this auth middlware anywhere you need authentication. Now our routes are clenaer and shorter.
const blogsRouter = require("express").Router();
const jwt = require("jsonwebtoken");
const Blog = require("../models/blog");
const User = require("../models/user");
const auth = require("../middleware/auth");
blogsRouter.delete("/:id", auth, async (request, response, next) => {
try {
const userid = request.user.id; //we set the user in the auth middleware, so we can access it like this
const blogs = await Blog.findById(request.params.id);
if (blogs.user.toString() === userid.toString()) {
await Blog.findByIdAndRemove(request.params.id);
response.status(204).end();
} else {
response.status(404).end();
}
} catch (exception) {
next(exception);
}
});
blogsRouter.post("/", auth, async (request, response, next) => {
try {
const body = request.body
const user = await User.findById(request.user.id);
const blog = new Blog({
title: body.title,
author: body.author,
url: body.url,
likes: body.likes,
user: user._id
});
const savedBlog = await blog.save();
user.blogs = user.blogs.concat(savedBlog._id);
await user.save();
response.json(savedBlog.toJSON());
} catch (exception) {
next(exception);
}
});
In this way you can send your token in the authorization header in the form Bearer TOKEN.....
" if (blogs.user.toString() === userid.toString()) {" it is important to restrict user access to his own resources, so that the route is something like:
delete posts/:id
rather than, delete /users/:uid/posts/:id because hackers will be able to guess out the ids and delete other people's posts.

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)
}
}

Resources