I am adding JWT authentication to a blog app I'm working on. On the server side (built with Nodejs) I am creating the token and sending it back with a successful login. On the client side I am saving the token in LocalStorage. When I log in and check the application tab in dev tools I can see the token. On the server route where blogs are posted to I check authentication. If the token is authenticated the blog posts to the database, but if I delete the token or change it and then make the post request the request fails, as expected.
So far so good.
What I'm confused about is how to restrict access to the page where the blog editor resides on the client. If people aren't authenticated they should not be able to access this page at all, even though if not authenticated they can't post anyway.
Login route on server:
router.post('/login', async (req, res, next) => {
const cursor = User.collection.find({username: req.body.username}, {username: 1, _id: 1, password: 1});
if(!(await cursor.hasNext())) {
return res.status(401).json({ message: 'Cannot find user with that username' });
}
const user = await cursor.next();
try {
if(await bcrypt.compare(req.body.password, user.password)) {
const token = jwt.sign({
email: user.email,
userId: user._id
}, process.env.JWT_SECRET, { expiresIn: "1h" })
return res.status(201).json({
message: 'User Authenticated',
token: token
});
} else {
return res.status(400).json({
authenticated: false,
username: req.body.username,
password: req.body.password
})
}
} catch (err) {
return res.status(500).json({ message: err })
}
});
How I'm checking the token authentication on the server:
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization;
console.log(token);
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.userData = decoded;
next();
} catch (error) {
return res.status(401).json({ message: 'Auth Failed' })
}
}
My client side login route fetch:
handleSubmit(event) {
event.preventDefault();
const formData = {
username: event.target.username.value,
password: event.target.password.value
}
fetch('http://localhost:4000/user/login', {
method: "POST",
mode: "cors",
body: JSON.stringify(formData),
headers: {
"Content-Type": "application/json"
}
})
.then(res => res.json())
.then(res => {
localStorage.setItem('authorization', res.token);
console.log(res);
})
.catch(err => console.error(err))
}
And here is my fetch call from the client on the blog posting route where the editor resides:
handleSubmit = (event) => {
event.preventDefault();
const data = new FormData(event.target);
const body = event.target.postBody.value;
const postTitle = event.target.title.value;
console.log(event.target);
console.log(data);
console.log(event.target.postBody.value);
fetch('http://localhost:4000/blog', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
"Authorization": localStorage.getItem('authorization')
},
mode: 'cors',
body: JSON.stringify({
title: postTitle,
postBody: body
})
})
.then(res => res.json())
.then(err => console.error(err))
}
So, like I said, everything is working as expected but I don't want people to be able to access the editor page if they are not authenticated. I guess I would check to see if the token exists in localstorage and then redirect? But wouldn't I also need to check to see if the token on the client can be authenticated on the server as well? So would I essentially need to post to the server to do the check whenever someone navigates to that page, or any other page I want to restrict access to? Come to think of it, if a user is already authenticated I don't want them to be able to access the login page either.
I have heard that people use Redux to manage state across components, but I really don't want to go down that road, at least not yet because this project is for learning purposes and I don't really want to start with Redux or anything else like that until I have a better grasp of React on it's own. I don't know if I need Redux or not and from what I understand, that's enough to know that I probably don't need it.
This is just such a different flow than I'm used to from PHP sessions and I'm having some trouble wrapping my head around it.
I realize that you folks may not really need to see all this code, but I also would like some more experienced eyes to see it and point out anywhere I might be making mistakes or where I could improve here.