t3-stack入門 (4) NextAuth.jsでEmail/Passwordでログインできるようにする

B2Bエンジニアにソーシャルログインなんて必要ありません(そもそもインターネットに出れないので使えません)。
NextAuth.jsでソーシャルログインをやめEmail/Passwordでログインできるように変更します。

## ハッシュ化するパッケージを導入する

```
npm install bcrypt @types/bcrypt
```

## パスワードカラムを追加＆初期データを設定する
prisma/schema.prismaのUserモデルにパスワード用のカラムを追加します。
```diff
model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
+  crypted_password String?
  image         String?
  accounts      Account[]
  sessions      Session[]
}

```
このままだとだれもログインできないので初期ユーザを追加するprisma/seed.tsを作成します。
```typescript
import { PrismaClient } from "@prisma/client";
import bcrypt from "bcrypt";
const prisma = new PrismaClient();

async function main() {
  prisma.$connect();
  const saltRounds = 10;
  const password = "test";
  const hashedPassword = await bcrypt.hash(password, saltRounds);
  const testUser = await prisma.user.upsert({
    where: { email: "test@test.com" },
    update: {},
    create: {
      id: "clbopauhz0000umekzyiiw4ww",
      email: "test@test.com",
      name: "テストユーザ",
      crypted_password: hashedPassword,
    },
  });

  const testUser2 = await prisma.user.upsert({
    where: { email: "test2@test.com" },
    update: {},
    create: {
      id: "clbopauja0002umeko9x3bc63",
      email: "test2@test.com",
      name: "テストユーザ2",
      crypted_password: hashedPassword,
    },
  });
  console.log({ testUser, testUser2 });
}
main()
  .then(() => {
    console.log("finished");
  })
  .finally(() => {
    prisma.$disconnect();
  });
```
ts-nodeをインストールしてseed.tsを実行します。そのままだと実行できずオプションが必要でした。
```shell-session
~/t3-todo-example$ ts-node --compiler-options '{"module":"CommonJS"}' prisma/seed.ts
{
  testUser: {
    id: 'clbopauhz0000umekzyiiw4ww',
    name: 'テストユーザ',
    email: 'test@test.com',
    emailVerified: null,
    crypted_password: '$2b$10$4JHMU.D4Xd4z.czfIWyVsOfTZkErxb0bVZFYK8puDJKD/cH4VunhO',
    image: null
  },
  testUser2: {
    id: 'clbopauja0002umeko9x3bc63',
    name: 'テストユーザ2',
    email: 'test2@test.com',
    emailVerified: null,
    crypted_password: '$2b$10$4JHMU.D4Xd4z.czfIWyVsOfTZkErxb0bVZFYK8puDJKD/cH4VunhO',
    image: null
  }
}
finished
```

## CredentialProviderを設定する

src/pages/api/auth/[...nextauth].tsをCredentialProviderを利用するように変更します
```typescript
import NextAuth, { type NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from "bcrypt";

import { env } from "../../../env/server.mjs";
import { prisma } from "../../../server/db/client";

export const authOptions: NextAuthOptions = {
  providers: [
    CredentialsProvider({
      // サインインフォームに表示する名前 (例: "Sign in with...")
      name: "E-mail/Password",
      credentials: {
        email: {
          label: "E-mail",
          type: "email",
          placeholder: "E-mail",
        },
        password: { label: "パスワード", type: "password" },
      },
      async authorize(credentials, req) {
        const email = credentials?.email;
        const password = credentials?.password || "";
        const user = await prisma.user.findUnique({ where: { email } });
        if (user == null) {
          return null;
        }
        if (bcrypt.compareSync(password, user.crypted_password || "")) {
          const { crypted_password, emailVerified, ...user2 } = user;
          return user2;
        } else {
          return null;
        }
      },
    }),
  ],
};

export default NextAuth(authOptions);
```
これでEmail/Passwordでログインできるようになりました。
![signin_credential.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508186/7d3f907e-495a-1e9d-0a36-f50af19d296f.png)

ただ、なぜか  adapter: PrismaAdapter(prisma)を削除しないと、ログインは成功するのですがSessionが取得できなくなってしまいました。
また、adapterを設定しない場合、いったいどこにSession情報が保存されているんでしょうか。メモリに保存されているのかなと思ったけどdevサーバを再起動してみてもSessionは引き継がれているみたいです。うーん、謎。

## adapterを設定しない場合、どこにSession情報が保存されている？
NextAuthOptionsのjwtプロパティに答えが書いてありました。
```typescript
/**
     * JSON Web Tokens are enabled by default if you have not specified an adapter.
     * JSON Web Tokens are encrypted (JWE) by default. We recommend you keep this behaviour.
     * * **Default value**: See the documentation page
     * * **Required**: *No*
     *
     * [Documentation](https://next-auth.js.org/configuration/options#jwt)
     */
```
ということで、adapterを設定しない場合JWTでセッション情報が保存されるみたいです。
CredentialsProviderとPrismaAdapterの組み合わせでセッションが有効にならない理由は謎ですが、JWTでセッションが有効になってるなら無理にDBにセッション情報を保存する必要はないのでそのままにしておきます。

ここまでの変更は以下の通りです。

https://github.com/bioflowy/t3-todo-example/commit/754fe60804ed2e97c94a1e5863d21c714a604dab

# 追記

これでOだと思っていたのですが、session.user.idにidが設定されていませんでした。

https://qiita.com/bioflowy/items/27b04a1027a2160971d2

をご覧ください。