🔑

Next Auth(v4)を導入しようとしているあなたに届けたい仕様と使い所

2024/06/17に公開

NextAuthについて色々検証する機会があったので備忘録としてここに残します。
NextAuthの導入を考えている方に少しでも参考になれば嬉しいです!
NextAuthはとても便利ですが、表面的に使うと仕様が暗黙的で戸惑う部分が多かったので仕様を探り解説していきます!

概要

NextAuthキャッチ
そもそもNextAuthとは何かについて軽くおさらいしましょう。

NextAuthの概要

NextAuthは、Next.jsアプリケーションのためのオープンソースの認証ライブラリです。
Next.jsアプリケーションに簡単に認証機能を追加するためのライブラリであり、対応プロバイダーはOAuth (Google, Facebook, Twitterなど)、EmailCredentials、任意のカスタムプロバイダーに対応しています。セッション管理はクッキーを使用したセッション管理が標準で提供され、ユーザーの認証状態を簡単に管理できます。また、セッションの永続化も対応しており、対応できるデータベースはMongoDBPostgreSQLMySQLDynamoDBSQLiteなど、様々なデータベースと連携が可能です。

NextAuthの主な機能

認証プロバイダーのサポート

  • OAuth 2.0プロバイダー(Google、GitHub、Facebookなど)
  • Email/Password認証
  • Credentials認証(独自の認証ロジックを追加可能)

セッション管理

  • サーバーサイドセッション、JWT(JSON Web Tokens)セッションの両方をサポート
  • セッションの持続時間や更新のカスタマイズが可能
  • 認証フローの各ステージ(サインイン、サインアウト、JWT生成など)に対してカスタムロジックを追加可能
  • ログイン成功時やエラー発生時などのイベントに対するハンドラーを設定可能

ユーザー情報の保護

  • トークンの暗号化とセキュアなストレージ
  • 簡単に導入できるセキュリティ設定

概要をおさらいしたところで、実際に具体的な設定や使い方、仕様について解説していきます。

サーバー側の設定・挙動

今回は前提として以下の設定を用います。

※firebaseのクライアントSDKをNextAuthでセッション管理すると、メアド更新やパスワード更新した時の認証が引き継がれないのでオススメしません。もしfirebaseとNextAuthでセッション管理するのであれば、admin SDKを使用するようにしましょう。

auth.ts
import type { NextAuthOptions } from 'next-auth'
import CredentialsProvider from 'next-auth/providers/credentials'
import { getAccessToken } from '@api/token'
import { createMultipass } from '@repo/backend-api'
import {  getAuth, signInWithEmailAndPassword } from 'firebase/auth'

const authOptions: NextAuthOptions = {
  // ①シークレットキーの設定
  secret: 'LlKq6ZtYbr+hTC073mAmAh9/h2HwMfsFo4hrfCx5mLg=',
  // ②ログの差し込み
  logger: {
    error(code, metadata) {
      console.error(code, metadata)
    },
    warn(code) {
      console.warn(code)
    },
  },
  providers: [
    // ③認証プロバイダーの設定
    CredentialsProvider({
      name: 'credentials',
      credentials: {
        email: { label: 'Email', type: 'email', placeholder: 'example@example.com' },
        password: { label: 'Password', type: 'password' },
      },
      // ④認証処理の実装
      async authorize(credentials) {
        if (!credentials) return null
        try {
                    const auth = getAuth()
          const res = await signInWithEmailAndPassword(auth, credentials.email, credentials.password)
          const idToken = await res.user.getIdToken()
          const accessToken = await getAccessToken(idToken)
          if (accessToken) {
            return {
              id: res.user.uid,
              email: res.user.email,
              accessToken,
              idToken,
            }
          }
        } catch (error) {
          throw new Error('Invalid credentials')
        }
        return null
      },
    }),
  ],
  // ⑤セッションの設定
  session: {
    strategy: 'jwt',
  },
    // ⑥処理のコールバック設定
  callbacks: {
    async signIn({ user, account, profile, credentials }) {
      return true
    },
    async redirect({ url, baseUrl }) {
      return url.startsWith(baseUrl) ? url : baseUrl
    },
    async jwt({ token, trigger, session, user }) {
      if (trigger === 'update') token.name = session?.user?.name
      return {
        ...user,
        ...token,
      }
    },
    async session({ user, session, token }) {
      session.user = user
      session.accessToken = token.accessToken
      session.idToken = token.idToken
      return session
    },
  },
}

export { authOptions }

