In this tutorial, you will learn to implement user authentication in a Next.js application using Auth.js and Upstash Redis. You will learn how to set up the required environment, create custom authentication provider for secure user sign-in and sign-up, and integrate Upstash Redis as the database for managing sessions.
Serverless database platform. You are going to use Upstash Redis for managing sessions and user information.
Set up an Upstash Redis instance
In your Upstash dashboard, go to Redis tab and create a database.
Scroll down until you find the REST API section, and select the .env button. Copy the content and save it somewhere safe.
Create a new Next.js application
Let’s get started by creating a new Next.js project. Open your terminal and run the following command:
When prompted, choose:
Yes when prompted to use TypeScript.
No when prompted to use ESLint.
Yes when prompted to use Tailwind CSS.
No when prompted to use src/ directory.
Yes when prompted to use App Router.
No when prompted to use Turbopack.
No when prompted to customize the default import alias (@/*).
Once that is done, move into the project directory and start the app in development mode by executing the following command:
The app should be running on localhost:3000. Stop the development server to install the necessary dependencies with the following commands:
The libraries installed include:
nanoid: A library for generating unique, secure IDs.
next-auth: Authentication solution built for Next.js.
@auth/core: Core package for handling authentication in Auth.js.
@upstash/redis: SDK to interact with Upstash Redis via HTTP requests.
@auth/upstash-redis-adapter: Adapter for integrating Auth.js with Upstash Redis.
Now, create a .env file at the root of your project. You are going to use the AUTH_SECRET, UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN values.
The .env file should have the following:
Instantiate the Upstash Redis Client
Create a file named redis.ts in the lib directory with the following code:
In the code above, an Edge-compatible Redis client instance is exported to be used throughout the application.
Set up User Types and Credentials Helpers
Create a file named types.ts in the lib directory with the following code:
The UserType interface defines the structure of a user object in the application. It includes:
id (optional): A unique identifier for the user.
email (required): The user's email address.
name (optional): The user's name.
image (optional): A URL or path to the user's profile image.
password (optional): The user's hashed password.
emailVerified (optional): A timestamp or string indicating if/when the user's email was verified.
This interface serves as a type definition for user data throughout the application.
Further, create a file named credentials.ts in the lib directory with the following code:
In the code above, Edge-compatiable utility functions for generating and managing secure tokens and hashed passwords are defined:
generateRandomToken: Creates a cryptographically secure random token by generating a 20-byte random array and converting it into a hexadecimal string.
generateRandomString: Accepts an input string, encodes it to bytes, hashes it using SHA-256, and returns the hash as a hexadecimal string. Useful for generating consistent, unique strings derived from input values.
hashPassword: Hashes a plaintext password using SHA-256 and converts the resulting hash to a hexadecimal string. This is used for securely storing passwords.
comparePassword: Compares a plaintext password to a hashed password by hashing the input and checking if it matches the stored hash.
Using Upstash Redis adapter with Auth.js
Create a file named auth.ts in the lib directory with the following code:
In the code above, authentication is being set up via creating the the handlers, signIn, signOut, and auth functions using a custom configuration. It uses a JWT-based session strategy, the UpstashRedisAdapter to connect to a Upstash Redis database for storing user data, and an array of authentication providers defined in the providers module. This setup enables seamless integration of authentication and session management with Upstash Redis as the backend.
Set up Custom Authentication provider with Auth.js
Create a file named providers.ts in the lib directory with the following code:
In the code above, an array of custom, credentials-based authentication provider is exported. The Credentials provider handles both sign-in and sign-up logic: it checks if the user exists in Redis and either validates their password or creates a new user. If signing in, it compares the provided password (hashed using a random string) with the stored password; if signing up, it creates a new user entry in Upstash Redis. The code uses Upstash Redis for user data storage and ensures that the process is done in either sign-up or sign-in mode based on the kind parameter in the URL.
Set up Authentication API Routes (powered by Auth.js)
Create a file named route.ts in the app/api/auth/[...nextauth] directory with the following code:
In the code above, two endpoint handlers GET and POST are exported that are handled by the handlers function exported from @/lib/auth.ts file.
Create a file named route.ts in the app/api/refresh directory with the following code:
In the code above, an Edge-compatible API route for refreshing a user's session is being created. It fetches the user session and related data from Redis, verifies the user's email, and updates the session cookie. If the user data is invalid or missing, the session cookie is cleared; otherwise, a new signed token is generated and stored in the cookie.
Create a file named route.ts in the app/api/user directory with the following code:
In the code above, an Edge-compatible API route for updating user data is being created. It authenticates the user, fetches their data from Redis using their email, and updates their information (e.g., name or image) based on the request body. If the user is not found or an error occurs, appropriate status codes and responses are returned.
Set up Next.js Middleware with Auth.js
Create a file named middleware.ts in the root directory of your project with the following code:
In the code above, the middleware is created by re-exporting the auth function from the @/lib/auth module. The middleware applies authentication to all routes except API routes, static files, image assets, and the favicon, as specified by the matcher configuration. This ensures non-excluded routes are protected by the authentication logic set with Auth.js.
Use Auth.js in Next.js App Router Components
Create a file named NextAuthProvider.tsx in the app directory with the following code:
In the code above, a client-side React component is created to wrap the application with NextAuth's SessionProvider. The component accepts children as props and ensures that the wrapped components have access to the user session context provided by NextAuth. This setup is essential for managing authentication state across the application in Next.js.
Create a file named csrf.tsx in the app directory with the following code:
In the code above, a client-side React component is exported that fetches a CSRF token using NextAuth's getCsrfToken function and stores it in a state variable. The component renders a hidden input field containing the CSRF token, allowing secure form submissions by including the token as a hidden value. This is useful for protecting against cross-site request forgery attacks.
Create a file named provider.tsx in the app directory with the following code:
In the code above, a React component for rendering a customizable sign-in button for different authentication providers is exported. It accepts provider (containing name and id) and prefix as props. The button triggers the signIn function for the specified provider and dynamically displays an icon and label based on the provider's id.
Update the file named layout.tsx in the app directory with the following code:
In the code above, the Next.js layout is wrapping the entire page in global styles and authentication context. It imports globals.css for global styling and NextAuthProvider to manage authentication state. The children prop represents the nested page content, which is wrapped in a styled container with responsive padding and a maximum width, providing a consistent layout for all pages.
Load the user authentication state in a client-side page
Update the file named page.tsx in the app directory with the following code:
In the code above, a client-side React component that displays the user's session data and provides a "Sign Out" button is exported. It uses NextAuth's useSession hook to retrieve the session data and status, displaying them as a JSON string. The signOut function is called when the button is clicked, allowing the user to log out.
Build a dynamic Sign-in page (using Auth.js)
Create a file named page.tsx in the app/signin directory with the following code:
In the code above, a sign-in page is being created. It checks if a user session already exists; if so, it redirects to the homepage. The page displays a sign-in form where users can enter their email and password, along with options to sign in using various providers. It also includes a CSRF token for security and a link to the sign-up page for new users.
Build a dynamic Sign-up page (using Auth.js)
Create a file named page.tsx in the app/signup directory with the following code:
In the code above, a sign-up page is being created. It checks if a user session already exists; if so, it redirects to the homepage. The page displays a sign-up form where users can enter their email and password, along with options to sign in using various providers. It also includes a CSRF token for security and a link to the sign-in page for existing users.
That was a lot of learning! You’re all done now ✨
References
For more detailed insights, explore the references cited in this blog.
In this tutorial, you learnt how to implement user authentication in a Next.js application using Auth.js and Upstash Redis. You learnt the setup of environment variables, the creation of a Redis client, and the implementation of a custom authentication provider. Additionally, you learnt how to set up API routes in Next.js for handling user sign-in and sign-up processes, as well as session management. By following these steps, you can effectively manage user authentication and data storage in your Next.js applications with Upstash Redis.