面倒なログインはNextAuthにやらせよう

![social-media-card.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/0d64eaff-3091-d845-1b29-65c2d4b988b1.png)
## 前書き
いつからでしょうか:sweat_smile:、何か新しいサービスを試す際に、GoogleアカウントやGitHubアカウントを利用してログインするしかやらなくなった。

ユーザーネームとパスワードを設定し:writing_hand_tone1:、メール認証を済ませて初めて利用可能になるようなサービスは自分でも嫌なので、多くの人にとっても面倒でしょう、これからサービス作る際に、いっそう認証周り全部OAuthに任せようと思いました。

いろいろ調べた結果、最終的に`NextAuth`に辿り着きました。

## NextAuthとは

[公式ドキュメント](https://next-auth.js.org/)

`NextAuthとは何か？`とChatGPTに尋ねたら、以下のような回答が返ってきました。
![1C05E983-470A-449E-A03A-4B3B56827E2D_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/89bc84ab-aae4-c4c6-5765-bc6d2d8328e3.jpeg)

大まか合ってますが、現時点では`SvelteKit`, `SolidStart`でも利用することができるらしいです。

## NextAuthの使い方

`NextAuth`の使い方は非常にシンプルです、`Next.js`制のプロジェクトでは三つのステップで基本形ができます。

- `NextAuth`インストール

```shell
npm install next-auth or yarn add next-auth
```

- 認証プロバイダーを設定する

認証プロバイダーを設定することによって、ユーザーがログインする方法を決められます。
たとえば、GitHub、Google、Facebook、TwitterなどのOAuthプロバイダーを使用できます。
次のように、`pages/api/auth/[...nextauth].js`というファイルを作成して、認証プロバイダーと`secret`を設定します。
`secret`の値は下記のコマンドで生成できます、セキュリティに関連する目的に使用されるため、設定しないとエラーになる可能性があります。
```
openssl rand -base64 32
```

```ts:pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth'
import GithubProvider from 'next-auth/providers/github';

export default NextAuth({
  providers: [
    GithubProvider({
      clientId: process.env.GITHUB_ID,
      clientSecret: process.env.GITHUB_SECRET,
    }),
  ],
 secret: process.env.SECRET,
})
```

`Github`のプロバイダを設定した場合、下記のURlにアクセスして、新規OAuth Appを作る必要がある、`New OAuth App`から作れる。
https://github.com/settings/developers



![E7CEFE09-0A72-4515-A3BC-BA0A2907F8A0_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/f36bafa7-dd0d-834a-117a-fc704c62cb4b.jpeg)

`Application name`、`Homepage URL` と`callback URL` 指定すれば`Client ID`と`Client secrets`を取得できる。

![FA13B752-7EC6-4614-A9C1-93D14886222F.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/7406256e-eb7e-683c-00da-9a2cfac2cf71.jpeg)

`Homepage URL`指定したURLは`.env`に追加する、定義しなければ`http://localhost:3000/`になる。
```shell:.env
NEXTAUTH_URL=http://localhost:3002/
```

他のプロバイダ使用する場合も手順が大体同じ。

- 認証機能をアプリケーションに実装する

最後に、認証機能をアプリケーションに実装する必要があります。たとえば、ユーザーがログインしているかどうかを確認するために、`useSession`というNext.jsのフックを使用できます。次のように、pages/index.jsというファイルにuseSessionをインポートして使用します。


```tsx:/pages/_app.jsx
import { SessionProvider } from "next-auth/react"

export default function App({
  Component, pageProps: { session, ...pageProps }
}) {
  return (
    <SessionProvider session={session}>
      <Component {...pageProps}/>
    </SessionProvider>
  )
}
```
ログインするために`signIn`メソッドも用意されてます、プロバイダ指定してログインする場合、プロバイダー名をパラメーターとして渡すことができます、`signIn('github')`。
```tsx:/pages/index.tsx
import { useSession, signIn, signOut } from "next-auth/react"

export default function Home() {
  const { data: session } = useSession()
  if(session) {
    return <>
      Signed in as {session.user.email} <br/>
      <button onClick={() => signOut()}>Sign out</button>
    </>
  }
  return <>
    Not signed in <br/>
    <button onClick={() => signIn()}>Sign in</button>
  </>
}
```
この状態でサーバーを立ち上げると、下記の画面が表示されます。

![1C6CCBF0-FA1C-42A6-9FD7-6FE4CA712BFD_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/9ddd3b33-8295-c081-df62-4af89939f34f.jpeg)
`Sign in`ボタンをクリックすれば`github`用のOAuthログイン画面が表示されます。
![3F74D54D-2332-46D8-A740-C9BE1F40A227.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/717d2c14-0840-2d7e-ef0f-adca03f0a827.jpeg)
クリックすれば、`github`のOAuthログイン画面にリダイレクトされて、許可すれば、ホームページにまたリダイレクトされます。
![2DDCE51A-BFAA-4219-8278-7C9BBFF8D23E.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/ed7d84cc-e7a3-7f7b-3c95-bb1f7abcc4d8.jpeg)
githubに登録されてメールアドレスが表示されることが確認できたら、実装完了です。
![E2B4525B-824A-4677-9A07-ADC6D8924804_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/a96aad35-d019-ae40-616e-3f45899ca7e1.jpeg)

### データベース使用する場合
先ほどのログインはセッションにユーザー情報を保存してましたが、データベースに保存することもできます。
その場合、`NextAuth`が期待するテーブル構造を用意してあげる必要があります。
詳細は[公式ドキュメント](https://authjs.dev/reference/adapters#models)を参照してください。
![5B492A7C-166A-46A4-9556-9B3E94A130EA.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/3d9226b8-d881-cf7d-45ae-d9be48028f97.jpeg)
prisma使用する場合、モデルの定義は下記の記述になります。
```
model Account {
  id                 String  @id @default(cuid())
  userId             String
  type               String
  provider           String
  providerAccountId  String
  refresh_token      String?  @db.Text
  access_token       String?  @db.Text
  expires_at         Int?
  token_type         String?
  scope              String?
  id_token           String?  @db.Text
  session_state      String?
  oauth_token_secret String?
  oauth_token        String?

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  accounts      Account[]
  sessions      Session[]
}

model VerificationToken {
  identifier String
  token      String   @unique
  expires    DateTime

  @@unique([identifier, token])
}
```