// ⑦interfaceの拡張
declare module 'next-auth' {
  interface Session {
    accessToken?: string
    idToken?: string
  }

  interface User {
    accessToken?: string
    idToken?: string
  }
}

declare module 'next-auth/jwt' {
  interface JWT {
    accessToken?: string
    idToken?: string
  }
}
src/pages/api/auth/[...nextauth].page.tsx
import type { NextApiRequest, NextApiResponse } from 'next'

import NextAuth from 'next-auth/next'

import { authOptions } from '@/auth'

// ⑧NextAuthのハンドラを設定する
export default async function auth(req: NextApiRequest, res: NextApiResponse) {
  return await NextAuth(req, res, {
    ...authOptions,
  })
}

_app.tsx
// ⑨クライアントの設定
import { SessionProvider, signIn as nextAuthSignIn, useSession } from 'next-auth/react'

export default APP = () => {
  return (
    <SessionProvider>
      <Card>
        <Title>NextAuth</Title>
        <Divider />
        <Flex>
          <NextAuthLogin />
        </Flex>
      </Card>
    </SessionProvider>
  )
}

const NextAuthLogin = () => {
  const { register, handleSubmit } = useForm({
    defaultValues: {
      email: '',
      password: '',
    },
  })
  const onSubmit = handleSubmit(async (data) => {
    await nextAuthSignIn('credentials', {
      email: data.email,
      password: data.password,
      redirect: false,
    })
  })
  const { data, status, update } = useSession()

  return (
    <FormContainer>
      <form onSubmit={onSubmit}>
        <Flex>
          <FormInput type="email" {...register('email')} placeholder="email" />
          <FormInput type="password" {...register('password')} placeholder="password" />
          <Button type="submit">Login</Button>
          <Button
            type="button"
            onClick={async () => {
              await update()
            }}
          >
            UPDATE
          </Button>
        </Flex>
      </form>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </FormContainer>
  )
}

①シークレットキーの設定

secret: 'LlKq6ZtYbr+hTC073mAmAh9/h2HwMfsFo4hrfCx5mLg=',

https://next-auth.js.org/configuration/options#secret
オプショナルな設定ですが、ここに設定した値を元にトークンをハッシュ化したり暗号キーの生成に使われます。環境変数として設定して値を隠蔽することが一般的です。この値の設定が漏れているとページを跨いだセッション管理ができなくなるので必ず設定しておくと良いでしょう。

https://next-auth.js.org/errors#jwt_session_error
もし設定が漏れるとJWT_SESSION_ERRORのエラーがthrowされます。

https://authjs.dev/concepts/session-strategies#jwt
▲このシークレットキーを使って暗号化されたセッション情報をnext-auth.session-tokenとしてクライアントのCookieに発行し、セッションデータを取得してくる際にシークレットキーで暗号化されたJWTをデコードしsessionデータとして返しています。
なので、仮にクライアントのCookieのnext-auth.session-tokenが漏洩したとしてもサーバー側で保持しているシークレットキーを知らなければデコードすることができないようになっています。

②ログの差し込み

