In Next.js 13, authentication and authorization are crucial components of building robust and secure applications. You can use the built-in `next/auth` package to handle authentication.
Next.js 13 introduces a new authentication flow that's more streamlined and easier to use. This new flow is based on the `next/auth` package, which provides a simple and secure way to authenticate users.
To get started with authentication in Next.js 13, you'll need to install the `next/auth` package using npm or yarn. This will give you access to a range of authentication features and tools.
Getting Started
To get started with Next.js 13, you'll want to set up your project with the right stack. Let's bring Next.js as the main character of the project, and use Tailwind CSS for the style.
Create a new Next.js app using the create-next-app or create-next-app@latest command-line tool, followed by the name of your choice for the app. This will create a new Next.js app with the chosen name, and install all the required dependencies for this project.
Visit Hanko Cloud and create an account to manage your Hanko project. Then, create a new project and set the App URL to your development URL, such as http://localhost:3000.
Tutorial
In the Next.js API, you'll find several routes and endpoints that help you manage users and authentication. The API contains routes for authenticating and registering users, as well as retrieving and updating user information.
To authenticate a user, you can use the `/api/users/authenticate` or `/api/account/login` route, both of which are public and accept a POST request with a username and password. This will generate a JWT token that you can use to access secure routes.
Secure routes in the Next.js API, such as `/api/users`, `/api/users/[id]`, `/api/users/[id]`, `/api/users/[id]`, and `/api/users/current`, require a valid JWT token in the HTTP Authorization header of the request.
You can use the `/api/users` route to retrieve all users, or the `/api/users/[id]` route to retrieve a specific user by their ID. The `/api/users/[id]` route also allows you to update a user, and the `/api/users/[id]` route allows you to delete a user.
Here are the main routes and endpoints in the Next.js API:
- /api/users/authenticate - POST - public route for authenticating username and password and generating a JWT token.
- /api/users/register - POST - public route for registering a new user with the Next.js app.
- /api/users - GET - secure route that returns all users.
- /api/users/[id] - GET - secure route that returns the user with the specified id.
- /api/users/[id] - PUT - secure route for updating a user.
- /api/users/[id] - DELETE - secure route for deleting a user.
- /api/account/login - POST - public route for authenticating username and password and generating a JWT token that is returned in an HTTP only authorization cookie.
- /api/account/logout - POST - public route for logging out that deletes the HTTP only authorization cookie.
- /api/account/register - POST - public route for registering a new user with the Next.js app.
- /api/users - POST - secure route for creating a new user.
- /api/users/current - GET - secure route that returns the current logged in user.
Setup
Let's get started by setting up our project. To create a new Next.js app, we can use the create-next-app or create-next-app@latest command-line tool followed by the name of our choice for the app.
The project configuration options will ask us some prompts on what we will use for the app, which should look something like this: The above choices will create a new Next.js app with the chosen name, all the required dependencies for this project will also be installed.
Next, we'll use Prisma to handle the storage, which is a crucial part of our project.
We'll also use Hanko for the login and registration, user management, and logout, which is a great tool for managing user authentication.
Authentication
Authentication is handled through a global JWT middleware in Next.js API, which is applied to all secure routes. This middleware ensures that all requests to these routes include a valid JWT token in the HTTP Authorization header.
The JWT token is generated on successful authentication using the jsonwebtoken npm package, and is digitally signed with a secret key stored in next.config.js to prevent tampering. The token is then returned to the client application and must be included in subsequent requests to secure routes.
You can use the usersRepo.authenticate() function to authenticate user credentials, as seen in the login route handler. This function validates the user credentials against the user data stored in MySQL, and returns a JWT token if the credentials are valid.
MySQL Auth Overview
A full-stack Next.js application is used in the example, combining a React front-end with a Next.js back-end.
The users repo is a crucial component of the example, encapsulating all access to user data stored in MySQL, and exposing methods for authentication and standard CRUD operations.
On successful authentication, a JWT token is generated using the jsonwebtoken npm package, which is then digitally signed with a secret key stored in next.config.js.
The JWT token is returned to the client application, which must include it in the HTTP Authorization header of subsequent requests to secure routes.
The fetch wrapper in the tutorial app handles this requirement, ensuring that the JWT token is properly included in every request.
MongoDB Auth Overview
Authentication is a crucial aspect of any web application, and MongoDB provides a robust way to handle authentication in your Next.js application. This overview will cover the basics of MongoDB authentication in a Next.js application.
Next.js applications can be organized into separate folders for better organization and maintainability. In the example Next.js + MongoDB Auth App, the project is organized into several folders, including `_components`, `_helpers`, and `_services`.
These private folders are ignored by the Next.js routing system, allowing you to keep your code organized without affecting the application's routing.
The `_services` folder contains services that encapsulate client-side logic and handle HTTP communication between the React front-end app and the Next.js back-end API. Each service is responsible for a specific content type, such as user authentication.
Here's a breakdown of the services found in the `_services` folder:
- User service: handles user authentication and CRUD operations.
- Alert service: displays and clears alerts.
The example application also includes a Secure Layout that automatically redirects unauthenticated users to the login page, making it easy to implement authentication in your Next.js application.
Login Form
The login form is a crucial part of the authentication process in our Next.js application. It's built with the React Hook Form library, which makes it easy to create and validate forms.
The login form contains two main fields: username and password. These fields are registered with the React Hook Form by calling the register function with the field name from each input element.
When the form is submitted and valid, the onSubmit function gets called. This function submits the user credentials to the API by calling userService.login(). The returned JSX template contains the form with all of the input fields and validation messages. The form fields are bound to each input element with the JS spread operator (e.g. {...fields.username}).
User Interface
In a Next.js 13 tutorial, building the user interface is a crucial step. The login page is where the Hanko-auth component will play its part in handling authentication.
To style the user interface, we'll use Tailwind CSS classes to create a centered container to display the todos. The todo page will feature a form with an input to create new todos.
The todo page will have a checkbox and a delete button for each todo element. This can be achieved by creating a new todo folder with a page.tsx file inside it, where we'll use the code provided in the tutorial.
User Interface
The user interface is where the magic happens. It's the first thing users interact with when they visit your app, and it sets the tone for their entire experience.
The login page is where users start their journey, and it's where the Hanko-auth component takes over to handle authentication.
A centered container is used to display todos on the todo page, which is styled using Tailwind CSS classes.
You'll also need a form with an input to create new todos, and each todo element will have a checkbox and a delete button.
Here are the key components of the todo page:
- Login page with Hanko-auth component
- Todo page with centered container, form, and todo elements
To manage user logouts, you'll create a logout button component using the @teamhanko/hanko-elements library.
The logout button component is created in a file called components/Logout.tsx.
Registration Form
The registration form is a crucial part of any user interface, and it's great to see how it's implemented in the Next.js tutorial app. The register page contains a simple registration form built with the React Hook Form library.
This form has fields for first name, last name, username, and password, making it easy for users to sign up. The fields are well-designed and user-friendly.
The onSubmit function gets called when the form is submitted and valid, and it's what makes the registration process seamless. It submits the form data to the Next.js API by calling the userService.register() function.
This API route handler supports HTTP POST requests containing user details, which are then registered in the Next.js tutorial app by the register() function. On successful registration, a 200 OK response is returned with an empty JSON object.
The registration form is built with the React Hook Form library, which makes it easy to manage form data and validate user input. The library provides a simple and efficient way to handle form submissions.
Loading Indicators
Loading indicators are a crucial part of creating a seamless user experience.
You can display a loading indicator while navigating between pages by using the loading.tsx file, which can be defined in every directory.
This file allows you to add any component you want to display while the page is loading, such as a top bar loader or a loading spinner.
Testing the loading file is essential to ensure its functionality.
Let's create a test file inside the same folder with the following code, which will display the UI from the loading.js file for a split second before the breaking component gets mounted.
Upon clicking a link, you'll see the loading indicator in action, giving users a clear idea of what's happening behind the scenes.
Global Css
Global CSS is a crucial part of any Next.js app, and it's used to define custom styles that can be applied across the entire application.
The globals.css file is where you'll find these global custom CSS styles, which are then imported into the tutorial app by the root layout component.
This approach allows you to define a consistent visual identity for your app, making it easier to manage and maintain.
Security and Redirecting
To secure your Next.js application, you can add paths to be protected in the Middleware configuration. This involves copying code to the bottom of your middleware.tsx file.
You can also redirect users to the login page if they're not authenticated by using the layout component to check the auth status. This is a common approach to ensure only authorized users can access private user data.
In API routes, you can use the redirect function imported from next/navigation to redirect users. For example, a valid server action can be created using this function.
Securing and Redirecting
To secure your application and prevent unauthorized users from accessing private user data, you can add protected paths to your Middleware configuration. This is done by copying the code from the Next.js Docs into your middleware.tsx file.
The JWT middleware uses the express-jwt library to validate JWT tokens in requests sent to protected API routes. If the token is invalid, an error is thrown, causing the global error handler to return a 401 Unauthorized response.
You can verify the JWT token by obtaining the token from the “hanko” cookie and calling the createRemoteJWKSet function from jose to get the JSON Web Key Set (JWKS). If the token can be verified, the promise resolves to a decoded token.
To redirect users to the login page if they are not authenticated, you can use the redirect function imported from next/navigation in API routes. This is useful for secure API routes, where you want to ensure that only authorized users can access the data.
Here are some common use cases for redirecting in Next.js:
The JWT middleware is added to the Next.js request pipeline in the API handler wrapper function, and it verifies the auth token cookie for secure API routes. You can also use the validate middleware to validate the JSON request body against a schema, if provided. Validation schemas are created with the Joi library and attached as a .schema property to route handler functions where required.
Alert Method Parameters
When working with alerts in your application, it's essential to understand the parameters involved in displaying them.
The alert method parameters are crucial in determining how and when an alert is displayed.
The first parameter is the alert message string.
This is the core of the alert, the message that will be displayed to the user.
The second parameter is an optional showAfterRedirect boolean parameter.
This parameter keeps the alert displayed after one route change, such as the redirect after successful registration.
By default, this parameter is set to false, meaning the alert will not be displayed after a redirect.
Here are the alert method parameters in a quick reference format:
- Alert message string (required)
- ShowAfterRedirect boolean (optional, default is false)
Todo App
Creating a Todo App with Next.js 13 is a great way to get hands-on experience with the framework. In this section, we'll take a high-level look at the Todo App.
To get started, you'll need to initialize the Next.js app, which involves setting up the project structure. This includes creating the necessary files and directories for your app.
Here's a brief overview of the project structure:
- Let’s set it up
- Initialize the Next.js app
- Understanding the project structure
In the Todo App, you'll need to set up Prisma, which is a tool for managing your database. This will allow you to interact with your database using a simple and intuitive API.
Todo with Hanko Auth and Prisma
In a Todo app using Hanko Auth and Prisma, we need to set up the project structure. The Todo app will be built using Next.js 13 and Prisma, which will handle the database operations.
To get started, we need to initialize the Next.js app. This involves installing the necessary dependencies, including the Prisma CLI, which we'll use to set up the Prisma schema.
Once we have the Prisma schema set up, we can create the Todo model. This involves updating the Prisma schema file to include the Todo model, which will define the structure of the Todo table in the database.
We can then create the Todo table in the database using the Prisma CLI. This involves running a migration to create the table, which will be stored in the SQLite database file named dev.db.
To prevent problems when instantiating PrismaClient, it's a good practice to create a db.ts file in the root of the app and add the code to instantiate PrismaClient safely.
Here's a summary of the steps to create the Todo table in the database:
With the Todo table set up, we can start building the user interface. This involves creating the Todo page, which will display the list of todos for the user.
To display the right todos for the user, we need to link the todos to the correct user ID. This involves updating the Todo model in the Prisma schema to include a foreign key to the user ID.
We can then update the API route to get the user ID from the token and create a new todo if there is a user ID. This involves updating the api/todo/route.tsx file to include the user ID in the request.
Finally, we can update the Prisma call to fetch all the todos from the todo/page.tsx file, which will display the list of todos for the user.
In the next step, we need to handle marking a todo as completed and deleting a todo. This involves creating update and delete functions that fetch the dynamic route for the todo item.
The update and delete functions will be implemented in the components/todos/TodoItem.tsx file, which will handle the logic for updating and deleting a todo item.
We'll also need to implement security measures to prevent unauthorized access to the app. This involves setting up authentication with Hanko and securing the application with middleware.
By following these steps and implementing the necessary security measures, we can build a robust and secure Todo app using Hanko Auth and Prisma.
Adding Hanko
================================
To integrate Hanko into our Todo app, we need to install the @teamhanko/hanko-elements package. This can be done by running the code `npx create-next-app todo-app` and then installing the package using npm.
The @teamhanko/hanko-elements package provides the necessary components to integrate Hanko into our app. We can update our "Home" page and rename the function to "Login". This involves importing the register function from @teamhanko/hanko-elements and calling it with the Hanko API URL as an argument.
Here's a step-by-step guide on how to add Hanko to your Next.js app:
1. Install the @teamhanko/hanko-elements package using npm.
2. Update the "Home" page and rename the function to "Login".
3. Import the register function from @teamhanko/hanko-elements and call it with the Hanko API URL as an argument.
By following these steps, you can successfully integrate Hanko into your Todo app.
Best Practices
The new app directory in Next.js 13 offers a lot of flexibility, but with that comes the need for some best practices to keep your codebase maintainable.
You can place components in the app directory, or in a separate directory like app/components, app/lib, etc., if you want to reuse them across multiple pages.
For global components used across multiple pages, consider placing them in the app directory or a separate directory like app/components.
Code Documentation
Code documentation is a crucial aspect of any project, and it's essential to have a clear and consistent structure for it. The Next.js documentation recommends organizing your code into folders, with each folder containing related components, helpers, or services.
For example, in a Next.js + MySQL project, you might have a folder called "components" containing React components used by pages or other components. Similarly, in a Next.js + MongoDB project, you might have a folder called "_components" (marked as a Private Folder) containing React components used by pages or other components.
Having a consistent naming convention is also essential for code documentation. In both examples, the authors use underscores (_) to mark private folders, and parentheses () to mark route groups that shouldn't be included in the URL path.
Here are some key takeaways for code documentation:
- Use a consistent naming convention for folders and files.
- Mark private folders with an underscore (_) to ignore them in the Next.js routing system.
- Use parentheses () to mark route groups that shouldn't be included in the URL path.
By following these best practices, you can ensure that your code is well-organized, easy to understand, and maintainable for yourself and others.
Best Practices
In Next.js 13, the new app directory structure offers flexibility in organizing components.
You can place components in the app directory, or in a separate directory like app/components or app/lib, if they're global and used across multiple pages.
To make your codebase more maintainable, consider placing components in the app directory if they're used globally.
For example, if you have a reusable component like a navigation bar, you can place it in app/components.
Components can also be placed in a separate directory to reuse them across multiple pages.
Route groups are useful for grouping pages under a common path segment or layout.
You can define a route group by creating a folder with the same name as the group, and placing pages and layouts inside it.
For instance, to group all pages under the /dashboard path, create a folder called dashboard, and place your pages and layouts inside it.
Next.js Layouts are foundational components that wrap pages, making it easy to reuse data-fetching and logic across pages.
You need to define one root layout component in the app directory for this to work.
This layout component will automatically wrap all pages within the folder where it's defined.
Error Handling
Error Handling is a crucial part of building robust applications with Next.js 13. By convention, errors of type 'string' are treated as custom (app specific) errors.
This simplifies the code for throwing custom errors since only a string needs to be thrown. For example, throw 'Username or password is incorrect'. If a custom error ends with the words 'not found', a 404 response code is returned, otherwise a standard 400 error response is returned.
The global error handler is used to catch all errors and remove the need for duplicated error handling code. It's added to the request pipeline in the API handler wrapper function.
If the error is an object with the name 'UnauthorizedError', it means JWT token validation has failed, so a HTTP 401 unauthorized response code is returned with the message 'Invalid Token'. All other (unhandled) exceptions are logged to the console and return a 500 server error response code.
Custom 400 and 500 pages can still be used using the old pages directory, even with the global error handler in place. This is why it's still recommended to use these custom pages.
Pages and Routing
In Next.js 13, you can define pages in the new app directory by using the special convention page.tsx. This allows you to colocate your files, placing components for a specific page right in the folder where it's defined.
You can place your components for a specific page in the folder where it's defined, like in the app/(site) directory. The page app/(site)/page.tsx will be accessible at the root path /.
To generate a list of static pages to be used with dynamic parameters, you can use the generateStaticParams function. This is useful for creating routes that can handle dynamic parameters.
Index Page
The users index page is a crucial part of your Next.js app, displaying a list of all users and providing buttons for adding, editing, and deleting users. It uses a useEffect hook to fetch all users from the user service and store them in local state.
You can define pages in your new app directory using the convention page.tsx. This means if you want to create a new page, you need to name the file page.tsx.
The users index page uses a delete button that calls the deleteUser() function, which updates the user in local state with an isDeleting = true property, displays a spinner on the button, deletes the user from the Next.js API, and then removes the deleted user from local state to remove it from the UI.
You can define pages in the app directory by placing the page in the app/(site) directory and naming it page.tsx, for example, to define the home page of your website.
Pages
To define pages in the new app directory, we use the special convention page.tsx. This means if we want to define a page, we need to name the file page.tsx.
We can place pages close to where they are used, which is a big improvement over the current pages directory. This is possible because we can colocate files in the app directory.
The new App Router works alongside the Pages Router to support incremental adoption and provide other new features like server-side rendering and static site generation. This allows us to place components in the same directory as the page that uses them.
For example, if we want to define the home page of our website, we can place the page in the app/(site) directory and name it page.tsx. This page will be accessible at the root path /.
Page Metadata and SEO
When building pages in Next.js, it's essential to consider their metadata and SEO. To specify the metadata of a page, you can export the constant metadata property in the page.tsx file.
You can access dynamic data using the generateMetadata function. This function allows you to create metadata based on the page's content.
To optimize your page's metadata, you can check out the Next.js documentation for the full list of supported metadata properties. This will give you a comprehensive understanding of what's possible and how to implement it effectively.
Loading Tsx File
The loading.tsx file is an optional component that can be created within any directory inside the app directory. It automatically wraps the page inside a React Suspense boundary.
This means that the component will be shown immediately on the first load as well as when navigating between sibling routes. You can run the bootstrapped code as is to see it in action.
The loading.tsx file is a great way to display a loading indicator while the page is loading, and you can customize it to fit your needs. You can add any component you want to display while the page is loading, such as a top bar loader or a loading spinner.
Project Configuration
Project configuration is a crucial step in setting up your Next.js project. The Next.js config file defines global config variables that are available to components in your app.
To configure your project, you'll need to update the secret property in the Next.js config file. This property is used to sign and verify JWT tokens for authentication, so it's essential to change it to a random string to prevent unauthorized access to your API. A quick way to generate a random string is to join a couple of GUIDs together.
ESLint config is also important, as it configures which rules are enabled when you run npm run lint or next lint on your project. The default rule set is next/core-web-vitals, which is included when you create a Next.js app with the command npx create-next-app@latest.
Pre-Production
Before running your Next.js app in production, there's a crucial step to take care of. Update the secret property in the Next.js config file to a long, random string.
This is used to sign and verify JWT tokens for authentication, so changing it ensures nobody else can generate a JWT with the same secret and gain unauthorized access to your API. Join a couple of GUIDs together to make a long random string, and you can find a quick and easy way to generate GUIDs at https://www.guidgenerator.com/.
Store Files Locally
Placing files close to where they are used can greatly improve efficiency. This can be achieved by colocating files in the app directory.
For example, a component can be placed in the same directory as the page that uses it. This eliminates the need for a separate directory for components.
By doing so, we can significantly reduce the time it takes to locate and access files. This is a big improvement over the current setup, where files are scattered across different directories.
Project
The project structure in Next.js has changed with version 13. The new directory named “app” is replacing “pages”, making it easier to work with.
The “app” directory is where you'll find your Server Components, unless you make a file a Client Component using the “use client” directive at the top of the file.
API Routes are now Server Components or Route Handlers. This is a key change to keep in mind when working with Next.js.
Here's a quick summary of the changes:
- The “app” directory replaces “pages”.
- “page.tsx|page.jsx” is the new “index.tsx|index.jsx”.
- “layout.tsx” is the new “_app.tsx”.
- Everything is a Server Component unless you make it a Client Component.
- API Routes are now Server Components or Route Handlers.
To get started with your project, remove unnecessary files such as logos and icons. If you're using Tailwind CSS, make sure to bring your desired configuration to the tailwind.config.ts file, defining your color palette, fonts, breakpoints, etc.
Root
The root of your Next.js app is where everything starts. This is where you define the outer HTML and body tags for your app.
You'll need to create a file at the top level of your app directory that defines the root layout. This layout will be applied to all routes in your app.
The root layout file must contain the HTML and body tags, as Next.js doesn't automatically add them. This is a mandatory requirement.
In your root layout file, you can control SEO metadata elements like title and description using the exported metadata object. This will help with search engine optimization.
Global CSS stylesheets can be imported at the top of your root layout file. This will make it easy to apply styles across your entire app.
Dotenv (.env) File
The dotenv (.env) file is where you'll define environment variables that are accessible to your Next.js app. This file is crucial for setting up your project's configuration.
By default, environment variables are only accessible to server-side code, so you'll need to prefix them with NEXT_PUBLIC_ to make them available to the React client app. This is a simple yet important step to ensure your app runs smoothly.
The .env file is the base configuration file that applies to all environments, but you can create additional files like .env.development and .env.production for specific environments. You can also use .env.local for local environment variables.
One important environment variable to update is JWT_SECRET, which is used to sign and verify JWT tokens for authentication. Make sure to change this to a random string, like a GUID from guidgenerator.com, to prevent unauthorized access to your app or API.
Hanko Cloud Setup
To set up Hanko Cloud, visit their website and create an account. Then, create an organization to manage your Hanko project.
Create a new project and set the App URL to your development URL, such as http://localhost:3000. This allows you to test and refine your project before moving to production.
Take the time to discover all the features of Hanko Cloud, including being able to see your API URL and other project insights in your dashboard.
Sources
- https://jasonwatmore.com/next-js-13-mysql-user-registration-and-login-tutorial-with-example-app
- https://docs.hanko.io/tutorials/nextjs-todo
- https://makerkit.dev/blog/tutorials/nextjs13
- https://jasonwatmore.com/next-js-13-app-router-mongodb-user-rego-and-login-tutorial-with-example
- https://blog.logrocket.com/next-js-13-app-router/
Featured Images: pexels.com