- Lab
- Core Tech

Guided: Managing Authentication and State in Next.js 14
Welcome to the Guided: Managing Authentication and State in Next.js 14 code lab, an essential resource to master authentication and state management in Next.js. By the end of this lab, you will have the skills to implement user authentication using Next.js, secure your application with protected routes, and manage global state using Redux Toolkit. This will empower you to build scalable, secure, and efficient Next.js applications.

Path Info
Table of Contents
-
Challenge
Introduction and Setup
Next.js Recap
Next.js is a popular React framework that enables developers to build fast, server-rendered web applications. It offers features like static site generation (SSG) and server-side rendering (SSR), which improve performance.
Next.js also includes automatic code splitting, routing, API routes, and optimized image handling.
Routing is a key feature in Next.js applications where users can navigate through different pages and perform various actions within the application. Applications' Authentication
Authentication for applications is the process of verifying a user's identity to grant access to secure features or data. It typically involves users providing credentials, like a username and password, which are then validated against stored records. Once authenticated, users are issued a session or token, allowing them to access restricted areas within the app securely.
State Management
State management in applications involves tracking and managing the data (or "state") that the app uses across different components. In relation to authentication, state management is crucial for storing and updating user authentication status, session information, and access permissions.
Having effective state management in your applications, ensures that the app consistently knows whether a user is logged in and adjusts the accessible content or actions based on their authentication state. The current source code includes the initial next.js react application for a task management system.
In the terminal window run the application using the following commands then refresh the browser to navigate through the initial application:
npm run dev
If you get stuck in any of the lab's tasks, check the
Solutions
folder. -
Challenge
Implementing User Authentication
Implementing user authentication is not only important for security, it is also important for personalizing the user experience, protecting sensitive data, and ensuring that only authorized users can access specific features or resources within the application. User Flow
The expected flow for user interaction within the application is as follows:
- If the user is not logged in, they are redirected to the login page.
- The user enters their username and password.
- Upon successful authentication, the user gains access to the task management page.
- When the user logs out, they are redirected back to the login page and cannot access the task management page until they log in again.
Authentication Flow
To validate and complete the user login process, the credentials entered by the user are sent to an API, which is the server-side backend code in Next.js responsible for handling authentication. This API verifies the credentials and, upon successful validation, completes the login process.
The
next-auth
library is used to streamline this process by managing the authentication flow, including credential validation, session handling, and secure token generation. Withnext-auth
, you can easily implement various authentication strategies, such as OAuth providers, email verification, and custom credential checks, ensuring a secure and efficient user login experience. -
Challenge
Implementing Protected Routes
Currently, the task manager is accessible to anyone. To restrict access to authenticated users only, you need to implement protected routes. These routes act as middleware for each page request, first checking if the user is logged in. If the user is authenticated, they are allowed to access the page; otherwise, they are redirected to the login page.
By implementing protected routes, you ensure that only authorized users can view and interact with sensitive parts of the application.
In this case, the home page, which contains the task manager, should be moved to a protected page called "dashboard." The home page should then be configured to check if the user is authenticated; if they are, it routes them to the dashboard, and if not, it redirects them to the login page.
-
Challenge
Integrating Redux for State Management
Local State Management
- Scope: Local state is limited to individual components or component trees, making it suitable for managing small-scale session information directly within specific parts of the application.
- Usage: Local state is managed with React’s
useState
oruseReducer
hooks and is ideal for handling simple session checks or temporary data within isolated components. - Pros: Lightweight and quick to set up; avoids the complexity of external libraries.
- Cons: Difficult to manage across multiple components or large applications, especially if session information needs to be shared globally.
Redux
- Scope: Redux provides a centralized store, allowing global access to the authentication state across the entire application.
- Usage: Redux is ideal for handling more complex authentication flows, especially when different parts of the app need access to user or session data (e.g., user roles or permissions).
- Pros: Centralizes session data, making it easier to handle complex authentication flows, role-based access, and protected routes consistently across the app.
- Cons: Adds extra setup and boilerplate code. Requires knowledge of Redux and, potentially, middleware for side effects (e.g.,
redux-thunk
orredux-saga
).
Summary
- Local State is best suited for simpler applications or isolated authentication checks within individual components.
- Redux is beneficial for larger applications where session state needs to be shared globally and managed consistently, providing better scalability and control over the authentication flow.
Key Redux Components
1. Store
- The store is the central place where the entire state of the application is stored.
- Created using
configureStore
(from Redux Toolkit) orcreateStore
(from core Redux), it holds the application’s state tree and allows components to read state or dispatch actions. - Only one store should exist per application, serving as the "single source of truth."
2. Reducer
- A reducer is a pure function that takes the current state and an action as arguments, then returns a new state based on the action type.
- Reducers define how the state changes in response to actions and are combined in the store to manage the overall state.
- Each reducer manages a specific part of the state, like
tasksReducer
for tasks orauthReducer
for authentication.
3. Action
- An action is a plain JavaScript object with a
type
property (and, optionally,payload
) that describes a change in state. - Actions are dispatched to trigger state changes; for example,
{ type: "ADD_TASK", payload: { id: 1, text: "New Task" } }
. - The action
type
tells reducers what kind of update to make, while thepayload
provides necessary data.
4. Action Creator
- An action creator is a function that returns an action object, often used to simplify and standardize action dispatching.
- For example:
const addTask = (task) => ({ type: "ADD_TASK", payload: task });
5. Dispatch
- The dispatch function sends actions to the Redux store, where they are handled by reducers to update the state.
- Components use
dispatch
to trigger state changes, e.g.,dispatch(addTask(newTask))
.
6. Selector
- A selector is a function that extracts specific data from the store's state.
- Selectors simplify accessing nested or derived state, especially when multiple components need similar data.
- Example:
const selectTasks = (state) => state.tasks
.
These components together form the Redux flow: actions are dispatched, reducers handle them to update the store, and selectors retrieve the updated state for components.
-
Challenge
Implementing Protected Task Management Application Features
The current available actions for the task manager are: add, toggle complete and delete.
These actions need to use the new global Redux state management instead of react hooks.
-
Challenge
Conclusion and Best Practices
Concepts Covered:
- Authentication with NextAuth.js: Implementing secure and user-friendly authentication in Next.js.
- Route Protection using RequireAuth: Protecting routes to restrict access to authenticated users.
- Global State Management with Redux Toolkit: Efficiently managing application-wide state. ### Best Practices
-
Use a Centralized Authentication State:
- Store authentication data (e.g.,
isAuthenticated
,user
info) in Redux to maintain a single source of truth across the application. This prevents inconsistencies and makes the auth state easily accessible from any part of the app.
- Store authentication data (e.g.,
-
Leverage NextAuth.js with Redux:
- Use NextAuth.js to handle backend authentication (login, logout, session management) and sync the authentication state with Redux for a smooth user experience.
- Utilize NextAuth.js session callbacks to update Redux or handle client-side redirects with Redux actions.
-
Protect Routes with Higher-Order Components:
- Wrap and protect routes with a component like
RequireAuth
for any page that requires authentication. This approach keeps your code clean and makes protected routes easy to manage.
- Wrap and protect routes with a component like
-
Dispatch Actions After Login/Logout:
- Dispatch Redux actions to update the authentication state after login and logout. For instance, use
dispatch(login())
upon successful login anddispatch(logout())
on logout to synchronize Redux with NextAuth.
- Dispatch Redux actions to update the authentication state after login and logout. For instance, use
-
Error Handling and User Feedback:
- Provide user feedback for login errors or loading states (e.g., show a loading spinner during login or display error messages for incorrect credentials).
Conclusion
Using NextAuth.js and Redux together in Next.js provides a robust and scalable solution for managing authentication. NextAuth.js handles backend authentication, while Redux maintains a centralized store for authentication state, making it easily accessible throughout the application.
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.