0. Project Structure
There will be two folders. One is
server and the other is
app. The server one will contain an Express app, while the other will contain the Nextjs app. Both will run on their own ports. App/Client on 3000, and Express/Api on 3001.
1. Server side
Lets first build the server side. Start first with a standard Express.js app. Create it in the
Now we add JWT support. I’m using the
Do note, to keep yout secrets secure in a .env file and never push them to git. This is just an example.
I define a custom middleware code that will check the header
Authorization if a value is present, validate it and then decide wether to continue with the request or block it and return 401 Not Authorized back. The api route for login does not need to be verified as that route will not have the header set.
Finally let’s add some routes that the client can call.
The login route will create a fixed JWT token, expiration set to 1min.
The token ping route, we need this one for the Next.js SSR. Next.js code while doing SSR it has to verify the token somehow otherwise it will render the “secret” page (non-auth users can’t access), show it to the user, and then again render another page on the client side (e.g. login) once it figures out that the user should not see the secret one. So while doing the token verification on the SSR of Nextjs live cycle, the app can render the correct page to the user from the start. Speaking of Next.js…
2. Client side
Create a Nextjs app like you would normally in the
appfolder. Now make a
pages folder and add the following two:
The index page has a button. When you click on it, it will call the
api/login endpoint, which returns a token. The token is then stored in a cookie. I also store it in state, just to display it on the page, for demo purposes.
Index also contains a link to a Secret page. Looks like this:
This secret page can only be viewed/accessed by if a valid token is present.
We can check this in the function
handleAuthSSR does all the checking. Based on the context it determines wether we are server-side or client-side, then gets the token value. Remember, while we are server-side we don’t have access to cookies or localStorage and other browser related stuff, only the request and what was send with it.
Based on the response from
api/token/ping, we either do nothing, or we redirect back to login in case of an error. Now, based on if we are server-side or client-side we must redirect the correct way.
And that’s it, fell free to clone the repo and run the example yourself if that helps you better understand.
Whole code can be found at: https://github.com/zprima/jwt-cors-app