✏ Secure Authorisation
Three posts ago, I thought authorisation is mainly a backend task.
LITTLE DID I KNOW.
The implementation on the frontend is just as important, and often it's done wrong, especially in tutorial videos. Many use localStorage, which is the most insecure way to authorise a user.
Frontend authorisation with cookies or local storage is perfectly fine for an application where you only store something meaningless like a nickname and a highscore. But whenever actual data like email addresses and passwords are involved, you really want to make sure that you don't expose your users to attacks.
If your app allows that evilCoder9 can easily get access to a user's personal data, you're making them vulnerable to harmful attacks - just because they were unlucky enough to sign up to your fun Game app with their real email address.
I've read a lot about this topic in the past days, and here's what I think is one of the best strategies to keep your app and users safe:
✏ Authorisation with JWT and httpOnly Cookies
You'll have two types of tokens:
- an access JWT that expires after ~ 10-15 minutes (sent in the response body)
- a refresh JWT that expires after ~ 3 days (sent as httpOnly cookie)
The access token is the one that gives access to protected routes. The refresh token is used to request a new access token.
Login and Registration
Do not save this token to local storage, instead save it in memory (in a variable) and use that variable to set the request authorization headers in subsequent requests, to access protected routes.
Even if evilCoder9 manages to steal that token, it'll expire very fast. Your frontend obviously has the same problem, the user will be automatically logged out after 10-15 minutes (and immediately if they refresh the page), which is not exactly a great UI.
To solve the first problem, the frontend will periodically make a silent refresh, right before the access token expires. The user won't even notice. But your app will send a request to a refresh route in the background, which will
- check the presence and validity of a refresh cookie/token (if none is present, the user is redirected to the login view)
- respond with a new access token to keep the user logged in while they navigate on the site
Handling page reload
The solution is very similar in this case. Whenever the app mounts, it'll send a request to the refresh route. Since the cookie is still there, the server can send a new access token. If the user hasn't visited your page in a few days, the refresh token will have expired, too, and they'll have to log in again. However, that's an acceptable price to pay.
If the user logs out, the frontend will delete the data/access token it has stored in state, and with a GET request to
/logout, the server will remove the refresh cookie.
That's the principle in a nutshell. In the next blog, I'll continue with part 4/4 of my "How to add a Login Function to your App" mini series, demonstrating how to implement all of the above in a real React application.
This post covered:
- never use local storage
- principles of secure authorisation (short-lived access JWT and httpOnly cookie refresh JWT)
✏ Thanks for reading!
I do my best to thoroughly research the things I learn, but if you find any errors or have additions, please leave a comment below, or @ me on Twitter. If you liked this post, I invite you to subsribe to my newsletter. Until next time 👋
✏ Previous Posts
You can find an overview of all previous posts with tags and tag search here: