NextAuth で Github 認証を簡単に作ってみた

<!-- ================================================= -->
# 1.はじめに
<!-- ================================================= -->

NextAuth.js を使って Next.js を簡単に Github 認証させてみたので、簡単な使い方を紹介したいと思います。  
https://next-auth.js.org/

この記事はTDCソフト株式会社Advent Calendarの4日目です。
https://qiita.com/advent-calendar/2022/tdc

<!-- ================================================= -->
# 2.NextAuth.js とは
<!-- ================================================= -->

NextAuth.jsは、Next.jsのOSSライブラリです。
OAuthを意識することなく、様々なプロバイダーと連携させることができます。
また、現在も頻繁に更新されており、コミュニティーは活発的です。
Github star 12.5k（2022/12/01現在）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/eacf963e-0456-1f2d-c4e7-c01ca12738b6.png" width="500">

<!-- ================================================= -->
# 3.手順
<!-- ================================================= -->

## 3.1 プロジェクトの作成と NextAuth.js の導入

Next.js のプロジェクトを作成します。  
今回は Typescrip で作成しています。

```
npx create-next-app@latest --ts
```

NextAuth.js をインストールします。

```
npm install next-auth
```

## 3.2 [...nextauth]ファイルの作成

`pages/api`のフォルダ内に`auth/[...nextauth].ts`を作成します。  
これが NextAuth.js での設定ファイルとなります。

```ts:auth/[...nextauth].ts
import NextAuth from "next-auth";

export default NextAuth({
  providers: [],

  secret: process.env.SECRET,

  session: {
    strategy: "jwt",
  },

  jwt: {},

  pages: {},

  callbacks: {},

  events: {},

  debug: false,
});
```

## 3.3 NextAuth の設定

NextAuth の設定を行っていきます。  
`pages/api/auth/[...nextauth].ts`を編集します。

### 3.3.1 providers

認証を行うプロバイダーを記載します。  
Github での認証を行いたいので、下記の通り作成します。  
それぞれの値は.env ファイルを作成し、あとで環境変数として作成します。  
今回は紹介しませんが、様々な認証プロバイダーを利用することができます。  
https://next-auth.js.org/providers/

```diff_typescript:auth/[...nextauth].ts
+ // 使用するプロバイダーをimportする
+ import GithubProvider from "next-auth/providers/github";

export default NextAuth({
  providers: [
+     GithubProvider({
+       clientId: process.env.GITHUB_ID || "",
+       clientSecret: process.env.GITHUB_SECRET || "",
+     }),
  ],

 ...
});

```

### 3.3.2 secret

Cookie の署名/暗号化などに利用する値を設定します。  
公式ドキュメントでは下記コマンドで生成できると記載されていました。  
`openssl rand -base64 32`

### 3.3.3 session

セッションの形式を選択します。  
デフォルトでは jwt ですが、Database を利用したセッションを利用した場合のみ、database を入力します。

### 3.3.4 jwt pages callbacks events debug

その他の項目については、今回は使用しないため、紹介を省きます。  
例として下記のようなことができます。

- jwt、session の生成方法
- signin ページの指定
- callback した際の指定
- ユーザーの event 時の処理

## 3.4 .env ファイルの作成

`GITHUB_ID` `GITHUB_SECRET` `SECRET` が記載されている.env ファイルをプロジェクトフォルダ内に作成します。  
`SECRET`はランダムな文字列を設定します。  
`GITHUB_ID` と `GITHUB_SECRET` については GitHub よりアプリとして登録し、生成する必要があります。  
そのため、下記 URL からログインして、アプリを登録します。  
https://github.com/settings/apps

左側のメニューにある`Oauth Apps`を押して、`Register a new application`を押します。
![Github連携1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/d1be0c95-6e79-5447-f085-c26232e137da.png)
それぞれの項目を入力し、アプリを登録します。  
![Github連携2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/e27220c4-d101-0e9f-b304-9d3f9c394b8a.png)
登録が完了すると Client secrets が発行できる場所があるので、`Generate a new client secret` を押して、Client secrets を発行します。  
![Github連携3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/bd8b3ff6-586d-1aad-ccfb-c57d09f39d75.png)
発行ができたら、Client ID と Client secret を それぞれ、env ファイルに記載します。

```env:.env
GITHUB_ID=Your Client ID
GITHUB_SECRET=Your Client secret
SECRET=Random String
```

## 3.5 セッションをすべてのページで確認できるようにする

すべてのページでセッションを取得できるよう`pages/_app.tsx`に追加します。

```diff_tsx:pages/_app.tsx
import '../styles/globals.css'
import type { AppProps } from 'next/app'

+ import { SessionProvider } from "next-auth/react"
+ import type { Session } from "next-auth"

- export default function App({ Component, pageProps }: AppProps) {
-   return <Component {...pageProps} />
- }

+ export default function App({
+   Component,
+   pageProps: { session, ...pageProps },
+ }: AppProps<{ session: Session }>) {
+   return (
+     <SessionProvider session={session}>
+       <Component {...pageProps} />
+     </SessionProvider>
+   )
+ }
```

## 3.6 認証ページの作成

認証が必要なページを作成します。  
今回は`page/index.tsx`に認証ページを作成します。  
また、`useSession`よりユーザーのセッションを取得することができます。  
セッションにはユーザーの情報が含まれているため、  
セッションがない場合は、ログインボタン、セッションが存在する場合はセッションの情報を表示しています。

```tsx:page/index.tsx
import { signIn, signOut, useSession } from "next-auth/react";

export default function Home() {
  const { data: session, status } = useSession();
  const loading = status === "loading";
  return (
    <div>
      {!session && (
        <>
          {loading ? (
            <>Loading ...</>
          ) : (
            <>
              Not signed in <br />
              <button onClick={() => signIn()}>Sign in</button>
            </>
          )}
        </>
      )}
      {session && (
        <>
          name:{session.user.name} <br />
          image:
          <img
            src={session.user.image}
            alt="icon"
            style={{ width: "100px", height: "100px" }}
          />
          <br />
          <button onClick={() => signOut()}>Sign out</button>
        </>
      )}
    </div>
  );
}
```

<!-- ================================================= -->
# 4.実際の動作
<!-- ================================================= -->

実際の動作はこんな感じです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/ef121c53-2e43-4674-e0a3-40cd082ade66.png" width="500">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/6fe1dc1f-bb57-7321-b757-82be09746ce4.png" width="500">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1240861/6e6b053f-ec05-5a17-e6dd-a0e751a6e710.png" width="500">

<!-- ================================================= -->
# 5.感想
<!-- ================================================= -->

NextAuth が対応している provider であれば、Oauth 認証や、その後の cookie 作成まで行ってくれるため、簡単に認証を作成することができました。  
複数 provider を入力することで、ユーザーには選択肢を与えながら、アプリは session で統一されていることが、便利だと感じました。  
なにかの参考になれば幸いです。

<!-- ================================================= -->
# 参考
<!-- ================================================= -->

https://refine.dev/blog/nextauth-google-github-authentication-nextjs/
https://zenn.dev/thim/articles/7e3fc6a67de764daf50a