mcpField
S
supabase-enterprise-rbac

supabase-enterprise-rbac

github.com/jeremylongshoreセキュリティ2.9k40 回インストール2 日前に更新
リポジトリ

supabase-enterprise-rbac について

ロールベースおよびマルチテナントの Supabase アプリを構築するためのスキルです。JWT のロール情報を使い、RLS ポリシーと API の両方でアクセスを制御します。

主な機能

  1. 01app_metadata によるロール設定
  2. 02ロール階層に基づく RLS ポリシー
  3. 03組織単位のアクセス範囲設定
  4. 04API ミドルウェアでのロール確認

活用シーン

  1. 01管理者、編集者、閲覧者の権限実装
  2. 02組織ごとのプロジェクトデータ分離
  3. 03API ルートと管理操作の保護

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jeremylongshore/tons-of-skills-marketplace --skill supabase-enterprise-rbac

インストール後、Claude Code で/supabase-enterprise-rbacを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
api-scoping-and-enforcement.md
errors.md
examples.md
rls-policies.md
role-assignment.md
role-implementation.md
sso-integration.md

詳細

Supabase エンタープライズ RBAC

概要

Supabase では、ユーザーの JWT の app_metadata にロール情報を保存し、RLS ポリシーで auth.jwt() ->> 'role' を介してこれらのクレームを読み取ることで、カスタムのロールベースアクセス制御(RBAC)をサポートしています。このスキルでは、app_metadata でのロール管理、ロール階層を適用する RLS ポリシー、組織スコープのアクセス、Admin API によるロール管理、ロールチェックで保護された API エンドポイントを備えた完全な RBAC システムを、実際の @supabase/supabase-js の createClient を使って実装します。

使用する場面: 複数のロール(admin/editor/viewer)を持つアプリケーションの構築、組織スコープのアクセスの実装、Supabase 組み込みの anon/authenticated ロールを超えるカスタム権限システムの作成、またはユーザーロールに基づく API 操作のスコープ設定。

前提条件

  • 管理操作用のサービスロールキーを備えた @supabase/supabase-js v2+
  • JWT クレームと Supabase の auth.jwt() SQL 関数に関する理解
  • RLS ポリシーを作成するための SQL Editor または psql によるデータベースアクセス
  • 認証が設定された Supabase プロジェクト

手順

ワークフローは 3 つのステップで構成されます。JWT にロールを割り当て、データベースで RLS を使って適用し、さらにアプリケーションコードでも適用します。

ステップ 1:app_metadata と JWT クレームでロールを定義する

Admin API を使って、ユーザーの app_metadata にカスタムロールを保存します。これらのクレームはユーザーが受け取るすべての JWT に含まれ、RLS ポリシーから読み取れます。サービスロールクライアントを使ってロールを割り当てます。

// Define the role hierarchy
type AppRole = 'admin' | 'editor' | 'viewer' | 'member';

// Assign a role to a user (admin operation, service role key required)
async function setUserRole(userId: string, role: AppRole, orgId: string) {
  const { data, error } = await supabase.auth.admin.updateUserById(userId, {
    app_metadata: { role, org_id: orgId },
  });
  if (error) throw new Error(`Failed to set role: ${error.message}`);
  return data.user;
}

アプリコードでは supabase.auth.getUser() でロールを取得し、数値の階層と照合します。これにより、hasRole('editor', 'viewer') は true になります。サービスロールクライアントの完全なセットアップ、きめ細かな権限、バルクの assignTeamRoles()、getCurrentUserRole()、getCurrentOrg()、hasRole()、requireRole() ガードについては、ロールの割り当てと JWT の抽出を参照してください。

ステップ 2:JWT ロールクレームを使った RLS ポリシー

auth.jwt() -> 'app_metadata' ->> 'role' と ... ->> 'org_id' を読み取る Row Level Security ポリシーを記述します。JWT の抽出をヘルパー関数でラップして、すべてのポリシーを読みやすくします。

CREATE OR REPLACE FUNCTION public.get_user_role()
RETURNS text AS $$
  SELECT coalesce(auth.jwt() -> 'app_metadata' ->> 'role', 'viewer');
$$ LANGUAGE sql STABLE SECURITY DEFINER;

CREATE POLICY "editors_create_projects" ON public.projects
  FOR INSERT WITH CHECK (
    org_id = get_user_org_id()
    AND get_user_role() IN ('admin', 'editor')
  );

