const express = require("express");
const jwt = require("jsonwebtoken");
const { type } = require("os");
const app = express();
app.get("/api", (req,res) => {
res.json({
message: "Welcome to the API"
});
});
app.post("/api/posts", (req,res) => {
res.json({
message: "Post has been created.."
});
});
app.post("/api/login", verifyToken, (req,res) => {
// Mock User
const user = {
id: 24,
username: "Jonah",
email: "jonah_52#gmail.com"
}
jwt.sign({user}, "secretkey", (err, token) => {
res.json({
token
});
});
});
// Verify Token
function verifyToken(req,res,next) {
// Get authorization header value
const bearerHeader = req.headers["authorization"];
}
// Check if bearer is undefined
if(typeof bearerHeader !== "undefined") {
}else{
// Forbidden
res.sendStatus(403);
}
app.listen(5000, () => console.log("Server is listening on port 5000.."));
I want to make Node.js API Authentication With JWT and when I check this with if/else method I get an error that is saying "res is not defined", how can I defined this "res" and fix the error?
res.sendStatus(403);
^
ReferenceError: res is not defined
You have some improper bracing in your verifyToken() function. Change this:
// Verify Token
function verifyToken(req,res,next) {
// Get authorization header value
const bearerHeader = req.headers["authorization"];
}
// Check if bearer is undefined
if(typeof bearerHeader !== "undefined") {
}else{
// Forbidden
res.sendStatus(403);
}
to this:
// Verify Token
function verifyToken(req,res,next) {
// Get authorization header value
const bearerHeader = req.headers["authorization"];
// Check if bearer is undefined
// FIX THIS: need to not just check if it's undefined, but if
// its valid
if(typeof bearerHeader !== "undefined") {
next();
} else {
// Forbidden
res.sendStatus(403);
}
}
Related
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.
In My MERN application SIGNUP,SIGNIN is working fine, but when am trying to view my profile on clicking about page then getting an error like "No authorization token was found" for backend am using issignedIn and isAuthenticated middleware, those person who SignIn application who are able to see about us page.
ABoutus.js
const callAboutPage = async()=>{
try{
const res = await fetch('/about',{
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('/login')
}
}
useEffect(() => {
callAboutPage();
}, [])
auth.js/controller
//PROTECTED ROUTE
exports.isSignedIn = expressJwt({
secret: process.env.SECRET,
algorithms: ['RS256'],
userProperty: "Auth"
});
//CUSTOM MIDDLEWARE
exports.isAuthenticated = (req, res, next) => {
let checker = req.profile && req.Auth && req.profile._id == req.Auth._id;
if (!checker) {
return res.status(403).json({
error: "ACCESS DENIED"
});
}
next();
};
Route
router.get('/about',isSignedIn,isAuthenticated,(req,res)=>{
res.send("A protected Route")
});
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 set up jsonwebtoken in node.js and testing it with postman. First upon login I generate the token, then I copy it and use for the post request to a protected route. And jwt.verify runs just fine, the correct userId is retrieved, but when I call next() (see below) it goes to 404 error handler instead of the index where the protected routes are located:
var index = require('./routes/index')
function verifyToken (req, res, next) {
// Get auth header value
const bearerHeader = req.headers['authorization']
// check if bearer is undefined
const message = 'Unauthorized user, access denied!'
if (typeof bearerHeader !== 'undefined') {
const bearerToken = bearerHeader.split(' ')[1]
jwt.verify(bearerToken, 'privateKey', (err, userData) => {
if (err) {
// Forbidden
console.log(err)
res.status(403).send({ err })
} else {
console.log(userData.userId)
req.userId = userData.userId
------> next()
}
})
} else {
// Forbidden
res.status(403).send({ message })
// res.sendStatus(403)
}
}
// app.use('/auth', index);
app.use('/auth', verifyToken, index)
Why is it happening? What am I doing wrong here? Basically my goal is to set userId on the req object, then get it back in the root route auth/ in index
How can i send and get jsonweb token in a browser? I need to send on every forbidden page token after user has signed in.
I have code and everything works fine in a postman but when i try in a browser req.headers["authorization"] is always undefined. I was also trying to res.set({'token': 'some token'}); and then request in the next page but still undefined. There is a lot tutorial with postman but I can figure out how can I do this in a browser.
Here is my code:
app.post("/api/login", function(req, res) {
var user = { id: 3 };
var token = jwt.sign({ user }, 'secret');
res.json({token: token});
});
app.get('/api/protected', ensureToken, function(req, res) {
jwt.verify(req.token, 'secret_key_goes_here', function(err, data) {
if (err) {
res.sendStatus(403);
} else {
res.json({
description: 'Protected information. Congrats!'
});
}
});
});
function ensureToken(req, res, next) {
const token = req.headers["authorization"];
console.log(token);
if (typeof bearerHeader !== 'undefined') {
console.log('ok');
const berarer = bearerHeader.split(" ");
const bearerToken = berarer[1];
req.token = bearerToken;
next();
} else {
console.log('no');
res.sendStatus(403);
}
}
Please, any suggestion.
Thanks