Open8
Auth.jsの導入
プロジェクト作成
- Next.jsのプロジェクトを作成する
npx create-next-app@latest
- プロジェクト名は
next-auth-app
にする
インストール
- Auth.jsはNext.js以外もサポートしているようだが、まずはNext.jsから始める
- https://authjs.dev/getting-started/installation
- npmでプロジェクトにインストールすれば良さそう
npm install next-auth@beta
- 2024/9/26時点ではまだbeta版らしい
- なお、現時点のNext.jsとauth.jsのバージョンは以下のとおり
"next": "14.2.13"
"next-auth": "^5.0.0-beta.21"
- 続いて、ターミナルから
AUTH_SECRET
(秘密鍵)を生成してやる必要があるようだnpx auth secret
- ルート直下に
.env.local
ファイルが自動生成される -
.env.local
AUTH_SECRET="1B73n3qYFSz0u5VhDYO1dVlJg/+ipc4eYabQRB0dUFY="
- 設定
- 次にルート直下に
./auth.ts
を追加する -
./auth.ts
import NextAuth from "next-auth" export const { handlers, signIn, signOut, auth } = NextAuth({ providers: [], })
- ここで使用する認証サービスを指定するっぽい。詳細は後述
- Googel
- GitHub
- その他...
- その次にルーティングを用意する→
/app/api/auth/[...nextauth]/route.ts
-
/app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth" // Referring to the auth.ts we just created export const { GET, POST } = handlers
- セッションを維持するためのオプションのミドルウェアを追加する。
- 呼び出されるたびにセッションの有効期限を更新するらしい。
-
middleware.ts
export { auth as middleware } from "@/auth"
- 次にルート直下に
認証
- https://authjs.dev/getting-started/authentication
- Auth.jsは4つの認証方法をサポートしているらしい
- OAuth
-
Auth.jsには80以上のプロバイダーがあらかじめ設定されています。 私たちは、最も人気のある20のプロバイダーを常にテストしています。 以下からプロバイダーを選んでウォークスルーを見ることもできますし、サイドバーからお好みのプロバイダーを見つけて詳細を見ることもできます。
-
- Magic Links
-
このログイン・メカニズムは、ユーザーがログイン・フォームにEメールアドレスを入力することから始まる。 その後、提供されたEメールアドレスにVerification Tokenが送信される。 その後、ユーザーは24時間以内にメール本文にあるリンクをクリックしてトークンを「消費」し、アカウントを登録しなければならない。
-
- Credentials
-
Auth.jsを外部認証メカニズムでセットアップしたり、単にユーザー名とパスワードを使用するには、Credentialsプロバイダを使用する必要があります。 このプロバイダは、ログインフォームに挿入された認証情報(ユーザー名/パスワードなど)を、プロバイダ設定のauthorizeコールバックを介して認証サービスに転送するように設計されています。
-
- WebAuthn
-
WebAuthn プロバイダーは、すべてのフレームワーク・インテグレーションと、それをサポートする予定のデータベース・アダプターを変更する必要があります。 そのため、WebAuthn プロバイダは現在、以下のフレームワーク・インテグレーションとデータベース・アダプタでのみサポートされています。 より多くのフレームワークとアダプタのサポートは近日中に予定されています。
-
- OAuth
- とりあえず、OAuthとCredentialsの使い方を理解しておけばよさそう
OAuth
GitHub
GitHub側の設定
- まずはGitHub の開発者ダッシュボードで OAuth アプリケーションをセットアップする必要があるみたい。
- 詳細は公式を参照するべし -> https://authjs.dev/guides/configuring-github
- GitHub側での設定が終わったら、
.env.local
を編集する。先程ターミナルで生成したAUTH_SECRET
とは別に以下を追加する -
.env.local
AUTH_GITHUB_ID={CLIENT_ID} AUTH_GITHUB_SECRET={CLIENT_SECRET}
プロバイダーの設定
- GitHub プロバイダをパッケージからインポートし、Auth.js 設定ファイルで先ほど設定した providers 配列に渡す
-
auth.ts
import NextAuth from "next-auth" import GitHub from "next-auth/providers/github" export const { handlers, signIn, signOut, auth } = NextAuth({ providers: [GitHub], })
-
- NextAuthが返すハンドラを
api/auth/[...nextauth]/route.ts
ファイルに追加し、Auth.jsがリクエストに対して実行できるようにする。-
api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth" export const { GET, POST } = handlers
-
コンポーネントの作成
- Navbarのようなアプリケーションのどこかにサインインボタンを追加する。 このボタンをクリックすると、Auth.jsのサインインが開始される。
サーバーコンポーネント
./components/sign-in.tsx
import { signIn } from "@/auth"
export default function SignIn() {
return (
<form
action={async () => {
"use server"
await signIn("github")
}}
>
<button type="submit">Signin with GitHub</button>
</form>
)
}
クライアントコンポーネント
./components/auth/signin-button.tsx
"use client"
import { signIn } from "next-auth/react"
export function SignIn() {
return <button onClick={() => signIn()}>Sign In</button>
}
認証
- https://authjs.dev/getting-started/session-management/login
- 認証されると、ユーザーはサインインを開始したページにリダイレクトされる。
- サインイン後、ユーザーを他の場所(例:
/dashboard
)にリダイレクトさせたい場合は、サインインオプションのredirectTo
にターゲットURLを渡すことで可能。
app/components/signin-button.tsx
import { signIn } from "@/auth.ts"
export function SignIn() {
return (
<form
action={async () => {
"use server"
await signIn("github", { redirectTo: "/dashboard" })
}}
>
<button type="submit">Sign in</button>
</form>
)
}
セッションの利用
- https://authjs.dev/getting-started/session-management/get-session
- Auth.jsのライブラリは、誤って機密性の高いユーザー情報を公開しないように、デフォルトではセッション内でユーザー情報の以下のサブセットのみを公開するらしい。
name
email
image
- その他については→を参照: https://authjs.dev/guides/extending-the-session
サーバーコンポーネントでの利用
- サーバーコンポーネント内でセッション情報を取得するには、auth()関数を使用する。
./components/UserAvatar.tsx
import { auth } from "../auth"
export default async function UserAvatar() {
const session = await auth()
if (!session.user) return null
return (
<div>
<img src={session.user.image} alt="User Avatar" />
</div>
)
}
クライアントコンポーネントでの利用
- クライアントサイドでは、
useSession
フックまたはSessionProvider
を使用してセッション情報にアクセスできるらしい。 - ただ実際は、これらの使用頻度はそれほど高くなく、 一般的には、パフォーマンスとセキュリティを最適化するために、サーバー側のレンダリングをフルに活用することが多いらしい。
app/admin/dashboard.tsx
"use client"
import { useSession } from "next-auth/react"
export default function Dashboard() {
const { data: session } = useSession()
if (session?.user?.role === "admin") {
return <p>You are an admin, welcome!</p>
}
return <p>You are not authorized to view this page!</p>
}
app/admin/page.tsx
import { SessionProvider } from "next-auth/react"
import { Dashboard } from "./Dashboard"
export default function Administrator() {
return (
<SessionProvider>
<Dashboard />
</SessionProvider>
)
}
リソースの保護
- https://authjs.dev/getting-started/session-management/protecting
- 一般的に、セッションをチェックし、アクティブなセッションが見つからない場合、ユーザをログインページにリダイレクトするか、単に
401: Unauthenticated
レスポンスを返すようなアクションを取ることで、経路を保護することができる。
Pages, API Routesは省略
Pages
サーバーコンポーネント
app/server/page.tsx
import { auth } from "@/auth"
export default async function Page() {
const session = await auth()
if (!session) return <div>Not authenticated</div>
return (
<div>
<pre>{JSON.stringify(session, null, 2)}</pre>
</div>
)
}
クライアントコンポーネント
import { auth } from "../auth"
export default function Dashboard({ session }) {
if (!session.user) return <div>Not authenticated</div>
return <div>{JSON.stringify(session, null, 2)}</div>
}
export async function getServerSideProps(ctx) {
const session = await auth(ctx)
return {
props: {
session,
},
}
}
import type { AppProps } from "next/app"
import { SessionProvider } from "next-auth/react"
export default function MyApp({
Component,
pageProps: { session, ...pageProps },
}: AppProps) {
return (
<SessionProvider session={session}>
<Component {...pageProps} />;
</SessionProvider>
)
}
API Routes
- Next.jsでは、auth関数を使ってAPIルートハンドラをラップすることができます。
- リクエストパラメータには、有効なセッションをチェックするための認証キーが設定されます。
サーバーコンポーネント
./app/api/admin/route.ts
import { auth } from "@/auth"
import { NextResponse } from "next/server"
export const GET = auth(function GET(req) {
if (req.auth) return NextResponse.json(req.auth)
return NextResponse.json({ message: "Not authenticated" }, { status: 401 })
})
クライアントコンポーネント
./pages/api/admin.ts
// TODO: Update once server-side API methods are implemented for pages router again
// import { auth } from "../../auth"
// import { getSession } from "next-auth/react"
import { NextApiRequest, NextApiResponse } from "next"
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// const session = await auth(req, res)
// const session = await getSession(req, res)
const url = `${req.headers["x-forwarded-proto"]}://${req.headers.host}/api/auth/session`
const sessionRes = await fetch(url)
const session = await sessionRes.json()
if (!session.user) {
return res.status(401).json({ message: "Not authenticated" })
}
return res.json({ data: "Protected data" })
}
Next.js Middleware
- Next.js 12+では、ミドルウェアを使うのがもっとも簡単です。
middleware.ts
は、root pagesディレクトリに次のような内容で作成します。
middleware.ts
export { auth as middleware } from "@/auth"
- 次に、auth.tsファイルにauthorizedコールバックを定義します。 詳細はリファレンスドキュメントを参照してください。
auth.ts
import NextAuth from "next-auth"
export const { auth, handlers } = NextAuth({
callbacks: {
authorized: async ({ auth }) => {
// Logged in users are authenticated, otherwise redirect to login page
return !!auth
},
},
})
- ミドルウェアの内部でさらにロジックを実装したい場合は、authメソッドをラッパーとして使うこともできる。
middleware.ts
import { auth } from "@/auth"
export default auth((req) => {
if (!req.auth && req.nextUrl.pathname !== "/login") {
const newUrl = new URL("/login", req.nextUrl.origin)
return Response.redirect(newUrl)
}
})
- また、正規表現を使用して複数のルートにマッチさせたり、特定のルートを否定して残りのすべてのルートを保護することもできます。 次の例では、ファビコンや静的画像などのパスでミドルウェアを実行することを避けています。
middleware.ts
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
}