🏘️

next-authを複数のプロジェクトで利用してるときにローカル環境のセッションが奪い合いになる問題に向き合う

2024/06/20に公開

next-authを複数のプロジェクトで利用を利用する場合、ローカルで同時に実行すると同一のCookieを参照するため、片方でログインすると片方がログアウトされるという状態が起きてしまう。

別ブラウザを利用するなどの対処法もあるが、いちいち面倒なので、対処を考える

Cookie名を変更する

この問題は同名のcookieを上書きしあってしまうことなので、cookie名を変更すれば良い。

next-authのcookie自体は、optionで変更が可能になっているのでこれを利用していく

// pages/api/auth/[...nextauth].js

const cookieOptions = ... // この部分については後述
export const authOptions = {
  // Configure one or more authentication providers
  providers: [
    GithubProvider({
      clientId: process.env.GITHUB_ID,
      clientSecret: process.env.GITHUB_SECRET,
    }),
    // ...add more providers here
  ],
  cookies: cookieOptions, 
}
export default NextAuth(authOptions)

middlewareを利用している場合は、そちらの設定も変更する必要がある。

// middleware.ts
const auth = withAuth({
  cookies: cookieOptions
})

Cookie設定を生成する

ここからCookieの設定を生成することを考える。
いくつか方法があるので、行儀の良い順に記載する

行儀の良いやりかた : Exampleをコピーしてくる

ドキュメントにあるExampleからコピーして書き換える。

丁寧だが若干面倒。複数プロジェクトあると面倒なのと、アップデート時などに変更を来にする必要はある

程々に行儀の悪いやりかた : nameのみ定義する

nameのみ変更した状態で管理することでも目的は達成できる。

const prefix = `my-app`
// 本来は cookies: Partial<CookiesOptions> 
const cookies: any = {
  sessionToken: { name: `${prefix}-auth.session-token` },
  callbackUrl: { name: `${prefix}-next-auth.callback-url`} ,
  csrfToken: { name: `${prefix}-next-auth.csrf-token` },
  pkceCodeVerifier: { name: `${prefix}-next-auth.pkce.code_verifier` },
  state: { name: `${prefix}-next-auth.state` },
  nonce: { name: `${prefix}-next-auth.nonce` }
}

この方法は内部実装で設定をdeep mergeしていることをアテにしているので、割と行儀が悪い

しっかり行儀の悪いやりかた : 内部実装のdefaultCookiesを引っ張ってくる

内部でデフォルトのcookieを生成しているdefaultCookiesを呼び出して書き換える方法もある。
この関数、VS Codeの補完などでは下記のように出てくるので一見使えそうなのだが、実際にはimportできていない。

import { defaultCookies } from "next-auth/core/lib/cookie"

// Module not found: Package path ./core/lib/cookie is not exported from package 

ただ、呼び出す方法が無いわけでもなく、このissueコメントを参考に、node_modulesを経由して呼び出すことはできる。

// lib/next-auth/cookie.ts
import { defaultCookies } from '../../../node_modules/next-auth/core/lib/cookie'

const prefix = "my-app"
export const getCookie = () => {
  if (process.env.NODE_ENV !== 'development') {
    return {}
  }
  return Object.fromEntries(
    Object.entries(defaultCookies(true)).map(([key, value]) => {
      return [key, {
        ...value,
        name: `${prefix}_${value.name}`
      }]
    })
  )
}

これを利用すると、行儀が悪いものの目的のcookieを生成することができる。

GitHubで編集を提案

Discussion