NextAuth.jsとは
NextAuth.jsとはNext.js向けに作られたライブラリで、認証やセッション管理を簡単に行うことができます。GoogleやTwitter、GitHubなど、OAuthを使った認証サービスが利用できるように設計されています。
Next.js(v13.4)環境においてGoogle OAuthを例にし、NextAuth.jsを使用した認証フローの流れをコードを交えて解説していきます。
また解説するコードのAPIはAPI Routes
ではなく、REST API風に書けるRoute Handlers
で記述しています。
今回作ったソースコードをGitHubにあげてありますのでもし良かったら参考にしてください。
OAuthとは
NextAuth.jsの使い方の説明に入る前に一般的なOAuthという認証フローについて、Googleを例に確認したいと思います。
➀. ユーザーがログインボタンをクリックするとOAuth認証プロセスが開始され、Googleのログインページにリダイレクトされる。
➁. Googleはユーザーに対してアクセス許可のリクエストを行い、ユーザーがアプリケーション(クライアント)にアクセス許可を与えるかどうかを選択。
③. ユーザーがアプリケーションに対し、許可を与えることを選択。
④. アプリケーションと認可サーバーの間でやり取りを行い、認可サーバーからアプリケーションに対してユーザー情報やアクセストークン(認可処理で使用)を取得し、ユーザーのプロフィール情報を元にアプリケーション内でのユーザーの認証やセッション管理を行う。
※ 上図の⑤はユーザーが保持しているGoogleのスプレッドシートなどのリソースサーバーに対し、アクセスする際の認可処理です。この認可処理でアクセストークンが使われますが、詳細な説明は割愛します。
OAuthの認証フローについての詳細を知りたい方は、以下の動画、記事で丁寧にまとめられてますのでご覧になってみてください。
NextAuth.jsは上記のOAuth認証フローを実装するためのライブラリということになります。
NextAuth.jsを使ったOAuth認証フローの実装
次にNextAuth.jsを使って上記の認証フローを実装する方法を解説していきます。
1. Google Cloud Platfom(GCP)の設定
前準備として、Google OAuth認証を利用するためにクライアントIDとシークレットIDが必要になるので以下の記事を参考に取得します。
クライアントIDとシークレットIDを取得したら.envファイルの環境変数GOOGLE_CLIENT_ID
とGOOGLE_CLIENT_SECRET
にそれぞれ記述します。
NEXTAUTH_SECRET=my_ultra_secure_nextauth_secret
NEXTAUTH_URL=http://localhost:3000
GOOGLE_CLIENT_ID=×××××××××××××××××××××××××××××××
GOOGLE_CLIENT_SECRET=××××××××××××××××××××××××××××
2. ログインボタン作成
"use client";
import { signIn } from "next-auth/react"; // 1⃣
import { useSearchParams } from "next/navigation";
export default function Login() {
const searchParams = useSearchParams();
const callbackUrl = searchParams.get("callbackUrl") || "/profile"; // 2⃣
return (
<button onClick={() => signIn("google", { callbackUrl })}> // 3⃣
Login With Google
</button>
);
}
上記のコードを解説していきます。
①のsignIn
関数は、next-auth/reactパッケージからインポートされた関数であり、Next.jsアプリケーション内でのユーザーのログイン処理を実行するための関数です。
signIn関数を使用すると、ユーザーをOAuthプロバイダ(Google)のログインページにリダイレクトし、ログインプロセスを開始することができます。そして、ユーザーが認証プロバイダでログインしてアクセス許可を与えると、アプリケーションにリダイレクトされます。
②は、searchParamsオブジェクトからクエリパラメータのcallbackUrlを取得しています。もしcallbackUrlが存在しない場合、デフォルトとして"/profile"を使用します。このcallbackUrlは、ログインが完了した後に/profileにリダイレクトします。
③は、 ログインボタンがクリックされたときにsignIn関数を呼び出します。signIn関数には、ログインするプロバイダ(この場合は"google")とオプションのパラメータ(callbackUrl)が渡されます。callbackUrlは、ログインが完了した後にリダイレクトするURLを指定します。
上記のコードでは、ログインボタンがクリックされるとsignIn関数が呼び出され、Googleのログインページにリダイレクトされます。callbackUrlが指定されている場合、ログインが完了した後にそのURLにリダイレクトされます。
3. NextAuth.jsを初期化
次にNextAuth.jsをアプリケーションに組み込むために初期化設定を行います。以下の公式ドキュメントに則って書いています。
import { authOptions } from "@/lib/auth";
import NextAuth from "next-auth";
const handler = NextAuth(authOptions); // 1⃣
export { handler as GET, handler as POST }; // 2⃣
import type { NextAuthOptions } from "next-auth"; // 3⃣
import GoogleProvider from "next-auth/providers/google"; // 4⃣
export const authOptions: NextAuthOptions = {
providers: [ // 5⃣
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}),
],
};
①はNextAuth.jsを初期化する処理です。NextAuth関数に初期化オプションを渡すことで認証機能を提供するハンドラー関数を返します。
②はNextAuthの認証機能を処理するためのエンドポイントとして使用されます。GETとPOSTという別名を付けてエクスポートしているため、このハンドラー関数はGETリクエストとPOSTリクエストの両方で使用できます。
③はnext-authパッケージからNextAuthOptions型をインポートしています。これは、NextAuthの設定オプションの型を指定するために使用されます。
④はnext-auth/providers/googleパッケージからGoogleProviderをインポートしています。これは、Google OAuthを使用するためのNextAuthのプロバイダーです。
⑤のprovidersプロパティは、認証に使用するプロバイダーを設定するための配列です。ここでは、Googleプロバイダーを指定しています。他にもGitHubやTwitterなどの認証プロバイダーを使いたい場合は、"next-auth/providers"からインポートする必要があります。
そして、GoogleProvider関数を呼び出してGoogleプロバイダーの設定オブジェクトを作成しています。この設定オブジェクトには、Google OAuthのクライアントIDとクライアントシークレットが含まれています。これらの情報は、環境変数(process.env)から取得しています。
authOptionsを使用してNextAuthを初期化し、認証機能を提供するハンドラー関数を作成し、そのハンドラー関数をGETとPOSTのエンドポイントとしてエクスポートすることで、アプリケーション内の他の場所からこのAPIにアクセスできるようになります。
上記のNextAuth関数を使って初期化を行ったことにより、認可サーバーからユーザーへの許可リクエスト、認可サーバーからユーザー情報やアクセストークンのデータをアプリケーションが受け取ることができるようになります。
4. 認可サーバーからユーザー情報を取得
次に認可サーバーから渡ってきたデータをアプリケーションで取得し、セッションとして管理する流れを説明します。
実は取得したデータは先程作成したNextAuth
関数のcallbacks
というオプションに渡ってきます。
import type { NextAuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
export const authOptions: NextAuthOptions = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}),
],
pages: { // 1⃣
signIn: "/login",
},
session: { // 2⃣
strategy: "jwt",
},
callbacks: {
jwt: ({ token, user }) => { // 3⃣
if (user) {
const u = user as unknown as any;
return {
...token,
id: u.id,
};
}
return token;
},
session: ({ session, token }) => { // 4⃣
return {
...session,
user: {
...session.user,
id: token.id,
},
};
},
},
};
①は認証ページの設定をオブジェクトとして指定します。signInプロパティは、サインインページのURLを指定しています。
②はセッションの設定をstrategyとして指定します。strategyプロパティは、JWT(JSON Web Token)を使用してセッションを管理することを示しています。
③は認可サーバーから取得したデータが渡ってきたときに実行されます。このコールバックでは、ユーザーが存在する場合にidをトークンに追加しています。そしてtokenを同じcallbacks
のsession
に渡す必要があるのでreturnしています。ここでsession
にtokenを渡さないとユーザー情報をセッションとして管理できなくなります。
最後の④ですがセッションの生成時に実行されます。このコールバックでは、jwt
から受け取ったtokenと、渡ってきたsessionをマージして、戻り値として返しています。これによってアプリケーション内でユーザー情報をセッションとして管理・取得できるようになります。
5. アプリケーション内でセッションを管理・取得
最後にユーザー情報をセッションとして取得し、ページを切り替える方法を紹介します。
"use client";
import { SessionProvider } from "next-auth/react";
type Props = {
children?: React.ReactNode;
};
export const NextAuthProvider = ({ children }: Props) => { // 1⃣
return <SessionProvider>{children}</SessionProvider>;
};
import "./globals.css";
import { NextAuthProvider } from "./provider";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<NextAuthProvider>{children}</NextAuthProvider>
</body>
</html>
);
}
import { getServerSession } from "next-auth"; // 2⃣
import { authOptions } from "@/lib/auth";
export default async function Profile() {
const session = await getServerSession(authOptions); // 3⃣
const user = session?.user;
return (
<>
<p>Profile Page</p>
{!user ? (
<p>ユーザー情報が取得できていません。。。</p>
) : (
<>
<div>
<img
src={user.image ? user.image : "/images/default.png"}
className="max-h-36"
alt={`profile photo of ${user.name}`}
/>
</div>
<div className="mt-8">
<p className="mb-3">Name: {user.name}</p>
<p className="mb-3">Email: {user.email}</p>
</div>
</>
)}
</>
);
}
①でnext-auth/react
からインポートしたSessionProvider
でpropsから受け取ったchildrenをラップしたコンポーネントをデフォルトエクスポートしています。SessionProvider
でラップすることにより、子コンポーネントはユーザーのセッション情報を取得することができるようになります。
②はnext-authモジュールからgetServerSession関数をインポートしています。この関数は、サーバーサイドでセッション情報を取得するために使用されます。
③はgetServerSession関数を使用してセッション情報を取得しています。authOptionsを引数として渡しており、このオプションはサーバーサイドでのセッション情報の取得方法を指定します。取得したセッション情報はsession変数に格納されます。
そして、プロフィールページの表示部分では、userの有無によって条件分岐しています。userが存在しない場合は、「ユーザー情報が取得できていません。。。」と表示されます。userが存在する場合は、プロフィール写真(user.image)や名前(user.name)などのユーザー情報が表示されます。
まとめ
NextAuth.jsはデータベースにユーザー情報をセッションとして管理できたり、Next.jsだけではなくSvelteなどのフレームワークでも使えるように改良を進めているそうです。
私自身まだまだNextAuth.jsを深く理解できていないのでもし今後実務で使用することがあればキャッチアップして、インプットしたことはこちらの記事にアウトプットしていければと思っています。
終わりに
最後まで記事を読んでいただきありがとうございました。
間違い等ありましたらご指摘いただけると幸いです
参考