【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は追加していけば良いかと思います。