Jwt authorization always giving 403 Forbidden - node.js

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

Related

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.

JWT Full Stack Verify Token Not Working to Frontend

I have an API in which uses VerifyToken authentication with JWT. This works through postman, however it appears there's an issue passing this through to the frontend to ensure the token is verified.
For one, I have a code block to create verifyToken:
const verifyToken = (req, res, next) => {
const authHeader = req.headers.token;
if (authHeader) {
const token = authHeader.split(" ")[1];
jwt.verify(token, process.env.JWT_SEC, (err, user) => {
if (err) res.status(403).json("Token is not valid!");
req.user = user;
next();
});
} else {
return res.status(401).json("You are not authenticated!");
}
};
If I run the following in Postman, it works all good, header and all.
localhost:5000/api/users/updateUser/62a9be62a8262145b72feee9
This is then handled in requestMethods,
import axios from "axios";
const BASE_URL = "http://localhost:5000/api/";
const user = JSON.parse(localStorage.getItem("persist:root"))?.user;
const currentUser = user && JSON.parse(user).currentUser;
const TOKEN = currentUser?.accessToken;
export const publicRequest = axios.create({
baseURL: BASE_URL,
});
export const userRequest = axios.create({
baseURL: BASE_URL,
bearer: { token: `Bearer ${TOKEN}` },
});
However when I pass this to the frotnend, for example through a request like this,
const updateUser = async () => {
//include all in the state earlier with spread
//update only position, which is input
userRequest.put(`users/updateUser/${user._id}`, userData);
console.log('your data has updated to', userData)
//include the put request here for API
}
I am now getting an error for a 401: Error: Request failed with status code 401
It appears the token isn't being passed to the frontend correctly. What am I doing wrong?
Passing token in headers in Axios is not tuned correctly. Notice the headers config.
export const userRequest = axios.create({
baseURL: BASE_URL,
headers: { token: `Bearer ${TOKEN}` },
});
Another way to pass the token is where you are calling the API:
const updateUser = async () => {
//include all in the state earlier with spread
//update only position, which is input
userRequest.put(`users/updateUser/${user._id}`, userData, {
headers: {token: `Bearer ${TOKEN}`}
});
console.log('your data has updated to', userData)
//include the put request here for API
}
Here is another tip: in your auth middleware by detecting a wrong token, do not go to next and return!
const verifyToken = (req, res, next) => {
const authHeader = req.headers.token;
if (authHeader) {
const token = authHeader.split(' ')[1];
jwt.verify(token, process.env.JWT_SEC, (err, user) => {
if (err) {
res.status(403).json('Token is not valid!');
return;
} else {
req.user = user;
next();
}
});
} else {
return res.status(401).json('You are not authenticated!');
}
};

JsonWebTokenError: jwt malformed (creating an API in ExpressJs)

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

Providing Express Authorization token for RESTFUL API

Looking at the following express code, how can I create a token to feed Authorization?
If I comment on this code, the application runs fine.
app.use((req, res, next) => {
const token = req.get('Authorization')
if (token) {
req.token = token
next()
} else {
res.status(403).send({
error: 'Please provide an Authorization header'
})
}
})
Here is
app.get('/', (req, res) => {
fetch(url, { headers: { 'Authorization': 'The token I need to generatet' }})
The ideal approach would be to generate a token on /login and send it to client.
Using a package like jsonwebtoken
Example method to generate a token:
const jwt = require('jsonwebtoken');
function generateToken(userId){
const privateKey = "Some Strong Secret String";
const token = jwt.sign({ userId }, privateKey, function(err, token) {
console.log(token)
});
}
To verify if token is valid (inside the if{})
jwt.verify(token, privateKey, function(err, decoded) {
if (err) {
/*
err = {
name: 'TokenExpiredError',
message: 'jwt expired',
expiredAt: 1408621000
}
*/
}
});

Securely access route with JWT and localstorage

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

Resources