Skip to content
Migrating from NextAuth.js v4? Read our migration guide.

providers/fusionauth

Built-in FusionAuth integration.

FusionAuthProfile

This is the default openid signature returned from FusionAuth it can be customized using lambda functions

Extends

Indexable

[key: string]: any

Properties

at_hash

at_hash: string;

aud

aud: string;

authenticationType

authenticationType: string;

c_hash

c_hash: string;

email

email: string;

email_verified

email_verified: boolean;

exp

exp: number;

family_name?

optional family_name: string;

given_name?

optional given_name: string;

iat

iat: number;

iss

iss: string;

jti

jti: string;

middle_name?

optional middle_name: string;

name?

optional name: string;

picture?

optional picture: string;

preferred_username?

optional preferred_username: string;

scope

scope: string;

sid

sid: string;

sub

sub: string;

default()

function default<P>(options): OAuthConfig<P>

Add FusionAuth login to your page.

Setup

Callback URL

https://example.com/api/auth/callback/fusionauth

Configuration

import { Auth } from "@auth/core"
import FusionAuth from "@auth/core/providers/fusionauth"
 
const request = new Request(origin)
const response = await Auth(request, {
  providers: [
    FusionAuth({
      clientId: FUSIONAUTH_CLIENT_ID,
      clientSecret: FUSIONAUTH_CLIENT_SECRET,
      tenantId: FUSIONAUTH_TENANT_ID,
      issuer: FUSIONAUTH_ISSUER,
    }),
  ],
})
⚠️

If you’re using multi-tenancy, you need to pass in the tenantId option to apply the proper theme.

Resources

Notes

By default, Auth.js assumes that the FusionAuth provider is based on the OAuth 2 specification.

Configuration

💡

An application can be created at https://your-fusionauth-server-url/admin/application.

For more information, follow the FusionAuth 5-minute setup guide.

In the OAuth settings for your application, configure the following.

If using JSON Web Tokens, you need to make sure the signing algorithm is RS256, you can create an RS256 key pair by going to Settings, Key Master, generate RSA and choosing SHA-256 as algorithm. After that, go to the JWT settings of your application and select this key as Access Token signing key and Id Token signing key.

💡

The FusionAuth provider comes with a default configuration. To override the defaults for your use case, check out customizing a built-in OAuth provider.

Disclaimer If you think you found a bug in the default configuration, you can open an issue.

Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec, we might not pursue a resolution. You can ask for more help in Discussions.

It is highly recommended to follow this example call when using the provider in Next.js so that you can access both the access_token and id_token on the server.

/// <reference types="next-auth" />
import NextAuth from 'next-auth';
export const { handlers, auth, signIn, signOut } = NextAuth({
 providers: [
   {
     id: 'fusionauth',
     name: 'FusionAuth',
     type: 'oidc',
     issuer: process.env.AUTH_FUSIONAUTH_ISSUER!,
     clientId: process.env.AUTH_FUSIONAUTH_CLIENT_ID!,
     clientSecret: process.env.AUTH_FUSIONAUTH_CLIENT_SECRET!,
     authorization: {
       params: {
         scope: 'offline_access email openid profile',
         tenantId: process.env.AUTH_FUSIONAUTH_TENANT_ID!,
       },
     },
     userinfo: `${process.env.AUTH_FUSIONAUTH_ISSUER}/oauth2/userinfo`,
     // This is due to a known processing issue
     // TODO: https://github.com/nextauthjs/next-auth/issues/8745#issuecomment-1907799026
     token: {
       url: `${process.env.AUTH_FUSIONAUTH_ISSUER}/oauth2/token`,
       conform: async (response: Response) => {
         if (response.status === 401) return response;
 
         const newHeaders = Array.from(response.headers.entries())
           .filter(([key]) => key.toLowerCase() !== 'www-authenticate')
           .reduce(
             (headers, [key, value]) => (headers.append(key, value), headers),
             new Headers()
           );
 
         return new Response(response.body, {
           status: response.status,
           statusText: response.statusText,
           headers: newHeaders,
         });
       },
     },
   },
 ],
 session: {
   strategy: 'jwt',
 },
 // Required to get the account object in the session and enable
 // the ability to call API's externally that rely on JWT tokens.
 callbacks: {
   async jwt(params) {
     const { token, user, account } = params;
     if (account) {
       // First-time login, save the `access_token`, its expiry and the `refresh_token`
       return {
         ...token,
         ...account,
       };
     } else if (
       token.expires_at &&
       Date.now() < (token.expires_at as number) * 1000
     ) {
       // Subsequent logins, but the `access_token` is still valid
       return token;
     } else {
       // Subsequent logins, but the `access_token` has expired, try to refresh it
       if (!token.refresh_token) throw new TypeError('Missing refresh_token');
 
       try {
         const refreshResponse = await fetch(
           `${process.env.AUTH_FUSIONAUTH_ISSUER}/oauth2/token`,
           {
             method: 'POST',
             headers: {
               'Content-Type': 'application/x-www-form-urlencoded',
             },
             body: new URLSearchParams({
               client_id: process.env.AUTH_FUSIONAUTH_CLIENT_ID!,
               client_secret: process.env.AUTH_FUSIONAUTH_CLIENT_SECRET!,
               grant_type: 'refresh_token',
               refresh_token: token.refresh_token as string,
             }),
           }
         );
 
         if (!refreshResponse.ok) {
           throw new Error('Failed to refresh token');
         }
 
         const tokensOrError = await refreshResponse.json();
 
         if (!refreshResponse.ok) throw tokensOrError;
 
         const newTokens = tokensOrError as {
           access_token: string;
           expires_in: number;
           refresh_token?: string;
         };
 
         return {
           ...token,
           access_token: newTokens.access_token,
           expires_at: Math.floor(Date.now() / 1000 + newTokens.expires_in),
           // Some providers only issue refresh tokens once, so preserve if we did not get a new one
           refresh_token: newTokens.refresh_token
             ? newTokens.refresh_token
             : token.refresh_token,
         };
       } catch (error) {
         console.error('Error refreshing access_token', error);
         // If we fail to refresh the token, return an error so we can handle it on the page
         token.error = 'RefreshTokenError';
         return token;
       }
     }
   },
   async session(params) {
     const { session, token } = params;
     return { ...session, ...token };
   },
 },
});
 
declare module 'next-auth' {
 interface Session {
   access_token: string;
   expires_in: number;
   id_token?: string;
   expires_at: number;
   refresh_token?: string;
   refresh_token_id?: string;
   error?: 'RefreshTokenError';
   scope: string;
   token_type: string;
   userId: string;
   provider: string;
   type: string;
   providerAccountId: string;
 }
}
 
declare module 'next-auth' {
 interface JWT {
   access_token: string;
   expires_in: number;
   id_token?: string;
   expires_at: number;
   refresh_token?: string;
   refresh_token_id?: string;
   error?: 'RefreshTokenError';
   scope: string;
   token_type: string;
   userId: string;
   provider: string;
   type: string;
   providerAccountId: string;
 }
}

Type Parameters

Type Parameter
P extends FusionAuthProfile

Parameters

ParameterType
optionsOAuthUserConfig<P> & { tenantId: string; }

Returns

OAuthConfig<P>

Auth.js © Balázs Orbán and Team - 2025