🚦

next-authとgetServerSide(Static)Props使うときの__NEXT_DATA__に注意

2021/01/27に公開

next-authなどでクライアントログインを仕掛けていてgetServerSidePropsやgetStaticPropsでちゃんと認証かけてないとだだ漏れするので気をつけましょうねという話。

通常のサーバーサイド開発に慣れていればあまりハマるようなことではないのだが、知らないで使うと危ないので記述しておく

例示

例えば下記のようなコードで考えてみる。
だいたいログイン判定はクライアントなら_app.jsにて書くだろう。

// _app.jsx
import { signIn, useSession } from "next-auth/client"

export const NeedLogin = ({children}) => {
  const [session, loading] = useSession()
  if (loading) {
    return null
  }
  if (!session ) {
    return <div>
      Not signed in <br />
      <button onClick={signIn}>Sign in</button>
    </div>
  }
  return children
}

function MyApp({ Component, pageProps }) {
  return (
    <Provider options={{ clientMaxAge: 60 }} session={pageProps.session}>
      <NeedLogin>
        <Component {...pageProps} />
      </NeedLogin>
    </Provider>
  )
}

export default MyApp

そしてページ側はこのような具合に書いてみる

// pages/somePage.jsx

import React from "react"
import { useSession } from "next-auth/client"
import { getSomeSecretData } from "../lib/getSomeData"


export default function Page({data}) {
  const [, loading] = useSession()
  if (loading) {
    return null
  }
  return (
    <div>
      Home
      <pre>Need Login: {JSON.stringify(data)}</pre>
    </div>
  )
}

export async function getServerSideProps() {
  const data = getSomeSecretData() // 例えばこれがログイン後にのみ表示させたいデータだとする
  return {
    props: { data },
  }
}

そうするとログイン後、ログイン前でこんな感じで表示されるはずだ

ログイン前 ログイン後
ログイン前 ログイン後

これにてめでたしめでたし。ではない。

ログイン前のページでinspectorをしてみよう

<script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"data":["This","Data","Need","Login"]},"__N_SSP":true},"page":"/","query":{},"buildId":"development","isFallback":false,"gssp":true}</script>

このように__NEXT_DATA__としてgetServerSidePropsの値は表示されてしまう

対応方法

getServerSidePropsで行う場合はgetSessionでチェックを入れましょう。

import { useSession,getSession } from "next-auth/client"

export async function getServerSideProps(context) {
  const session = await getSession(context) // awaitを忘れずに
  if (!session) {
    return { props: {} }
  }
  
  const data = getSomeSecretData()
  return {
    props: { data },
  }
}

こうすれば__NEXT_DATA__にも表示されません。 [1]
[1]: awaitを忘れるとsessionがPromiseになってしまいif(!session)を通過してしまうので注意

OKなパターン

<script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{},"__N_SSP":true},"page":"/","query":{},"buildId":"development","isFallback":false,"gssp":true}</script>

また、getStatciPropsの場合はリクエストなどのcontextが取得できないためこのままではできません。もともとgetStatciPropsはログインが必要などのユースケースは含まれてないため、どうしてもgetStaticPropsを利用する必要がある場合はAPI経由などで取得をするのが良いでしょう。

APIの場合であれば上記同様getSessionが利用できます

Discussion