🔑

NextAuth.js で SessionProvider を利用しユーザ情報を取得する

2024/04/12に公開

背景

NextAuth.js でログインしている User の情報を Client side で取得する方法を色々調べながら学んでいました。
参考にしたサイトと私が使っている Next.js のバージョンの違いなどもあり迷走したので、備忘録としてまとめておきます。皆様の助けになれば幸いです。
(Next.js はバージョン違いで結構仕様が異なるのでこの辺は沼ですね)

https://next-auth.js.org/

ソフトウェアのバージョン

npm ls | grep next
├── next-auth@4.23.2
├── next@13.5.6
next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  compiler: {
    styledComponents: true,
  },
  experimental: {
    appDir: true,
  },
}

module.exports = nextConfig

方法

NextAuth.js を用いると簡単に Github や Gmail を利用した認証機能を実装することができます。
さらに認証済みの状態の時に現在ログインしているユーザの名前やその他の情報を取得するのも、(私は少しつまづいてしまいましたが)簡便にできます。

おそらく本来なら簡単にできるところを

  • Provider の実装を行う必要があるが初めての実装であった
  • 動的ルーティングを行っているページでユーザ情報を取得する
  • client side として取得する

と言う状況でやや複雑であったのが原因ではないかなとは思います。
そこで同じ状況で実装する方のために備忘録としてまとめておきます。

1. Provider として SessionProvider を実装する

Client side で NextAuth.js のユーザなどのセッション情報を取得するためには SessionProvider を利用する必要があります。
そこで src/app/context/providers.tsx を以下のように実装しました。
use client を先頭につけることで client side で利用することを明示しておきます。

src/app/context/providers.tsx
'use client'

import { SessionProvider } from 'next-auth/react'
import { ReactNode } from 'react'

type ProviderProps = {
  children: ReactNode
}

function Provider({ children }: ProviderProps) {
  return <SessionProvider>{children}</SessionProvider>
}

export default Provider

2. 動的ルーティングを行なっていないページでユーザ情報を取得する

コケた時に変更点がたくさんあるとどこでつまづいたかわかりづらくなるので、まずは動的ルーティングを行っていないページで取得してみます。原因の切り分けは大事です。

例えば src/app/foo/page.tsx のような適当なディレクトリに page.tsx を作成し以下のように実装します。

src/app/foo/page.tsx
'use client'

import React from 'react'
import Provider from '../../context/providers'
import MyClientComponent from '../../components/MyClientComponent'

export default function page() {
  return (
    <Provider>
      <MyClientComponent />
    </Provider>
  )
}

ここで先ほど作成した Provider タグを呼び出し MyClientComponent タグを挟んでいるので MyClientComponent コンポーネント内でユーザ情報を取得することができるようになります。

続けて MyClientComponent コンポーネントを実装します。

src/app/components/MyClientComponent.tsx
'use client'

import React from 'react'
import { useSession } from 'next-auth/react'

export default function MyClientComponent() {
  const { data: session, status } = useSession()

  console.log('session', session)
  console.log('status', status)

  if (status === 'loading') {
    return <p>Loading...</p>
  }

  return (
    <div>
      <h1>My Client Component</h1>
      <h2>User ID: {session?.user?.id}</h2>
      <h2>status: {status}</h2>
    </div>
  )
}

useSession でユーザ情報を取得します。 useSession は client side のみでしか使えないため providers.tsx, page.tsx, MyClientComponent.tsxuse client と client side であることを明示している必要があるわけですね (server side の場合は getServerSession を使いましょう)

ただし、 MyClientComponent がレンダリングされた瞬間、ユーザなどのセッション情報が取得されるわけではないようなので

  if (status === 'loading') {
    return <p>Loading...</p>
  }

と、 statusloading の場合はロード中であることを表示し、取得後 (statusauthenticated になります) にユーザ情報に基づいたページを表示するよう今回はしてみました。

  return (
    <div>
      <h1>My Client Component</h1>
      <h2>User ID: {session?.user?.id}</h2>
      <h2>status: {status}</h2>
    </div>
  )

なお最初に

  const { data: session, status } = useSession()

  console.log('session', session)
  console.log('status', status)

と、 console.log でデバッグライトしているのですが、ここはユーザなどのセッション情報取得前に実行されるので session = undefined, status = loading と表示され、ここでデバッグライトしていると当たり前ですが値が取得できません。(ここでデバッグライトしてたせいでずっと値が入らなくて他の部分がおかしいのかなーと少しハマってしまいました)

ここまで実装しテストをすると以下のようにユーザなどのセッション情報が取れているのが確認できました。

3. 動的ルーティングを行なってるページでユーザ情報を取得する

では最後に元々の目標であった動的ルーティングを行っているページでユーザ情報を取得してみます。

先ほどと同様に src/app/baz/[title]page.tsx を作成し title の値を取得したいとします。

動的ルーティングを用いると以下のように title が取得できます。

src/app/baz/[title]/page.tsx
'use client'

export default function Page({ params }: { params: { title: string } }) {
  const { title } = params
  return (
    <main>
      <h1>My Page</h1>
      <p>title: {title}</p>
    </main>
  )
}

同じようにユーザなどのセッション情報を含めつつ動的ルーティングをするページを src/app/foo/[title]/page.tsx を実装していきます。本来は別のファイルなのですが 「2. 動的ルーティングを行なっていないページでユーザ情報を取得する」 で実装したページとの差分として表示しておきます。

src/app/foo/[title]/page.tsx
'use client'

import React from 'react'
import Provider from '../../../context/providers'
import MyClientComponent from '../../../components/MyClientComponent'

- export default function page() {
+ export default function page({ params }: { params: { title: string } }) {
+   const { title } = params
  return (
    <Provider>
-       <MyClientComponent />
+       <MyClientComponent title={title} />
    </Provider>
  )
}

MyClientComponent 側も title を受け取り表示するようにアップデートします。

src/app/components/MyClientComponent.tsx
'use client'

import React from 'react'
import { useSession } from 'next-auth/react'

- export default function MyClientComponent() {
+ export default function MyClientComponent({ title }: { title: string }) {
  const { data: session, status } = useSession()

  console.log('session', session)
  console.log('status', status)

  if (status === 'loading') {
    return <p>Loading...</p>
  }

  return (
    <div>
      <h1>My Client Component</h1>
+       <h2>Title: {title} </h2>
      <h2>User ID: {session?.user?.id}</h2>
      <h2>status: {status}</h2>
    </div>
  )
}

ここまで実装し、 例えば http://localhost:3000/foo/abc にアクセスすると以下のように title として abc が取得され表示されるようになります。また、ユーザなどのセッション情報も引き続き取得されています。

💡 まとめ

  • NextAuth.js でログイン中のユーザなどのセッション情報を取得しました
  • Session Provider を用いて Client side で利用できるようにしました
  • 動的ルーティングも同時に行いました
Cykinso's Tech Blog

Discussion