Reputation: 174
I'll try to keep this as quick and simple as possible while providing enough information:
I've been working to create a clean, best-practice, most secure possible user authentication system, incorporating role-based users (isAdmin User schema bool) to conditionally determine accessible routes (Protected Routes) and which components to render.
As it stands, when a user authenticates, I set the local storage with the JWT received from the back-end and then set the user state (redux toolkit) based on the JWT in the local storage, verifying the token and payload are valid before setting the state with the user data (isAdmin value).
Every time the App component updates (useEffect) it re-sets the User state in redux using the local storage JWT, verifying it each time.
My goal was to avoid the possibility of people changing the react state on the client-side, thus rendering certain components which should be inaccessible to their user role.
The problem is not only do I believe it's still possible to update the client-side and see the components which shouldn't technically be accessible (partially mitigated by back-end validation), but also the issue of using three separate JWTs which I'm not fond of.
I use an Access Token with a short-lived (15s) expiration stored in the local storage to make authenticated requests by attaching it to the header of api calls within an axios interceptor.
I use an HTTP-Only Refresh Token with an hour expiration (this one determines how often user has to sign-in) to update the Access Token once it's expired which I check for in the interceptor as well, returning both a new Access and Refresh Token.
Finally, I use a User Token similarly to the Access Token, stored in the local storage, however, with a longer expiration (matching Refresh Token; auth period) so I can access and verify the user data payload to set the User app state in redux with verified user data (including isAdmin flag).
I haven't provided code snippets because everything works as written, but conceptually and logically I think improvements can be made and I'd love to hear your thoughts.
Thank you!
Upvotes: 1
Views: 2385
Reputation: 12342
remember that StackOverflow is about solving concrete problems with development, so you might have moderators close your question (just a heads-up).
That said, here are my comments.
You've put these two pieces of information together:
most secure possible user authentication system
and
when a user authenticates, I set the local storage with the JWT received from the back-end
Saving tokens to local storage is not considered secure. In fact, as of April 2022, there is no secure way of keeping tokens in the browser. Current best practice recommends avoiding handling tokens in the browser at all. Instead, you should use secure HTTP-only cookies and backend components that handle these cookies and keep tokens secure. Have a look at the Token Handler pattern that we've described in Curity. We've also provided some ready-to-use components that you can just plug into your app. The pattern does add some more complexity but strengthens the security of your app.
The problem is not only do I believe it's still possible to update the client-side and see the components which shouldn't technically be accessible (partially mitigated by back-end validation)
That's true. No matter what you do in the fronted code, it will always be possible to tweak that code and get to things that, in theory, shouldn't be accessible to a given user. That's why you always need to call your backends and let the backend verify the request and return the data or views to the browser. You can hide an "edit" link from regular users and show it only to administrators, but you must validate on the backend that it was indeed an administrator who has clicked it.
All in all, I think you shouldn't be considered that much with the frontend code but with the backend code. People will always be able to abuse your frontend code and get to parts of your app that you didn't want them to see. It's your backend's responsibility to stop any requests that come from unauthorized users, and this is where you should focus. Also, you should make sure that tokens are kept secure. If an attacker manages to steal an administrator's token from the local storage, she will be able to do anything that that administrator could.
Upvotes: 2