B2Bエンジニアにソーシャルログインなんて必要ありません(そもそもインターネットに出れないので使えません)。
NextAuth.jsでソーシャルログインをやめEmail/Passwordでログインできるように変更します。
ハッシュ化するパッケージを導入する
npm install bcrypt @types/bcrypt
パスワードカラムを追加&初期データを設定する
prisma/schema.prismaのUserモデルにパスワード用のカラムを追加します。
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を作成します。
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を実行します。そのままだと実行できずオプションが必要でした。
~/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を利用するように変更します
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でログインできるようになりました。
ただ、なぜか adapter: PrismaAdapter(prisma)を削除しないと、ログインは成功するのですがSessionが取得できなくなってしまいました。
また、adapterを設定しない場合、いったいどこにSession情報が保存されているんでしょうか。メモリに保存されているのかなと思ったけどdevサーバを再起動してみてもSessionは引き継がれているみたいです。うーん、謎。
adapterを設定しない場合、どこにSession情報が保存されている?
NextAuthOptionsのjwtプロパティに答えが書いてありました。
/**
* 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にセッション情報を保存する必要はないのでそのままにしておきます。
ここまでの変更は以下の通りです。
追記
これでOだと思っていたのですが、session.user.idにidが設定されていませんでした。
をご覧ください。