JWT Authentication Express Js and React Js for specific user email - node.js

I have e problem in JWT with the express and react project. User add products and items page show product , Specific User see his added item not all item, I have use filter method.
Here is my API
app.get('/items', verifyJWT, async (req, res) => {
const decodedEmail = req.decoded.email;
const email = req.query.email;
if (email === decodedEmail) {
const query = { email: email };
const cursor = ItemsCollection.find(query);
const Items = await cursor.toArray();
res.send(Items);
}
else {
res.status(403).send({ message: 'forbidden access, Try again' })
}
})
my JWT function
function verifyJWT(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).send({ message: 'unauthorized access' });
}
const token = authHeader.split(' ')[1];
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, decoded) => {
if (err) {
return res.status(403).send({ message: 'Forbidden access, Please dont try' });
}
console.log('decoded', decoded);
req.decoded = decoded;
next();
})
}
My frontend react code is
const [user] = useAuthState(auth);
const [Items, setItems] = useState([]);
const navigate = useNavigate();
const [shouldRemount, setShouldRemount] = useState(false)
useEffect(() => {
const fetchData = async () => {
const email = user.email;
const url = `https://protected-tor-63915.herokuapp.com/items?email=${email}`;
try {
const response = await fetch(url)
if (response.status === 200) {
let data = await response.json();
setItems(data);
}
} catch (error) {
if (error.response.status === 401 || error.response.status === 403) {
signOut(auth);
navigate('/login')
}
}
}
fetchData();
}, [user, shouldRemount, navigate]);

Related

What would be the error with this token verification code?

When making the middleware request in my route, I always fall into the else of "verifyAdmin" (error 403). The big problem is that I can't send a throw or catch of this error, it just doesn't return any error in the terminal, but when testing in postman it always goes to else
const jwt = require('jsonwebtoken');
const verifyToken = (req, res, next) => {
const { authorization } = req.headers;
if (!authorization) {
return res.status(401).json('Invalid Authorization')
};
const token = authorization.replace('Bearer', ' ').trim();
try {
const secret = process.env.JWT_SECRET;
const data = jwt.verify(token, secret);
req.users = data;
const { id } = data;
req.userId = id;
return next();
} catch (err) {
return res.status(400).json(err);
}
};
const verifyAdmin = (req, res, next) => {
if (req.users.isAdmin === true) {
next();
} else {
return res.status(403).json("You are not alowed to do that!");
}
};
module.exports = {
verifyToken,
verifyAdmin,
};
in route
const { verifyToken, verifyAdmin } = require('../middlewares/verifyToken');
router.get('/', verifyToken, verifyAdmin, FindAllUsersController.index);
construction token
const db = require('../../models/index');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
exports.store = async (req, res) => {
const { email, password } = req.body;
const secret = process.env.JWT_SECRET;
try {
const user = await db.User.findOne({ where: { email } });
if (!user) {
return res.status(401).json('User does not exist');
}
const isValidPassword = await bcrypt.compare(password, user.password);
if (!isValidPassword) {
return res.status(401).json('Password is not valid');
}
const token = jwt.sign({ id: user.id }, secret, {
expiresIn: process.env.EXPIRES_TOKEN,
})
return res.status(200).json({
user,
token,
});
} catch (err) {
console.log(err);
}
}
The isAdmin flag is not contained in your token, because you include only the id when constructing it:
const token = jwt.sign({ id: user.id }, ...)
You need (at least):
const token = jwt.sign({ id: user.id, isAdmin: user.isAdmin }, ...)

Cannot post Token from Client

