JsonWebTokenError: jwt malformed (creating an API in ExpressJs) - node.js

I've checked many answers on the internet but I think I'm doing some other mistake also. My question is strictly related to guarding /contribute route using JWT (which is a POST request). My API is in expressjs. First I hit /login route with correct credentials and get a token. This token I cross checked on jwt.io and it says "Invalid Signature".
Here is that token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWJqZWN0IjoiNWVhZDlkOGY4N2VjMjkwMDE3YzRkODkzIiwiaWF0IjoxNjA3ODczNjY2fQ.H5RI-lOBzfJo4_FgParuJA4ULnJ_An6uihiF31bzNtU
Then I would hit /contribute route with the same token. Here is my code:
api.js
const express = require('express');
const router = express.Router();
...
const jwt = require('jsonwebtoken');
...
router.post('/login', (req, res) => {
let userData = req.body
User.findOne({ email: userData.email }, (error, user) => {
if (error) {
console.log(error)
} else {
if (!user) {
res.status(401).send('Invalid email')
} else if (user.password !== userData.password) {
res.status(401).send('Invalid password')
} else {
let payLoad = { subject: user._id }; // tried { subject: 'foobar' } also
let token = jwt.sign(payLoad, 'secretKey');
res.status(200).send({ token, userData, user });
}
}
})
})
router.post('/contribute', verifyToken, (req, res) => {
console.log('Pushing new article');
let userPost = req.body;
let post = new Post(userPost);
post.save((error, registeredPost) => {
if (error) {
console.log(error);
} else {
res.status(200).send(registeredPost);
}
})
})
function verifyToken(req, res, next) {
if (!req.headers.authorization) {
return res.status(401).send('Unauthorized request')
}
let token = req.headers.authorization.split(' ')[1];
if (token === 'null') {
return res.status(401).send('Unauthorized request')
}
let payload = jwt.verify(token, 'secretKey')
if (!payload) {
return res.status(401).send('Unauthorized request')
}
req.userId = payload.subject
next()
}
module.exports = router;
But the moment I hit /contribute I get this:
JsonWebTokenError: jwt malformed
at Object.module.exports [as verify] (C:\Users\320050772\Documents\socialcoderapinodejs\node_modules\jsonwebtoken\verify.js:63:17)
at verifyToken (C:\Users\320050772\Documents\socialcoderapinodejs\routes\api.js:86:23)
at Layer.handle [as handle_request] (C:\Users\320050772\Documents\socialcoderapinodejs\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\320050772\Documents\socialcoderapinodejs\node_modules\express\lib\router\route.js:137:13)
at Route.dispatch (C:\Users\320050772\Documents\socialcoderapinodejs\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\Users\320050772\Documents\socialcoderapinodejs\node_modules\express\lib\router\layer.js:95:5)
Please point out my mistake.
I checked on Postman also. Token is generated but again it is invalid. Why my code is generating invalid tokens.

It been noticed that Generated Token is valid. But its not been reaching to backend with upcoming next call e.g Post of /contribute. showing some non-valid value.
Therefore, let the valid token to be arrived on Backend so that jwt.varify could validate it correctly.

Bro I checked your code, all is OK, verifyToken ✅ , (though not the User and Post objects) make sure you send the authorisation header correctly like this:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWJqZWN0IjoiMTIzNDQ1IiwiaWF0IjoxNjA3ODgwMjkwfQ.zJbcqM8yBRABxhm5BgQNow1gmzsgUjiLdPdv7Tq5ND4
I used the code from your example and modified a bit
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.get('/', (req, res) => res.send('123'));
app.post('/login', (req, res) => {
let payLoad = { subject: '123445' };
let token = jwt.sign(payLoad, 'secretKey');
res.status(200).send({ token });
});
app.post('/contribute', verifyToken, (req, res) => {
res.send('i can reach here...');
});
function verifyToken(req, res, next) {
if (!req.headers.authorization) {
return res.status(401).send('Unauthorized request');
}
let token = req.headers.authorization.split(' ')[1];
if (token === 'null') {
return res.status(401).send('Unauthorized request');
}
let payload = jwt.verify(token, 'secretKey');
if (!payload) {
return res.status(401).send('Unauthorized request');
}
req.userId = payload.subject;
next();
}
app.listen(3000, () => console.log('server on 3000'));

