【NextAuth】AppRouter + API (+漏れがちな設定

NextAuthを使ってAPIで認証するのに苦戦したので雑にメモ。

# 環境変数
よくわかってないけど設定しないとうまく動かない。
これが原因と分からずずっと動かなかった。

・`NEXTAUTH_URL`
認証系のURLの設定
https://next-auth.js.org/configuration/options#nextauth_url

・`NEXTAUTH_SECRET`
認証してJWTをなんやかんやするのに必要らしい
https://next-auth.js.org/configuration/options#nextauth_secret

```.env
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=secret
```

# オプション作る
NextAuthの設定的なもの。

```ts:src\lib\nextAuthOptions.ts
import { NextAuthOptions } from "next-auth";
import GithubProvider from "next-auth/providers/github"
import CredentionsProvider from 'next-auth/providers/credentials'

const authOptions: NextAuthOptions = {
  secret: process.env.NEXTAUTH_SECRET,
  // 必要なプロバイダーは複数設定できる
  providers: [
    // Githubの認証
    GithubProvider({
      clientId: process.env.GITHUB_ID || '',
      clientSecret: process.env.GITHUB_SECRET || '',
    }),
    // Credentialsは普通のIDパスワード方式
    CredentionsProvider({
      name: 'Credentials',
      credentials: {
        username: {
          label: 'Username',
          type: 'text',
          placeholder: 'username'
        },
        password: {
          label: 'Password',
          type: 'password'
        }
      },
      async authorize(credentials, req) {
        // ここでユーザー認証を行う
        // テスト用のユーザー
        const user = { id: '1', name: 'Taro', email: 'test@example.com' }
        if (user) {
          return user
        } else {
          return null
        }
      }
    })
  ],
  // ログインページとかのURLを変えたい場合pagesを設定する
  // pages: {
  //   signIn: '/auth/signin',
  //   signOut: '/auth/signout',
  //   error: '/auth/error', // Error code passed in query string as ?error=
  //   verifyRequest: '/auth/verify-request', // (used for check email message)
  //   newUser: '/auth/new-user'
  // }
}
export default authOptions;
```

# ハンドラー作る
`AppRouter`を使う場合はRoute Handlerというやつを使うらしい。

```ts:src/app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth";
import authOptions from "@/lib/nextAuthOptions";

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST }
```

# Provider
アプリ内でセッションが使えるようにする。

```tsx:NextAuthProvider.tsx
"use client";
import { SessionProvider } from "next-auth/react";
import { Session } from "next-auth";

export const NextAuthProvider = ({
  children,
  session,
}: {
  children: React.ReactNode;
  session: Session | null;
}) => {
  return (
    <SessionProvider session={session} refetchOnWindowFocus={false}>
      {children}
    </SessionProvider>
  );
};
```
ルートのレイアウトでプロバイダーを使用

```tsx:src\app\layout.tsx
export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const session = await getServerSession(authOptions);
  return (
    <html lang="ja">
      <body className={inter.className}>
        <NextAuthProvider session={session}>{children}</NextAuthProvider>
      </body>
    </html>
  );
}
```
# ページとか
`signIn`には引数でプロバイダーを指定できる。
`signIn()`でログインページに飛ぶ

```tsx:ExampleTopPage.tsx
'use client'
import React from 'react'
import { signIn, signOut } from 'next-auth/react'

function page(req, res) {
  const onClickLogin = async () => {
    signIn()
  };

  const onClickLogout = async () => {
    signOut()
  }
  return (
    <div>
      <div>
        <button onClick={onClickLogin}>ログイン</button>
      </div>
      <div>
        <button onClick={onClickLogout}>ログアウト</button>
      </div>
    </div>
  )
}

export default page
```


# API入口
認証で制限をかけたAPI。
ログインできていたらデータが取れる。
`Response`ではなく`NextResponse`で作成しないとエラーが出る。

```ts:api/api-test/route.ts
import { getServerSession } from 'next-auth/next';
import { NextApiRequest, NextApiResponse } from 'next'
import authOptions from '@/NextAuthConfig/Option'

export const GET = async (req: NextApiRequest, res: NextApiResponse) => {
  const session = await getServerSession(authOptions)
  console.log(session);
  
  if (session) {
    return NextResponse.json({ data: "Protected data" })
  }
  return NextResponse.json({ message: "Not authenticated" }, { status: 401 })
}
```