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.
// 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.
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!
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.
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:
-
Open/create
next-auth.d.ts
file. -
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:
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';
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.