1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

検証先が違う——それだけでgetClaims()とgetUser()の使い分けがスッキリした

1
Posted at

はじめに

Supabaseの認証には、getClaims()とgetUser()というメソッドがあり、それぞれ現在のユーザーを検証するという意味合いで使われます。
これを席替えアプリで使う前は違いがよくわかっておらず、どういった場面で使うのか具体的な案もありませんでした。
今回、公式のドキュメントを見ながら、自分の中で考えがまとまったので記事にしたいと思います。

getClaims() VS getUser()

以下の写真を見てもらえれば分かるように、私がgetClaimsについて調べようとキーワードを打ち込むとGoogleの予測変換の上位にこの対立が表示されました。個人的には、VSというほど対立はしていない印象を持ちました。
しかし、取得できるdataは似ていますので、それぞれの特徴をまとめていきます。

スクリーンショット 2026-05-02 4.59.49.png

       getClaims()       getUser()
検証先 JSON Web Key Setエンドポイント Supabase Authサーバー
応答速度 速い 遅い
信頼度 やや低い 高い
使いどころ Proxy(middleware) サーバーコンポーネント

上記の表は公式のドキュメントを参照しながら、まとめています。
それぞれの項目について見ていきます。

表の解説

検証先を理解すると、自分の中でスッキリしました。
以下は、公式からの引用です。

getClaims()について

アクセストークンに含まれるJWTクレームを抽出するために、まずサーバーのJSON Web Key Setエンドポイント(/.well-known/jwks.json多くの場合キャッシュされている)に対してJWTを検証します。これにより、応答速度が大幅に向上します。JWTごとに認証サーバーにリクエストを送信する#getUserメソッドよりも、このメソッドの使用をお勧めします。

getClaims()に関しては、サーバーのJSON Web Key Setエンドポイントに対してJWT(JSON WEB TOKEN)を検証し、これは多くの場合キャッシュされているため、本当に正規かというのは厳密さに欠けるところがあります。(キャッシュされたものの検証なので、キー更新のタイミングでズレが生じる可能性あり)しかし、応答速度は速いので、Proxyのような門番で記述して、毎回呼び出しても軽量というメリットはあります。

getUser()について

このメソッドは、Supabase Authサーバーへのネットワークリクエストを実行するため、返される値は正規のものであり、認証ルールの基準として使用できます。

getUser()に関しては、AuthサーバーへのネットワークリクエストというgetClaims()よりは重い処理を行います。しかし、返される値は正規のものであるため、信頼がおけます。処理が重いが、信頼がおけるので、アプリの鍵穴のようなイメージです。サーバーコンポーネントでアプリの大枠で使用します。

使用例

最後に席替えアプリで実際に書いたコードを載せておきます。

getClaims()

proxy.auth.ts
import { createServerClient } from '@supabase/ssr';
import { NextRequest, NextResponse } from 'next/server';

export async function updateSession(request: NextRequest) {
  let supabaseResponse = NextResponse.next({ request });

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
    {
      cookies: {
        getAll() {
          return request.cookies.getAll(); // リクエストからcookieを読んで、Supabaseに渡す
        },
        setAll(cookiesToSet) {
          // リクエストオブジェクトにも反映
          cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value));
          // レスポンスを再生成(新しいCookieを含めるため)
          supabaseResponse = NextResponse.next({ request });
          // レスポンスのCookieに書き込む(ブラウザに返される)
          cookiesToSet.forEach(({ name, value, options }) =>
            supabaseResponse.cookies.set(name, value, options),
          );
        },
      },
    },
  );
  const { data } = await supabase.auth.getClaims();

  const claims = data?.claims ?? null;
  const isFullUser = claims?.is_anonymous === false;
  const isAnonymous = claims?.is_anonymous === true;
  const isGuest = claims === null;
  const { pathname } = request.nextUrl;

  // ユーザーが存在しないかつ/classroom配下もしくは/user/updateへのアクセスがあったとき
  if (isGuest && (pathname.startsWith('/classroom') || pathname.startsWith('/user/update'))) {
    return NextResponse.redirect(new URL('/user/signin', request.url));
  }
  // 仮ログインしているかつ/または/user/signin配下へのアクセスがあったとき
  if (isAnonymous && (pathname === '/' || pathname.startsWith('/user/signin'))) {
    return NextResponse.redirect(new URL('/classroom', request.url));
  }
  // ログインしているかつ/または/user配下へのアクセスがあったとき
  if (isFullUser && (pathname === '/' || pathname.startsWith('/user'))) {
    return NextResponse.redirect(new URL('/classroom', request.url));
  }
  return supabaseResponse;
}

サーバーとクラアントの間に立つProxyの中で使用しています。
claimsがnullではなければ、ユーザーの情報があり、ログインがされている状態です。アプリでは、認証済みとそうでないユーザーを分けることに役立っています。

getUser()

page.ts
import { cookies } from 'next/headers';
import MainContents from './components/layouts/MainContents';
import { createServerClient } from '@supabase/ssr';
import { redirect } from 'next/navigation';

export default async function Classroom() {
  const cookieStore = await cookies();
  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll();
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) => {
            cookieStore.set(name, value, options);
          });
        },
      },
    },
  );

  const {
    data: { user },
  } = await supabase.auth.getUser();
  if (!user) {
    redirect('/user/signin');
  }

  return <MainContents />;
}

こちらは、ログイン専用ページの初めに書いています。
どちらもサーバー上で実行して、getAll()でcookieを取得し、setAll()でcookieを書き込んでいます。

まとめ

getClaims()とgetUser()の違いについてまとめていきました。
VSという表現は使われがちのようですが、使う場面が違うだけで、どちらかを選ぶものではない気がしています。

結果として、以下のようなアプリの変化がありました。
以前、React SPAと変わらなかったという記事を投稿していましたが、レンダリング方式に変化があり、Next.jsの恩恵が感じられました。

スクリーンショット 2026-05-01 5.58.43.png

おまけ(他の認証機能について)

onAuthStateChange()というクライアントサイドで使用する認証の変化を監視するメソッドがあります。
アプリのタブを複数開いていて、片方のタブがログインしたら、それを検知して、もう一方のタブにもログイン状態が即座に反映されます。

これについては、アプリの全体を囲うProviderを設置し、"use Client"を先頭に書いてコードを記述しています。UIの状態管理ですね。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?