ポリシーでは、操作(SELECT/INSERT/UPDATE/DELETE)ごとに、org_id = get_user_org_id() によるテナントチェックと get_user_role() によるロールチェックを組み合わせます。2 つのヘルパー関数、projects/documents/team_members にわたるポリシー一式、インデックス付きの organizations/team_members/projects テーブルスキーマについては、RLS ポリシーと組織スコープのスキーマを参照してください。

ステップ 3:API キーのスコープ設定とアプリケーションコードでのロール適用

RLS だけに依存して API ルートを保護することがないよう、アプリケーション層でもロールを再度適用します。サーバーサイドの withRole() ミドルウェア、リクエストごとのクライアント作成、管理パネルの操作(メンバー一覧表示、ユーザー招待、ロール変更)、組織管理のパターンについては、API キーのスコープ設定とロールの適用を参照してください。

出力

このスキルを完了すると、次のものが生成されます。

  • app_metadata によるロール割り当て — admin.updateUserById() でユーザーの JWT にロールクレームを設定
  • JWT クレームの抽出 — get_user_role() と get_user_org_id() SQL ヘルパー関数
  • ロールベースの RLS ポリシー — ロール階層(admin > editor > member > viewer)に基づいてスコープを設定した SELECT/INSERT/UPDATE/DELETE
  • 組織スコープのアクセス — JWT クレーム内の org_id と RLS ポリシーによるマルチテナント分離
  • アプリケーション層での適用 — 適切な 401/403 レスポンスを返す、API ルート用の withRole() ミドルウェア
  • 管理パネルの操作 — データベースと JWT の両方を更新して、メンバー一覧表示、ユーザー招待、ロール変更を実行
  • ロール階層のチェック — ロールの昇格比較に対応する hasRole() 関数

エラー処理

エラー原因解決策
JWT の app_metadata.role が nullロールが設定されていない、またはユーザーが再ログインする必要がありますadmin.updateUserById() を呼び出してロールを設定します。ユーザーはセッションを更新する必要があります
RLS ポリシーが空の結果を返すJWT クレームがポリシーの条件に一致しませんSQL Editor で auth.jwt() の出力を確認し、app_metadata が正しく設定されていることを検証します
permission denied for functionヘルパー関数が作成されていないか、スキーマが誤っていますSECURITY DEFINER を指定して、public スキーマに get_user_role() を作成します
ユーザーのロール変更が反映されないJWT に古いクレームがキャッシュされていますユーザーはサインアウトしてから再度サインインするか、supabase.auth.refreshSession() を呼び出す必要があります
duplicate key value violates unique constraintユーザーはすでに組織に所属しています挿入する前に team_members テーブルに既存のエントリがないか確認します
team_members の foreign key violationユーザーまたは組織が存在しませんメンバーシップを挿入する前に、user_id と org_id の両方が存在することを確認します
ロール階層の回避サービスロールを使用したデータベースへの直接アクセスサービスロールは仕様上 RLS をバイパスします。サーバー側の管理操作にのみ使用を制限してください

例

例 1 — コンポーネントでの簡単なロール確認:

async function canEditProject(): Promise<boolean> {
  const { data: { user } } = await supabase.auth.getUser();
  const role = user?.app_metadata?.role;
  return role === 'admin' || role === 'editor';
}

例 2 — RLS ポリシーが正しく機能することを検証する:

-- Test as an editor in org-123
SET request.jwt.claims = '{"sub": "user-uuid", "role": "authenticated", "app_metadata": {"role": "editor", "org_id": "org-123"}}';

SELECT * FROM projects;                                                    -- returns only org-123 rows
INSERT INTO projects (org_id, name, created_by) VALUES ('org-123', 'Test', 'user-uuid');  -- succeeds
DELETE FROM projects WHERE id = 'some-project-id';                          -- fails (editors cannot delete)

RESET request.jwt.claims;

オンボーディングの全例(onboardOrganization() — 組織の作成、作成者の管理者への割り当て、team_members の初期データ投入)については、ロール割り当てのリファレンスとAPI スコープ設定のリファレンスを参照してください。

リソース

次のステップ

  • データベースのマイグレーションパターンについては、supabase-migration-deep-dive を参照してください
  • セキュリティ強化と API キーのスコープ設定については、supabase-security-basics を参照してください
  • データの取り扱いと GDPR 準拠については、supabase-data-handling を参照してください

コメント