Cannot post Token from Client - node.js

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.

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.

Trying access the jws token in middleware file. jws token alwasy undefine in middleware file

I'm having trouble accessing my jwt token in the middleware file. I'm getting a token. But when I decoded it using online (https://jwt.io/) it says Invalid Signature. But the decoded result shows the correct email and hash password. Here is the userController.js file. I'm using node.js and express.js for this project
export const signUp = async (req, res) => {
const { email, password, fName, lName, confirmPassword } = req.body;
try {
const existingUser = await userModel.findOne({ email });
if (existingUser)
return res.status(400).json({ message: "User already exist" });
if (password !== confirmPassword)
return res.status(400).json({ message: "Password don't match" });
const hashedPasswoed = await bcrypt.hash(password, 12);
const result = await userModel.create({
email,
password: hashedPasswoed,
name: `${fName} ${lName}`,
});
const token = jwt.sign({ email: result.email, id: result._id }, "test", {
expiresIn: "1h",
}); // secret = test
res.status(200).json({ result, token });
} catch (error) {
res.status(500).json({ message: error.message });
}
};
then I'm trying to access the above token in my middleware.js file.
import jwt from "jsonwebtoken";
const authMiddleware = async (req, res, next) => {
try {
const res = req.headers.authorization; // always undefine
// const res = //tryed this way also but undefine
// req.body.token || req.query.token || req.headers["authorization"];
const token = res.split(" ")[1];
const isCustomeAuth = token.lenght < 500;
let decodedData;
if (token && isCustomeAuth) {
decodedData = jwt.verify(token, "test");
req.userId = decodedData.id;
} else {
decodedData = jwt.decode(token);
req.userId = decodedData.sub;
}
next();
} catch (error) {
console.log(error);
}
};
export default authMiddleware;
'res' always undefine. Please help me to access the token in middleware as I'm trying this for days and got nowhere
In the client-side API file, I added an API interceptor. Then it got fixed
API.interceptors.request.use((req) => {
if (localStorage.getItem("profile")) {
req.headers.Authorization = `Bearer ${
JSON.parse(localStorage.getItem("profile")).token
}`;
}
}
Now I can access the req.header.authorization and use the split function.

JWT Authentication Express Js and React Js for specific user email

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

Why do I get an authentication error when I pass an axios config object with an authorization token? MERN

I am making a small social network application. I came up with a like post route and the user has to be logged in to be able to perform that action, and I have auth middleware that looks like this:
const auth = async (req, res, next) => {
// check header
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer')) {
throw new UnauthenticatedError('Authentication invalid');
}
const token = authHeader.split(' ')[1];
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
req.user = { userId: payload.userId };
next();
} catch (error) {
throw new UnauthenticatedError('Authentication invalid');
}
};
When I pass an object with an authorization token to Axios, I get an error, here is the action.js file:
export const likePost = id => async (dispatch, getState) => {
try {
const {
userLogin: { userInfo },
} = getState();
const config = {
headers: {
Authorization: `Bearer ${userInfo.token}`,
},
};
const { data } = await axios.put(`/api/v1/post/${id}/like`, config);
dispatch({ type: POST_LIKE_SUCCESS, payload: data });
} catch (error) {
console.log(error);
dispatch({
type: POST_LIKE_FAIL,
payload: { msg: error.response.data.msg },
});
}
};
When I open the network tab in the browser, the request tab shows that I forward the headers {Authorization: Bearer .... token} and in response I get error 401.
I'm no expert with Axios, but according to the documentation, the put method uses data as the second argument and config as the third.
Maybe try to provide an empty value like null or an empty string for the second argument:
const { data } = await axios.put(`/api/v1/post/${id}/like`, null, config);

[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