At Agilno, we often encounter complex project requirements that demand deep customization of popular libraries. One of these challenges involved using NextAuth, a robust authentication solution for Next.js, and adapting it to meet more specific, business-critical needs.
NextAuth works out of the box for many use cases, but it sometimes lacks flexibility when handling more advanced use cases. We faced this exact situation when implementing custom authentication flows and enriching session tokens. In this article, we’ll share what we learned, the challenges we overcame, and the steps we took to extend NextAuth’s functionality.
The Challenge: Beyond the Basics
We started with a project that relied on magic email tokens for authentication. This simplified the login process by removing passwords altogether, but it also presented a unique set of challenges. NextAuth, while powerful, didn’t fully support this flow out of the box.
We discovered that:
- Magic email tokens are not part of NextAuth’s standard provider setup.
- Managing complex user roles and groups was essential for our frontend, but the default session tokens were too minimal.
- We needed to securely manage access and refresh tokens for token-based authentication.
Our experience taught us that, while NextAuth can handle standard OAuth flows or credentials-based login, it requires customization to handle token-based, passwordless authentication and more complex role management.
Customizing NextAuth for Magic Email Tokens
Magic email tokens allow users to log in via a one-time link sent to their email. This is simpler for users, but implementing it requires careful customization. The biggest lesson here was understanding how to leverage NextAuth’s credentials provider to handle a non-standard flow.
What We Learned:
- Customization is Key: NextAuth is highly extendable, and we used a custom credentials provider to intercept the authentication process.
- API Communication: We had to send the email to our backend, which generated the token and sent it to the user’s email. This separation of concerns kept our authentication logic secure and scalable.
Code Example:
Here’s a snippet of how we implemented the custom credentials provider:
```javascript // pages/api/auth/[...nextauth].js import NextAuth from 'next-auth'; import Providers from 'next-auth/providers'; import { sendMagicLink } from '../../../lib/auth'; // Custom method to handle magic link export default NextAuth({ providers: [ Providers.Credentials({ name: 'Email Login', credentials: { email: { label: "Email", type: "email", placeholder: "your-email@example.com" }, }, async authorize(credentials) { const response = await sendMagicLink(credentials.email); if (response && response.success) { return { email: credentials.email }; } else { throw new Error('Invalid email or unable to send token'); } } }) ], pages: { signIn: '/auth/signin', }, secret: process.env.NEXTAUTH_SECRET, }); ```
This worked because we could bypass the need for traditional credentials (e.g., username/password) and focus purely on email-based authentication. We discovered that while NextAuth’s credentials provider was designed for more traditional login forms, with a little modification, it became a perfect fit for magic email tokens.
Enriching Session Tokens for Complex Frontend Logic
In many applications, roles and permissions are central to managing user experience. However, the default JWT session tokens generated by NextAuth were too limited for our needs. We required additional information, such as:
- User roles (e.g., admin, client, team member)
- Groups or permissions the user belonged to
- Access and refresh tokens returned from the backend
The Hurdles We Faced:
- Role Management: The default session token contained very little information. For example, we needed to manage permissions dynamically based on the user’s role (e.g., admin vs. client).
- Token Expiry: Access tokens have a limited lifespan, so refreshing them securely without requiring a user to log back in was crucial.
Our Solution:
We extended the JWT and session callbacks to include these properties. One critical lesson here was ensuring that the access token and refresh token returned by the backend were available in the session so that the frontend could seamlessly handle token refresh logic.
Code Example:
Here’s how we enriched the session tokens:
```javascript // pages/api/auth/[...nextauth].js import NextAuth from 'next-auth'; import Providers from 'next-auth/providers'; export default NextAuth({ providers: [ // Define your providers (e.g., Credentials, Google, etc.) ], callbacks: { async jwt(token, user, account) { if (account && user) { token.accessToken = account.accessToken; token.refreshToken = account.refreshToken; token.role = user.role; token.group = user.group; token.isAdmin = user.isAdmin; } return token; }, async session(session, token) { session.accessToken = token.accessToken; session.refreshToken = token.refreshToken; session.user.role = token.role; session.user.group = token.group; session.user.isAdmin = token.isAdmin; return session; }, }, secret: process.env.NEXTAUTH_SECRET, }); ```
This worked well because we could now pass detailed user information and token data to the frontend. This was particularly important for handling role-based UI rendering and token refresh without disrupting the user experience.
What You Should Know
Here are some key takeaways from our experience customizing NextAuth:
- NextAuth Is Highly Flexible, But Needs Customization: If your project involves more than simple login flows, you’ll need to extend NextAuth to suit your needs.
- Magic Email Tokens Require Custom Providers: While not supported out of the box, magic tokens are possible with the right setup.
- Token Management Is Critical: Securely managing access and refresh tokens is essential for user authentication that scales.
Summary
Customizing NextAuth gave us the flexibility to implement complex authentication flows while ensuring secure and seamless user management. By leveraging custom providers and enriching session tokens, we optimized our application’s authentication logic and improved the user experience.
If your project has advanced authentication requirements, NextAuth’s flexibility will allow you to implement them, but don’t expect everything to work out of the box. With the right tweaks and adjustments, you can build scalable, secure solutions tailored to your specific needs.