Related

Jwt authorization always giving 403 Forbidden

I am a beginner with node js. I want to make an authentication server using jwt (jsonwebtoken).
The problem is when I test my end point "/api/posts?authorisation=Bearer token..." in postman with method POST with the right token, it gives me forbidden.
Here is my code:
const express = require('express')
const jwt = require('jsonwebtoken')
const app = express()
app.get("/api", (req, res) => {
res.json({
message: "Hey there!!!"
})
})
app.post('/api/posts', verifyToken, (req, res) => {
jwt.verify(req.token, "secretkey", (err, authData) => {
if (err) {
res.sendStatus(403) //forbidden
res.send(`<h2>${err}</h2>`)
} else {
res.json({
message: "Post Created...",
authData
})
}
})
})
app.post('/api/login', (req, res) => {
const user = {
id: 1,
username: "John",
email: "john#gmail.com"
}
jwt.sign({ user: user }, "secretkey", (err, token) => {
res.json({
token
})
})
})
function verifyToken(req, res, next) {
const bearerHeader = req.headers["authorization"]
if (typeof bearerHeader !== "undefined") {
const bearerToken = bearerHeader.split(" ")[1]
req.token = bearerToken
next()
} else {
res.sendStatus(403) //forbidden
}
}
app.listen(5000, () => {
console.log("Server is running :)")
})
I expected it to work because I brought it from a tutorial.
Your code works
The problem is in your request invocation:
According to the oauth2 spec, the Authorization token should be a header and your code expect that
So the token should be sent as http header, not as a query param like foo/bar?authorization=Bearer token...".
Here some samples
Postman
Axios (javascript)
let webApiUrl = 'example.com/getStuff';
let tokenStr = 'xxyyzz';
axios.get(webApiUrl,
{ headers: { "Authorization": `Bearer ${tokenStr}` } });
Advice
Read about oauth2 and jwt
Perform the token validation in the middleware to avoid the validation on each route

Handling Login Authorization with Node.js

Here I'm carrying out the GET method to list the data by authenticating the login credentials, but when I pass the token in value in the header it directly catch the error message. is anything I'm doing wrong?
Authentication Middleware - authentication.js
const jwt = require("jsonwebtoken");
const authenticate = (req, res, next) => {
const access_token = req.headers["authorization"];
if (!access_token) return res.status(401).send("Access denied! no token provided.");
try {
const decoded = jwt.verify(access_token, "SECRET_JWT_CODE");
req.receive = decoded;
next();
} catch (error) {
res.status(400).send("invalid token.");
}
};
module.exports = authenticate;
console.log(req.headers)
GET method
const authenticate = require("./authentication.js");
router.get("/admin", authenticate, async (req, res) => {
try {
const receive = await SomeModel.find();
res.json(receive);
} catch (err) {
res.send(err);
}
});
login
router.post("/admin/sign_in", (req, res) => {
if (!req.body.email || !req.body.password) {
res.json({ error: "email and password is required" });
return;
}
login
.findOne({ email: req.body.email })
.then((admin) => {
if (!admin) {
res.json({ err: "user does not exist" });
} else {
if (!bcrypt.compareSync(req.body.password, admin.password)){
res.json({ err: "password does not match" });
} else {
const token = jwt.sign(
{
id: admin._id,
email: admin.email,
},
SECRET_JWT_CODE
);
res.json({
responseMessage: "Everything worked as expected",
access_token: token,
});
}
}
})
.catch((err) => {
err.message;
});
});
The token is in the form Bearer <token> so you need to split it before verifying it:
const jwt = require("jsonwebtoken");
const authenticate = (req, res, next) => {
const access_token = req.headers["Authorization"];
if (!access_token) return res.status(401).send("Access denied! no token provided.");
const splitToken = access_token.split(' ');
if (splitToken.length !== 2) return res.status(401).send("Access denied! invalid token.");
const token = splitToken[1];
try {
const decoded = jwt.verify(token, "SECRET_JWT_CODE");
req.receive = decoded;
next();
} catch (error) {
res.status(400).send("invalid token.");
}
};
module.exports = authenticate;
Also, make sure that you pass the token via the Authorization tab in Postman.
It should be available in req.headers["Authorization"] (capitalized) in the expected Bearer <token> format.

I am not able to get jwt token from cookies while authorization some pages.?

I have created a MERN application in that application whenever try to login in application jwt token is generated each time whenever he/she tries to log in and stored inside the MongoDB atlas as well as browser cookies.
In the authorization part, if a user is authorized (cookies token matches with the MongoDB token) then he/she can see only the about-me page or else be redirected to the login page.
so whenever I clicked to about-me page I got err:
TypeError: Cannot read properties of undefined (reading 'jwtoken')
at Authenticate (/Users/apple/Desktop/projects/mern-auth-user/user-data-123/mern-july1/server/middleware/authenticate.js:8:35)
at Layer.handle [as handle_request] (/Users/apple/Desktop/projects/mern-auth-user/user-data-123/mern-july1/server/node_modules/express/lib/router/layer.js:95:5)
error so on....
here; How I created sign API and stored token into MongoDB and cookies. (in auth.js)
router.post("/signin", async (req, res) => {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({ err: "invalid details" });
}
try {
const userLogin = await User.findOne({ email: email });
// console.log(userLogin);
if (!userLogin) {
res.status(400).json({ err: "invalid email" });
}
else {
const isMatch = await bcrypt.compare(password, userLogin.password);
// jwt token
const token = await userLogin.generateAuthToken();
res.cookie("jwtoken", token, {
expires : new Date(Date.now()+25892000000), // after 30 days
httpOnly: true
});
console.log(token);
if(!isMatch){
res.status(400).json({err: 'invalid pass'})
}
else{
res.status(201).json({message:'signin successfully'})
console.log(userLogin)
}
}
} catch (err) {
console.log(err);
}
});
generateAuthToken defined inside the user schema modal used inside the signin API.
userSchema.methods.generateAuthToken = async function(){
try{
const token = jwt.sign({_id : this._id}, process.env.SECRET_KEY); // _id(asking unique id) : this._id (taken id from database of corresponding login email)
this.tokens = this.tokens.concat({token : token}) // storing jwt token into tokens. token(from userSchema) : token (value from generated token)
await this.save();
return token;
}
catch(err){
console.log(err);
}
}
this is my middleware "Authenticate"
const jwt = require('jsonwebtoken');
const User = require('../models/userSchema');
const Authenticate = async (req, res, next) => {
try {
//get jwt token from cookies
const token = req.cookies.jwtoken;
//verifying token with SECRET_KEY
const verifyToken = jwt.verify(token, process.env.SECRET_KEY);
// get user data from token, if token id(from cookies)===tokens.token
const rootUser = await User.findOne({ _id: verifyToken._id, "tokens.token": token });
if (!rootUser) { throw new Error('user not found') }
// if get user
req.token = token;
// get user's all data in rootUser
req.rootUser = rootUser;
// get id od rootUser
req.userID = rootUser._id;
next();
}
catch (err) {
res.status(401).send("unauthorized: no token provided")
console.log(err)
}
}
module.exports = Authenticate;
used inside the about-me API; auth.js
router.get('/about', authenticate, (req, res) => {
// console.log(Authenticate.token)
res.send('hello world from server side');
res.send(req.rootUser)
})
now code inside the Reactjs
About.js
const history = useHistory();
const callAboutPage = async () =>{
try{
const res = await fetch('/about', { //this res is backend response , not from call back function
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
credentials: "include"
});
const data = await res.json();
console.log(data)
if(! res.status === 200){
const error = new Error(res.error);
throw error;
}
}
catch(err){
console.log(err)
history.push('/signin');
}
};
useEffect(() => {
callAboutPage()
}, [])
These is my cookies stored in the browser
enter image description here
please help me to get the about-me page right now I am not able to see my page even I have cookies inside my application(browser).
Cannot read properties of undefined (reading 'jwtoken') indicates that req.cookies is undefined in Authenticate.js.
You may miss the middleware cookie-parser for express, check express doc (req.cookies) here.
To fix this, follow the example of cookie-parser (npm) would help. Code like below will allow you to read cookies from req.cookies.
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser());

How to use jwt in koa2 & nodejs

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

[ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client in MERN Stack Application

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

Resources