logger: {
error(code, metadata) {
  console.error(code, metadata)
},
warn(code) {
  console.warn(code)
},

https://next-auth.js.org/configuration/options#logger
オプショナルな設定ですが、監視ツールなど導入していればloggerを設定してログの監視もできます。
余談ですがデプロイ先がVercelであればVercelのロギングサービスで確認することもできます。

③認証のプロバイダを設定

CredentialsProvider({
  name: 'credentials',
  credentials: {
    email: { label: 'Email', type: 'email', placeholder: 'example@example.com' },
    password: { label: 'Password', type: 'password' },
  },

https://next-auth.js.org/tutorials/ldap-auth-example
GoogleやFacebook認証といったOAuthなどを使用して認証する以外の方法ではCredentialsProviderを用いて独自の認証方法を定義することができます。

この例では、emailとpasswordを受け取って認証することを設定しています。
ここで作成した設定を元にクライアント側で認証を行うことができます。

import { signIn } from 'next-auth/react'

await signIn('credentials', {
  email: data.email,
  password: data.password,
  redirect: false,
})

next-auth/reactから提供されているsignInの第一引数にCredentialsProviderで設定したnameを渡すと型もつくのでとても開発しやすくなります。

providers: [
    Apple,
    Auth0,
    Cognito,
    Coinbase,
    // ...中略
    Slack,
    Spotify,
    Twitch,
    Twitter,
],

また、providersは複数設定することができるので、一つのサービスで複数の認証方法を採用することも容易です。

https://next-auth.js.org/providers/
どの認証方法が使えるかは公式ドキュメントにまとまっています。

④認証処理の実装

  async authorize(credentials) {
    if (!credentials) return null
    try {
      const auth = getAuth()
      const res = await signInWithEmailAndPassword(auth, credentials.email, credentials.password)
      const idToken = await res.user.getIdToken()
      const accessToken = await getAccessToken(idToken)
      if (accessToken) {
        return {
          id: res.user.uid,
          email: res.user.email,
          accessToken,
          idToken,
        }
      }
    } catch (error) {
      throw new Error('Invalid credentials')
    }
    return null
  },

ここが認証処理の箇所になります。

ここで行っているのはfirebaseのSDKを使用してauthを取得して、それを元にcredentialsで渡ってきたemailpasswordを用いてsignInWithEmailAndPasswordを実行しています。
firebaseのidTokenを使ってアプリケーションやサーバーのAPIと疎通するaccessTokenを取得しsession情報として返り値を設定しています。

前述したsignIn関数を実行すると/api/auth/signinにルーティングされauthorizeが実行されます。成功すれば戻り値で指定したデータが、後述するcallbackのsignInjwtが実行され、最終的にsessionとしてクライアントにsession情報を渡すことができます。

⑤セッションの設定

  session: {
    strategy: 'jwt',
  },

https://next-auth.js.org/getting-started/upgrade-v4#session-strategy
セッション自体の管理方法をここで指定することができます。
strategy: 'jwt' | 'database'を設定することができ、デフォルトではjwtが設定されます。sessionを永続的に持ちたいのであればdatabaseを指定し、そうでないのであればjwtを設定すればOKです。

要件や課題次第でどちらが適切か判断し、設定してください。

jwtを指定すれば、sessionデータをJWTで管理し、暗号化されたJWTがセッションクッキーに保存されます。
session_cookie

databaseを指定するとadapterの設定が必須になります。adapterにはsessionの保存先のDBを指定します。また、この設定を行う場合はセッションクッキーのsessionTokenのみ扱う挙動になります。そして、callbackで設定しているjwtは不要とみなされ実行されなくなります。

以下がstrategy: "database"で設定する場合のサンプルです。

session: {
  strategy: "database",
  // Seconds - アイドル・セッションが期限切れで無効になるまでの時間。
  maxAge: 30 * 24 * 60 * 60, // 30 days

    // Seconds - セッションを延長するために、データベースへの書き込み頻度を制限する。
    // 書き込み操作を制限するために使用します。常にデータベースを更新するには `0` に設定します。
    // 注: JSON Web トークンを使用している場合、このオプションは無視されます。
  updateAge: 24 * 60 * 60, // 24 hours

    // セッション・トークンは通常、ランダムなUUIDか文字列のどちらかです。
    // よりカスタマイズされたセッショントークン文字列が必要な場合は、独自の生成関数を定義することができます。
  generateSessionToken: () => {
    return randomUUID?.() ?? randomBytes(32).toString("hex")
  }
}

https://next-auth.js.org/adapters

⑥処理のコールバック設定

  callbacks: {
    async signIn({ user, account, profile, credentials }) {
      return true
    },
    async redirect({ url, baseUrl }) {
      return url.startsWith(baseUrl) ? url : baseUrl
    },
    async jwt({ token, trigger, session, user }) {
      if (trigger === 'update') token.name = session?.user?.name
      return {
        ...user,
        ...token,
      }
    },
    async session({ user, session, token }) {
      session.user = user
      session.accessToken = token.accessToken
      session.idToken = token.idToken
      return session
    },
  },

https://next-auth.js.org/configuration/callbacks

signIn

callbacks: {
  async signIn({ user, account, profile, email, credentials }) {
    const isAllowedToSignIn = true
    if (isAllowedToSignIn) {
      return true
    } else {
      // Return false to display a default error message
      return false
      // Or you can return a URL to redirect to:
      // return '/unauthorized'
    }
  }
}

このコールバックは主にEmail Providerを使用する際に有効です。ユーザがVerification Requestを行ったとき(サインインするためのリンクが記載されたメールが送信される前)と、サインインメールに記載されたリンクをユーザが有効化した後の両方でトリガされます。
trueを返せば処理が続行されます。falseを返せばVerify Errorとしてエラーを返すことができ、処理がスタックされます。false以外にもリダイレクト先のURLを設定することもできます(/unauthorizedのようにパス指定できる)

redirect

callbacks: {
  async redirect({ url, baseUrl }) {
    // Allows relative callback URLs
    if (url.startsWith("/")) return `${baseUrl}${url}`
    // Allows callback URLs on the same origin
    else if (new URL(url).origin === baseUrl) return url
    return baseUrl
  }
}

このコールバックはユーザーがコールバックURLにリダイレクトされるたびに呼び出されます(サインイン時やサインアウト時など)。デフォルトではサイトと同じURLのみが許可されますが、リダイレクトコールバックを使って動作をカスタマイズすることができます。リダイレクトさせたくない場合はsignIn()signOut()などでオプションでredirect: falseをすると効かなくなります。

jwt

callbacks: {
  async jwt({ token, account, profile }) {
    // Persist the OAuth access_token and or the user id to the token right after signin
    if (account) {
      token.accessToken = account.access_token
      token.id = profile.id
    }
    return token
  }
}

このコールバックはJWTの作成時(サインイン時など)や更新時(クライアントでセッションにアクセスする時など)に呼び出されます。返される値は暗号化され、クッキーに保存されます。主にクライアントからsessionの呼び出しを行うときに内部でこのコールバックが実行されますが、前述した通り、JWTセッションを使用している場合のみ実行でき、DBでsessionを永続化している場合は実行されません。サインイン時に引数のuserもしくはaccount(OAuthを使用している場合はAccountが渡ってくる)からjwtに追加したいデータを抽出して、引数のtokenに渡すことができます。こうすることでsessionデータにaccsess_tokenなどの機密性が高いデータも渡すことが可能です。

また、access_tokenのリフレッシュなど、jwtの更新を行いたい場合は引数のtriggerupdateをトリガーとしてリフレッシュ処理を行えばOKです。

session

callbacks: {
  async session({ session, token, user }) {
    // Send properties to the client, like an access_token and user id from a provider.
    session.accessToken = token.accessToken
    session.user.id = token.id
    
    return session
  }
}

このコールバックはsessionが呼び出される度に実行されます。主にクライアント側で実行されるuseSession()やサーバーで実行されるgetServerSession/api/auth/sessionにアクセスした時などにsessionが実行され、レスポンスとしてsessionデータが返ってきます。

{
  user: {
    name: string
    email: string
    image: string
  },
  expires: Date // This is the expiry of the session, not any of the tokens within the session
}

▲デフォルトでは機密性の低いデータが返ってくるようになっています。もしsessionデータにaccess_tokenなど機密性が高いデータや別のデータも追加したい場合は前述のjwtのコールバックで追加を行った上で、引数のtokenから取り出してsessionに追加する必要があります。

⑦interfaceの拡張

declare module 'next-auth' {
  interface Session {
    accessToken?: string
    idToken?: string
  }

  interface User {
    accessToken?: string
    idToken?: string
  }
}

declare module 'next-auth/jwt' {
  interface JWT {
    accessToken?: string
    idToken?: string
  }
}

https://github.com/nextauthjs/next-auth/blob/24b0e66ec81d097f0d0e9b0a81fade748d31df5a/packages/core/src/types.ts#L254
sessionuserといったNextAuthから提供される、データの型は最低限の型のみサポートされています。accessTokenなどsessionデータのカスタマイズを行なったら、そのデータの型もサポートしてほしいと思います。その場合はdeclare moduleでインターフェースの更新を行なって下さい。

https://next-auth.js.org/getting-started/typescript#extend-default-interface-properties
▲公式ではtypes/next-auth.d.tsなどを作成してそこでインターフェースの更新を行うように記載がありますが、Userなどの名前空間はfirebaseのSDKや、既存のアプリケーションでもよく使われている命名だと思うので、NextAuthの設定ファイルで一緒に宣言したほうが場合によっては適していると言えます。

⑧NextAuthのハンドラを設定する

export default async function auth(req: NextApiRequest, res: NextApiResponse) {
  return await NextAuth(req, res, {
    ...authOptions,
  })
}

※上記の記述はPage Routerで記載しています。

https://next-auth.js.org/getting-started/rest-api
Next.jsのAPI routeを使用してREST APIとしてクライアントに提供します。
App Routerの場合はapp/api/auth/[...nextauth]/route.ts
Page Routerの場合はpages/api/auth/[...nextauth].tsにハンドラを置きます。
そうすることで以下のルーティングで各処理を呼び出すことができます。基本は上記の処理をおくだけでもうOKです。各エンドポイントは大抵クライアントに提供される関数の処理で内部的にリクエストを行っているので、基本開発者は各エンドポイントに明示的にリクエストすることは少ないと思います。

/api/auth/signin

GETメソッドでリクエストすると、サインイン処理(authorize)が実行されます。

https://next-auth.js.org/getting-started/client#signin
▲クライアントのsignIn()は内部でこのエンドポイントにリクエストしています。

/api/auth/signin/:provider

POSTメソッドでリクエストすると、設定した各プロバイダーのサインイン処理が実行されます。
OAuth プロバイダの場合、このエンドポイントを呼び出すと、ID プロバイダへの認証リクエストが開始されます。
このエンドポイントをPOSTするときはCSRFトークンが必要になります。
CSRFトークン(Cross-Site Request Forgery トークン)はCSRF攻撃から保護するための処置として用いられるトークンです。
CSRFトークン/api/auth/csrfのリクエストでCookieに付与されます。

https://next-auth.js.org/getting-started/client#signin
▲クライアントのsignIn()は内部でこのエンドポイントにリクエストしています。

/api/auth/callback/:provider

サインイン中にOAuthサービスから返ってくるリクエストを処理するエンドポイントです。
checks をサポートしている OAuth 2.0 プロバイダでは、state パラメータをサインインフローの開始時に生成されたものと照合します。
https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2
▲詳しくはOAuthの仕様をご確認ください。

/api/auth/signout

GETメソッドでリクエストすると、サインアウトページへ表示・リダイレクトされます。

POSTメソッドでリクエストすると、ユーザのサインアウトを実行します。
悪意のあるリンクがユーザの同意なしにサインアウトを引き起こすことを防ぐためにPOST送信で実行しています。ユーザーのセッションは、セッションを保存するために選択したフローに応じて、cookie・DBから無効化/削除されます。
またこのリクエストにはCSRFトークンが必要になります。

https://next-auth.js.org/getting-started/client#signout
▲クライアントのsignOut()は内部でこのエンドポイントにリクエストしています。

/api/auth/session

GETメソッドでリクエストすると、クライアントセーフなセッションオブジェクトをレスポンスします。セッションがない場合は空のオブジェクトをレスポンスします。返されるセッションオブジェクトの内容は、前述したセッションコールバックで設定可能です。

https://next-auth.js.org/getting-started/client#usesession
▲クライアントのuseSession()getSession()は内部でこのエンドポイントにリクエストしています。

/api/auth/csrf

GETメソッドでリクエストすると、CSRFトークンを含むオブジェクトを返します。NextAuth.jsでは、すべての認証ルートにCSRF対策が施されています。署名されたHttpOnly、ホストのみのCookieを使用する「ダブルサブミットのCookieメソッド」を使用しています。

「ダブルサブミットのCookieメソッド」を簡単に説明すると、CSRFトークンをCookieとして送信し、フォームデータまたはリクエストヘッダーに同じトークンを含めて送信することでCSRF攻撃のセキュリティ強化することができます。これにより、攻撃者がリクエストを偽造するためには、ユーザーのCookieにアクセスできる必要があり、さらにそのトークンを正確に取得してリクエストに含める必要があります。

このエンドポイントが返すCSRFトークンは、すべてのAPIエンドポイントへのPOST送信において、csrfTokenという名前のフォーム変数として渡す必要があります。

https://next-auth.js.org/getting-started/client#getcsrftoken
▲クライアントのgetCsrfToken()は内部でこのエンドポイントにリクエストしています。

/api/auth/providers

GETメソッドでリクエストすると、設定されているOAuthサービスのリストと、各サービスの詳細(サインインやコールバックURLなど)をレスポンスします。

カスタムサインアップページを動的に生成したり、設定されている OAuth プロバイダごとにどのようなコールバック URL が設定されているかを確認したりするのに用いられます。

https://next-auth.js.org/getting-started/client#getproviders
▲クライアントのgetProviders()は内部でこのエンドポイントにリクエストしています。

⑨クライアントの設定

ポイントは主にSessionProvideruseSessionsignInのあたりです。

SessionProvider

import { SessionProvider } from "next-auth/react"

export default function App({
  Component,
  pageProps: { session, ...pageProps },
}) {
  return (
    <SessionProvider
      session={session}
      basePath="/"
      refetchInterval={5 * 60}
      refetchOnWindowFocus={true}
    >
      <Component {...pageProps} />
    </SessionProvider>
  )
}

basePathはアプリケーションのルートとなるページのパスを渡します。
refetchIntervalは指定した秒数毎にsessionを再取得します。
refetchOnWindowFocusはウィンドウを再度フォーカスする度にsessionを再取得します。
いずれもオプショナルな設定なので、必要に応じて設定して下さい。

useSession

import { useSession } from "next-auth/react"

export default function Component() {
  const { data: session, status } = useSession()

  if (status === "authenticated") {
    return <p>Signed in as {session.user.email}</p>
  }

  return <a href="/api/auth/signin">Sign in</a>
}

data(session)はsessionのコールバックで設定したsessionデータが入ってきます。
statusはそれぞれ、"loading" | "authenticated" | "unauthenticated"が入ってきます。loadingは認証の過程状態。
authenticatedは認証済みの状態。
unauthenticatedは未認証の状態。
ここはカスタムできないのでstatusで処理をハンドリングする場合はここを考慮する必要があります。
他にもupdateが提供されていますが、それを実行すると前述した、jwtのコールバックの引数であるtriggerupdateとなり、更新処理を実行することができます。

signIn

import { signIn } from "next-auth/react"

export default () => (
  <button onClick={() => signIn("google")}>Sign in with Google</button>
)

signInprovidersCredentialsなどで設定した認証プロバイダーに基づいた認証を実行することができます。上記だとgoogleのOAuthを使用した認証を実行することができます。

ルーティングのカスタム

デフォルトのベースパスは/api/authですが、NEXTAUTH_URLでカスタムパスを指定することでカスタマイズ可能です。

NEXTAUTH_URL=https://example.com/myapp/api/authentication

▲このように設定することで/api/auth/signin/myapp/api/authentication/signinにパスを変更することもできます。既存のアプリケーションにも導入しやすいです。

NextAuthの導入に適しているケース

NextAuthの導入が適しているケースは、主にシンプルな認証フローが必要な場合に適しています。

OAuth認証(Google、GitHub、Facebookなど)、Email/Password認証、Credentials認証などの標準的な認証フローを簡単に設定できます。

複数の認証プロバイダーをサポートする必要がある場合

複数のプロバイダーを一元的に管理し、ユーザーに複数のログインオプションを提供する場合に適しています。

Next.jsを使用している場合

NextAuthはNext.jsに特化して設計されており、Next.jsアプリケーションとシームレスに統合できます。

迅速なセットアップが求められる場合

クイックスタートが可能で、数行のコードで認証機能を実装できます。設定ファイルにプロバイダーの情報を追加するだけで動作します。

カスタマイズ性が必要な場合

認証フローの各ステージに対してカスタムロジックを追加できるため、特定のビジネスロジックに基づいた認証処理が求められる場合に適しています。

セッション管理が必要な場合

サーバーサイドセッション、JWTセッションの両方をサポートしており、ユーザーのセッション管理を簡単に行えます。

NextAuthの導入に適していないケース

Next.js以外のフレームワークを使用している場合

NextAuthはNext.js専用のライブラリであり、他のフレームワークやプラットフォーム(React単体、Angular、Vueなど)には対応していません。

非常に複雑な認証フローが必要な場合

特殊な認証プロトコルや複雑な多要素認証(MFA)が必要な場合、NextAuthでは対応が難しいことがあります。そのような場合には、Auth0やFirebase Authenticationなどの専用サービスが適しています。

既に他の認証サービスを利用している場合

既存のシステムでAuth0、Firebase Authentication、AWS Cognitoなどの認証サービスを利用している場合、それらを引き続き使用する方が一貫性が保たれ、移行コストも低くなります。

大規模なエンタープライズ環境での利用

エンタープライズレベルのスケーラビリティやサポートが必要な場合、NextAuthは適していないかもしれません。Auth0やOktaなど、エンタープライズ向けの認証サービスの方が適していることが多いです。

おわり

いかがだったでしょうか?
今回はシンプルかつ網羅的にNextAuthを解説してみました。
他にもNextAuthには様々な機能が備わっているようなので、Next.jsでアプリケーションを開発する際には前向きに導入の検討してみて下さい。
1から実装すると面倒だったり困難だったり色々考慮しなければならない認証周りの機能をシンプルに実装できます。

最後にNextAuthを使った開発に役立ちそうなexampleリポジトリがあったのでこちらを参考にして実際に開発してみて下さい!

https://github.com/nextauthjs/next-auth-example

NextAuth V5も来ますね!

https://authjs.dev/getting-started/migrating-to-v5

Discussion