Skip to main content

Credentials Provider Using NextAuth.js

Overview

Welcome to the world of seamless authentication using NextAuth.js using Credentials Provider. In this guide, we'll walk you through the process step by step, ensuring you grasp each concept fully. Whether you're new to NextAuth or a seasoned developer, you're in for an enjoyable journey filled with insights and links to fine-tune your authentication flow to match your project's unique requirements.

Prerequisites

Before we dive into the implementation details, ensure you have a basic understanding of NextAuth.js. If you need a quick refresher, feel free to explore the relevant documentation: NextAuth.js.

Initialize NextAuth.js

Next.js 13.2 introduced Route Handlers, the preferred way to handle REST-like requests in App Router (app/).

You can initialize NextAuth.js with a Route Handler too, very similar to API Routes.

src/app/api/auth/[...nextauth]/route.ts
// Third-party Imports
import NextAuth from 'next-auth'

// Lib Imports
import { authOptions } from '@/libs/auth'

const handler = NextAuth(authOptions)

export { handler as GET, handler as POST }

Internally, NextAuth.js detects that it is being initialized in a Route Handler (by understanding that it is passed a Web Request instance, and will return a handler that returns a Response instance. A Route Handler file expects you to export some named handler functions that handle a request and return a response. NextAuth.js needs the GET and POST handlers to function properly, so we export those two.

info

Technically, in a Route Handler, the api/ prefix is not necessary, but we decided to keep it required for an easier migration.

Please refer to the NextAuth official documentation for advanced initialization.

auth.ts file

The implementation of the authentication logic should be performed within the src/libs/auth.ts file. It is advisable to tailor this file to suit the specific requirements of your project. The necessary configurations and instructions for customizing can be located in the NextAuth official documentation.

Before you embark on your journey of customization, take a moment to understand the contents and functioning of the src/libs/auth.ts file. Let's dive in!

note

All the options used in the src/libs/auth.ts file are outlined and explained below. Please refer to the official NextAuth documentation for a comprehensive list of available options.

Providers

We utilize the CredentialsProvider which enables the handling of sign-in through diverse credentials, such as a username and password. Multiple authentication providers can be configured simultaneously.

Please refer to this example for the comprehension of implementing NextAuth with actual APIs via CredentialsProvider.

The following options are available within CredentialsProvider:

`name`

This is the display name shown on the sign-in form. Make it intuitive, like 'Sign in with...'.

`type`

This option defines the type of provider, which, in this case, is 'credentials'.

`credentials`

Here, you define the credentials required for sign-in. Since we're using our custom sign-in page, there's no need to manually alter the username or password attributes in the credentials.

`authorize`

This callback executes once the user is being authorized. It's your opportunity to implement your own logic here. Take the submitted credentials and return either an object representing the user or a value like false/null if the credentials are invalid. For instance, you could return { id: 1, name: 'J Smith', email: 'jsmith@example.com' }. You can also utilize the req object to gather additional parameters, like the request IP address.

Caution

To match user credentials, you'll need to make an API call to the login API. Remember, it's crucial to handle sensitive information with care. Make sure to remove any confidential data from the API response or before sending the user data back to maintain security.

For more providers options and configurations, refer to the official NextAuth documentation.

secret

It is a random string used to hash tokens, sign/encrypt cookies and generate cryptographic keys.

If the NEXTAUTH_SECRET is set as an environment variable, it is not necessary to define this option. A secret can be generated by visiting the documentation link provided below.

Kindly consult the official NextAuth documentation for more details on secret.

session

`strategy`

You are required to make a choice on how you wish to preserve the user session. The default option is jwt, which involves the storage of an encrypted JWT (JWE) within a session cookie. If you elect to use an adapter, the default setting will be changed to database instead. It is still possible to explicitly specify jwt and retain a JWT session. If the option database is chosen, the session cookie will only hold a sessionToken value, which will then be used to retrieve the session information from the database.

`maxAge`

The duration of an idle session until it expires and becomes invalid. It accepts a number in seconds.

Kindly consult the official NextAuth documentation for more session options.

pages

Specify URLs to be used if you want to create custom sign in, sign out and error pages. Pages specified will override the corresponding built-in page.

Kindly consult the official NextAuth documentation for more pages options.

callbacks

Callbacks are asynchronous functions you can use to control what happens when an action is performed. Callbacks are extremely powerful, especially in scenarios involving JSON Web Tokens as they allow you to implement access controls without a database and to integrate with external databases or APIs.

`jwt`

This callback is called whenever a JSON Web Token is created (i.e. at sign in) or updated (i.e whenever a session is accessed in the client). The returned value will be encrypted, and it is stored in a cookie.

When utilizing the jwt strategy within the session option, the jwt() callback will be executed prior to the session() callback. The data returned by the authorize function in the providers option will be passed to the jwt() callback in the form of the token.

To include custom parameters in the session() callback, they must be added to the token in the jwt() callback, which will then be transferred to the session() callback for further processing.

Kindly consult the official NextAuth documentation for more details on jwt() callback.

`session`

The session callback is called whenever a session is checked. By default, only a subset of the token is returned for increased security. If you want to make something available you added to the token (like access_token and user.id from above) via the jwt() callback, you have to explicitly forward it here to make it available to the client.

Kindly consult the official NextAuth documentation for more details on session() callback.

Kindly consult the official NextAuth documentation for more callbacks options.

We hope that with the information provided, you have now acquired a comprehensive understanding of all the options available within the auth.ts or auth.js file.

Login Form & API

Login Form

We can create a login form which calls the signIn function from the next-auth/client library. The signIn function will call the login API and return the user data if the credentials are valid.

Please refer to the src/views/Login.tsx file for the implementation of the login form.

Login API

Please create your login API, where the authentication of the user's credentials will take place.

Please refer to the src/app/api/login/route.ts file for the implementation of the login API.

Extending NextAuth Types for Custom User Fields

In your dynamic and vibrant app, you might have a unique role field nestled within your user object. Fear not, for we shall seamlessly guide you in extending the NextAuth types to gracefully accommodate this distinct feature. 🎩 ✨

The first stop on our customization tour is the next-auth.d.ts file. This TypeScript declaration file plays a crucial role in modifying the types specified by NextAuth. Locate or create this file in the root directory of your project. Don't worry, you don't need a magic wand – just a few keyboard strokes! ⌨️ 🔮

Let's say your user objects have a charming role field that you'd like to include in the NextAuth types. This field adds a touch of distinction to your users. To achieve this, follow these simple steps:

  1. Open/create next-auth.d.ts file.

  2. Your declaration should look something like this:

    import 'next-auth/jwt'
    import { DefaultSession } from 'next-auth'

    declare module 'next-auth/jwt' {
    type JWT = {
    role: string
    }
    }

    declare module 'next-auth' {
    type Session = {
    user: {
    role: string
    } & DefaultSession['user']
    }

    type User = {
    role: string
    }
    }

After adding the role field declaration, the NextAuth types are now aware of your custom user data.

Now that you've extended the NextAuth types, it's time to put your custom field to good use in your authentication flow.

First let's add role into the user session, open src/libs/auth.ts file and add role in JWT and Session callbacks like following:

src/libs/auth.ts
callbacks: {
/*
* While using `jwt` as a strategy, `jwt()` callback will be called before
* the `session()` callback. So we have to add custom parameters in `token`
* via `jwt()` callback to make them accessible in the `session()` callback
*/
async jwt({ token, user }) {
if (user) {
/*
* For adding custom parameters to user in session, we first need to add those parameters
* in token which then will be available in the `session()` callback
*/
token.name = user.name
token.role = user.role
}

return token
},
async session({ session, token }) {
if (session.user) {
// ** Add custom params to user in session which are added in `jwt()` callback via `token` parameter
session.user.name = token.name
session.user.role = token.role
}

return session
}
}

Whenever you're accessing user data through NextAuth, you can access the role field like a pro:

// Accessing user data with the role field
import { useSession } from 'next-auth/react';

// Inside your component...
const { data: session } = useSession();
const userRole = session?.user?.role || 'defaultRole';
info

Please note that customization of NextAuth to meet specific requirements, such as modifying the session strategy, adjusting session expiration times, designating authentication pages, and customizing callback functions, are not considered part of the support. These elements will vary based on the specific implementation.