Persist State from JWT Stored in Cookie


Updated Apr 27th, 2022

Option#1: Use Next JS middleware to check for existing token, verify token, handle redirects if necessary AND send payload to the client. Unfortunately I don’t think the AND part of option #1 is possible. This is what I’ve been racking my brain about.

Note: A bonus for middleware is it allows you to static site while still using auth.

Option #2a: Each page has an on page load useEffect to ping an “/api/user” endpoint that returns jwt payload if applicable. Alternatively could do this in a layout wrapper.

Option #2b: in the layout component, or another comp at the top of the component tree, on page load inside of a “useEffect,” hit a “/api/getUser” endpoint that verifies there is a token, verifies that it is valid, and returns the payload which can then be used to update global state. But since this runs on the client get ready for some flashing.

Option #3: Have any protected page have a getServerSideProps function that takes a look at the req.cookies and returns the jwt payload as props. Seems like a lot of code repetition but shouldn’t be any flashing.

Option #4: Use the jwt to set another “non-http-Only” cookie and load this into state in GSSP. Could do this cookie set in the middleware function.

Option #4a: He literally says because local storage will not work with SSR. This is the same message I got. Note: you can do a work-around to make sure it is on the client but you end up with the dreaded flashing state. He uses “getInitialProps” (June 2019). He sets a (non-http-Only) cookie from the browser whenever a piece a state changes. Detects changes with “useEffect.”

import Cookie from "js-cookie"

See the code here.

Middleware Sending Payload

Note: option two seems so similar to what the middleware function does. It seems that the middleware function is going to already be verifying the token should be able to forward the payload. But the req/res is only accessible from server side code. Is the middleware function just about protecting the page? I guess so. It still saves a lot of getServerSide props code to make sure the user is authenticated.

A thread here along my same lines.

So Many Options

Next middleware seems cool but can’t send the payload to the front end. After verifying the token from the “httpOnly” cookie we could set another “non-httpOnly” cookie with the user’s name. We would reset this cookie over and over and over.

We could fetch the user data from an internal “/api/getUser” api route that verifies the token and returns payload but this results in flashing and pinging our own API is redundant when there is GSSP.

We can use GSSP to return payload as props but we have to do this in very page. No flashing is good although tough to tell how this will truly affect speed of app.

We could use still use the browser’s “LocalStorage” to store non-sensitive information like the user’s username. We can get the payload as part of the “api/registration” and “api/login” routes and store that. When we log out we can clear LocalStorage.

The Header

A case to be made for user data being stored in global state is the header component. The “getServerSideProps” functions only work in “/pages” components. If you want to conditionally show buttons based on a user’s login status then you need that global state.

Reduce Flashing

Not related to only this post but I do not like the flashing that comes with client side data fetching. I feel like I would rather use Server-Side rendering on every request even if it is slower because I dislike the flashing that much. It’s also tough to tell the speed without deploying.

Revisit Local Storage

At this point it’s clear that you should not store JSON web token in local storage but what about basic user data like username (and ID) and logged in boolean?

If every page is going through authorization check on the server anyway. If you change a logged in bullion to true you don’t get access to secure parts of the app. It’s more for UI purposes.

This makes managing global user states so much easier. You can set the user and logged in at the time of these actions and leave it there for eternity or until the user logs out.

Bang Head Here

After experimenting with using Next middleware to authenticate and local storage to persist a username and a logged in boolean variable, I realize neither of these may be the final approach. The middleware functions cannot return the payload to the client that’s been established. Local storage cannot be accessed on the server (throws a warning if you try to access for initial state) and so the initial state needs to be false or empty and updated with a useeffect which results in some flashing. If you’re going to use get server side props to avoid flashing and get the verified JSON web token payload to the front end, You might as well just use ONLY use get server-side props?!