【Lucia】Prisma + Luciaで認証構築してみる
こんにちは、フリーランスエンジニアの太田雅昭です。
Lucia
Luciaは、認証フローをまとめてくれるライブラリです。セッション管理などが楽になるようです。
比較
- NextAuth: 簡単。ブラックボックスでわかりにくい
- Lucia: 自分で実装するため明瞭。複雑
個人的には楽な方がいいのですが、ある程度規模のあるプロジェクトでは複雑な仕様にも対応できる、Luciaの方がいいかなと。
バージョン3と4
Luciaは2025年3月にバージョン4になると公言されていますが、2025/04/16現在まだバージョン3です。Primsa用のadapterが提供されていますが、Prisma6に対応していません。今後もadapterは更新されないそうですので、adapterも自作する方向になるようです。ブラックボックスが減るので、良い方向かと思います。
実装してみる
attrubutesが独特です。一旦attributesは無しで実装します。
export const lucia = new Lucia(adapter, {
sessionCookie: {
expires: false,
attributes: {
secure: process.env.NODE_ENV === "production"
}
},
sessionExpiresIn: new TimeSpan(SESSION_EXPIRATION_DAYS, 'd'),
});
declare module "lucia" {
interface Register {
Lucia: typeof lucia;
DatabaseUserAttributes: {}; // attributesは一旦空にする
DatabaseSessionAttributes: {};; // attributesは一旦空にする
}
}
adapterは下記のように実装します。上記で設定したDatabaseUserAttributes, DatabaseSessionAttributesに縛られます。一見循環参照のようですが、型定義の場合問題ないようです。
export const adapter: Adapter = {
deleteExpiredSessions: async () => {
await prisma.session.deleteMany({ where: { expiresAt: { lt: new Date() } } })
},
deleteSession: async (sessionId) => {
await prisma.session.delete({ where: { id: sessionId } })
},
deleteUserSessions: async (userId) => {
await prisma.session.deleteMany({ where: { userId } })
},
setSession: async (session) => {
await prisma.session.create({ data: { id: session.id, expiresAt: session.expiresAt, userId: session.userId, } })
},
updateSessionExpiration: async (sessionId, expiresAt) => {
await prisma.session.update({ where: { id: sessionId }, data: { expiresAt } })
},
getUserSessions: async (userId) => {
const sessions = await prisma.session.findMany({ where: { userId } })
return sessions.map((session) => ({ ...session, attributes: {} }))
},
getSessionAndUser: async (sessionId) => {
const session = await prisma.session.findUnique({ where: { id: sessionId }, include: { user: true } })
if (!session) return [null, null]
const { user } = session;
const databaseSession: DatabaseSession = {
id: session.id,
userId: user.id,
expiresAt: session.expiresAt,
attributes: {},
}
const databaseUser: DatabaseUser = {
id: user.id,
attributes: {},
}
return [databaseSession, databaseUser]
},
}
今後必要に応じて、attributesは追加していけば良いかと思います。