Closed2

【Next.js】Auth.js v5ではどうやってRSCでセッションを取得できているかを追ってみる

1zushun1zushun

モチベーション

v4からv5のマイグレーションをしている中でRSCでセッションを取得するためにはgetServerSessionではなくNextAuthから返されるauthを使ってセッションを取得する方法に変わったようです。

さらにCredential Providerを使っているのですが、v5からJWT session strategyしかサポートしなくなったようなので、これを機にAuth.jsではどのようにRSCでJWTを使ってセッション管理しているか実際にコードを追っていこうと思います。

現状JWTはcookieで管理されていて、RSCでセッションを取得する際はJWTをAuth.js側でデコードして返しているのはずなのでJWTのエンコード、デコードしているコードまでたどりつければこちらのスクラップをクローズさせようと思います。

ちなみにuseSessionを使ってクライアント側でセッションを取得する際はContext APIで用いているようです。(以下参照)

https://github.com/nextauthjs/next-auth/blob/6377582636401060842f6ff918741440f049a6ff/packages/next-auth/src/react.tsx#L130-L169

Auth.js v5ではどうやってRSCでセッションを取得できているかを追ってみる

サンプル

サンプルコードはこちらから引用しています

import { auth } from "../auth"
 
export default async function UserAvatar() {
  const session = await auth()
 
  if (!session.user) return null
 
  return (
    <div>
      <img src={session.user.image} alt="User Avatar" />
    </div>
  )
}
auth.ts
import NextAuth from "next-auth"
import { ZodError } from "zod"
import Credentials from "next-auth/providers/credentials"
import { signInSchema } from "./lib/zod"
// Your own logic for dealing with plaintext password strings; be careful!
import { saltAndHashPassword } from "@/utils/password"
import { getUserFromDb } from "@/utils/db"
 
export const { handlers, auth } = NextAuth({
  providers: [
    Credentials({
      // You can specify which fields should be submitted, by adding keys to the `credentials` object.
      // e.g. domain, username, password, 2FA token, etc.
      credentials: {
        email: {},
        password: {},
      },
      authorize: async (credentials) => {
        try {
          let user = null
 
          const { email, password } = await signInSchema.parseAsync(credentials)
 
          // logic to salt and hash password
          const pwHash = saltAndHashPassword(password)
 
          // logic to verify if the user exists
          user = await getUserFromDb(email, pwHash)
 
          if (!user) {
            throw new Error("User not found.")
          }
 
          // return JSON object with the user data
          return user
        } catch (error) {
          if (error instanceof ZodError) {
            // Return `null` to indicate that the credentials are invalid
            return null
          }
        }
      },
    }),
  ],
})

1, NextAuth(packages/next-auth)

import NextAuth from "next-auth"から見ていきます。
next-authをインポートしている大元のファイルはこちら。
L385のauthでsessionを受け取れます。
authはinitAuthで生成しているようなので見ていきましょう。
setEnvDefaultsは後回しにして余裕があれば追って見ます。

https://github.com/nextauthjs/next-auth/blob/6377582636401060842f6ff918741440f049a6ff/packages/next-auth/src/index.ts#L370-L420

2, initAuth(packages/next-auth)

initAuthを扱っているファイルはこちら。
L129のコメントアウトの通りRSCのコードをたどりたいのでL134のgetSessionを見ていきます。

https://github.com/nextauthjs/next-auth/blob/6377582636401060842f6ff918741440f049a6ff/packages/next-auth/src/lib/index.ts#L120-L228

3, getSession(packages/next-auth)

getSessionはinitAuthと同じファイルで定義されていて、最終的な成果物はAuthで生成しているのでそちらを見にいきます。

https://github.com/nextauthjs/next-auth/blob/6377582636401060842f6ff918741440f049a6ff/packages/next-auth/src/lib/index.ts#L62-L97

4, Auth(packages/core)

ここからはパッケージが変わります。
configにNEXTAUTH_SECRETが内包されているはずなのでAuthInternalを追ってみます。

https://github.com/nextauthjs/next-auth/blob/6377582636401060842f6ff918741440f049a6ff/packages/core/src/index.ts#L100-L198

5, AuthInternal(packages/core)

ここら辺でSessionStoreが見えてきたのでゴールは近そうです。
Switch文の中でsessionStoreを流しているのはactions.callbackとactions.session(actions.webAuthnOptionsはスキップ)なのでactions.sessionの方を覗いて見ます。

https://github.com/nextauthjs/next-auth/blob/6377582636401060842f6ff918741440f049a6ff/packages/core/src/lib/index.ts#L12-L94

6, actions.session(packages/core)

ようやくここでjwt.encodeを見つけました。
L38でJWTをデコードしてpayloadを取得してようです。
ちなみにsessionStrategy(jwtとdatabase)のコードがこちらにまとまっていました。

https://github.com/nextauthjs/next-auth/blob/6377582636401060842f6ff918741440f049a6ff/packages/core/src/lib/actions/session.ts#L9-L160

まとめ

ざっと見てきましたが、やはりRSCでセッションを取得する際はcookie情報(JWT)をAuth.js側でデコードしているようです。また、全部は見切れていませんがコードを追っていくならpackages/coreを見ていくと良さそうです。

簡単ですが以上です。
補足やメモ書きなどがありましたら追記していきます。

このスクラップは3ヶ月前にクローズされました