Frontend Code
<script>
const formDOM = document.querySelector(".form");
const btnSubmitDOM = document.querySelector(".btn-submit");
// get token form server
formDOM.addEventListener("submit", async (e) => {
e.preventDefault();
const email = document.querySelector("#email").value;
const password = document.querySelector("#password").value;
try {
const { data } = await axios.post(
"http://localhost:3000/api/v1/auth/login",
{ email, password }
);
console.log("token", data.token);
localStorage.setItem("token", data.token);
} catch (error) {
console.log(error);
}
});
// after submit, if authenticated, goes to protective route
btnSubmitDOM.addEventListener("click", async () => {
const token = localStorage.getItem("token");
console.log("toekn when press", token);
try {
const { data } = await axios.get("http://localhost:3000/api/v1/inventories", {
headers: {
Authorization: `Bearer ${token}`,
},
});
location.assign("/inventories");
} catch (error) {
localStorage.removeItem("token");
console.log(error);
}
});
</script>
Here is the backend code
const jwt = require("jsonwebtoken");
const auth = async (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer")) {
res.send("Auth route - Unauthenticated Error");
}
const token = authHeader.split(" ")[1];
console.log("token is ", token);
try {
const payload = jwt.verify(token, 'secret');
// console.log(payload);
// attached to the inventories route
req.user = {
userId: payload.userId,
email: payload.email,
};
// console.log(req.user)
next();
} catch (error) {
console.log(error);
}
};
module.exports = auth;
Here is the error from server side
token is eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2MzY4ZjFiM2U1ODJlYmQ1MjJkODI4MGEiLCJlbWFpbCI6Imt5YXdAZ21haWwuY29tIiwiaWF0IjoxNjY3ODI3NzIzLCJleHAiOjE2NzA0MTk3MjN9.rptgRrYB4TrLQCxbB18JqMHd05LSox-LQuiLJS0L2Gw
/Users/kyawmyo/Desktop/software-development/nodejs-&-vanilajs-projects/inventories/middleware/authentication.js:8
const token = authHeader.split(" ")[1];
^
TypeError: Cannot read properties of undefined (reading 'split')
I try to test with postman, all working fine.
But I send request from client side, it is error, I cannot figure it out. Please help.

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

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

how to jwt token verify using nodejs

how to implement jwt verify token implement in node js.I already tried but its not showing error but its showing undefined.How to solve this issue.
auth.py
function jwtAccessAuthCheck(accessToken)
{
if(accessToken)
{
console.log("Sucess")
jwt.verify(accessToken,"secretkey",function(err){
if(err) {
console.log(typeof(err.message))
return err.message
}
else {
return "token"
}
});
}
else
{
return "Invalid token"
}
}
routes.py
//Get Data from Database
router.get('/', async (req, res,next) => {
(async function() {
try {
await client.connect();
console.log("Connected correctly to server");
const db = client.db('olc_prod_db');
const token_validation = validate_token.validate_token(req.headers.authorization)
console.log((token_validation))
const r = await db.collection('Ecommerce').find().toArray();
client.close();
res.send(r)
} catch(err) {
console.log(err.stack);
}
})();
});
const express = require('express');
const app = express();
const jwt = require('jsonwebtoken');
const bodyparser = require('body-parser');
const user = {username : "user",password : "pass"}
app.use(bodyparser.json());
const checkToken = function (req,res,next)
{
const header = req.headers['authorization'];
if(typeof header !== 'undefined')
{
const bearer = header.split(' ');
const token = bearer[1];
req.token=token;
next();
}
else
{
res.sendStatus(403);
}
}
app.post('/login',function (req,res)
{
const { body } = req;
const { username } = body;
const { password } = body;
if(username === user.username && password === user.password)
{
jwt.sign({user},'privatekey',{expiresIn : '1h'},function (err,token){
if(err)
{
console.log(err)
}
console.log(token);
res.end();
});
}
else
{
console.log('Error : Could not log in');
}
});
app.get('/data',checkToken,function(req,res)
{
jwt.verify(req.token,'privatekey',function (err,authorizedata)
{
if(err)
{
console.log('Error : Could not connect to the protected route');
res.sendStatus(403);
}
else
{
res.json({
message : 'Successful log in',
authorizedata
});
console.log('Success : Connected to protected route');
}
});
});
app.listen(3000,console.log("Server is running at 3000"));
This is how I have implemented JWT token

Resources