NextJS + JWT Authentication
In this article, we will discuss how we can integrate Redux store with our NextJS
app by covering simple JWT auth and we will do all that without compromising on SSR.
Disclaimer: This is strictly a hands-on guide targeted towards developers with an intermediate to advanced skill set in NextJS.
Here's a list of the concepts that will be covered in this blog:
- Structuring domain logic in Redux.
- Integrating Redux store with NextJS.
- Rebuilding state on page changes.
- Persisting state in cookies without sacrificing SSR.
You can find the complete code at:
https://github.com/muzzamilr/Nextjs-auth-blog
and the deployed application at
https://nextjs-auth-blog-4e61wgtb0-muzzamilraza-carbonteqco.vercel.app.
Let's Code!
A Next app can be created with the following shell command
npx create-next-app@latest
The development server for the app can be started by running the following script.
npm run dev
The dev server can now be accessed at http://localhost:3000.
Looking towards the folder structure, base app includes the following directories
pages
public
styles
Pages directory includes the api
folder which you can use to create your REST API with Next
.
Now you need to create the following directories in the root directory of the project
components
store
Dependencies
Our dependency list for this example application is very brief. We'll be using the following dependencies.
- ReduxToolkit (for app state management)
- JWT (for easy auth)
- Axios (the HTTP client)
- cookies-next
Let's install them after creating the directories.
npm install @reduxjs/toolkit \
react-redux jsonwebtoken axios cookies-next
Authentication State Setup
Redux toolkit massively simplifies the usage of Redux. It comes recommended as the standard way to write Redux logic. It removes a lot of boilerplate that used to be necessary to get Redux up and running and it integrates very well with React's functional components as it provides a few very useful hooks out of the box.
A slice in Redux is exactly what its name suggests: a slice of the globally accessible Redux store that is used to share application logic across all components of the application.
In our case, we will create an authentication slice that will contain the shared logic needed for authentication purposes in the application. By convention each slice is put into a separate directory so we'll create an auth directory for the authentication slice that will house our slice's logic. The logic here will be encapsulated inside a file named auth-slice.js.
store/auth/auth-slice.js
As you can see, this file makes use of the createSlice
function provided by Redux toolkit. We provide this function with an object that contains the name of our slice, its initial state and its reducers. Reducers are functions that transform our initial state into our desired state. You can see that we have directly mutated our initial state in these reducers but bear in mind that this is not what actually happens, the updates to the state are always applied immutably but all of that gets handled by redux-toolkit
itself. This saves us from having to write a lot of boilerplate code.
The initial state of the slice is declared dynamically by checking for a jwt cookie, if that exists we'll assume that the user is logged in and vice versa.
Now we need to create a file called action-creators.js
in the auth
directory. These functions will be used to dispatch reducers which update the state of our application.
store/auth/action-creators.js
Now, we need to configure our store and create a file named store.js
in store
directory.
store/store.js
In the _app.js
file we will add the following code to make the store globally accessible to each of our application's components.
NextJS App Setup
pages/_app.js
We've also added a link to bootstrap's CDN as we'll be using it for this app's styling.
pages/index.js
This is the /
route of the Next.JS application. In this code we are using server side rendering to verify the JWT token and if the token is not verified then router pushes to the signin page. getServerSideProps
is invoked before the rendering of component and returns the props which are further used by component.
Authentication Workflow in NextJS
For the login page we will create a file named signin.js
in the pages
directory. NextJS automatically adds routes for pages that correspond to each file in this directory.
We have a simple login page for this app, it looks like this:
pages/signin.js
So in this page, we check from our redux store if the user is already logged in or not. If they are logged in, they get redirected to the home page of our app otherwise they can put their credentials in the form provided by the Login component.
A sign in request is sent to the server once the user submits their credentials. The server responds to the request with a JWT (JSON web token) that we store in a client-side cookie. This provides us with a mechanism to keep the user logged in. We'll discuss it in more detail in our section concerning the server-side code.
The login component provides a simple form where the user may enter their credentials.
components/login.js
Express JWT Middleware
api/auth.js
In this file, we have created a request handler, which takes a user information as request body and jwt.sign method is used to generate a token. The token is generated everytime when user logs in to the app. If the request method is not appropriate then it will return unauthorised status.
Running the App
After successfully completing all these steps you will need to create a .env
file and set the JWT_KEY
there instead of hardcoding it in the application. The NEXT_PUBLIC_
prefix allows the app to access these variables at the run time instead of just the build time.
NEXT_PUBLIC_JWT_KEY = <Your Key>
Now you can run the app in development server using command
npm run dev
In order to run this app in production environment you can use the following commands
npm run build
npm run start
Resources
Resources for further exploration of Redux toolkit and JSON Web Tokens: