Supabase RLSポリシー作成パターン
RLS(Row Level Security)はSupabaseセキュリティの核心です。Claude CodeにRLSポリシーを任せる際は、テーブル構造、認証方式、アクセスルールを一緒に明示することで、意図した通りに作成してもらえます。
RLSをClaudeに任せるときのよくある間違い
auth.uid()と一般カラムの混同 Claudeがuser_id = auth.uid()の代わりにuser_id = current_userのような誤った関数を使う場合があります。必ずSupabase authの関数を明示してください。
ポリシー名なしで作成 名前のないポリシーは、後で修正・削除するときに見つけにくくなります。
SELECT/INSERT/UPDATE/DELETEをひとつにまとめる Supabase RLSは操作ごとにポリシーを分けて管理するのが良いプラクティスです。
📋 プロンプトテンプレート
以下のテーブルに対するSupabase RLSポリシーを作成してください。
[テーブル構造]
- テーブル名: typing_sessions
- カラム: id (uuid), user_id (uuid, FK → auth.users), content (text), created_at (timestamptz)
[認証方式]
- Supabase Auth使用
- 認証済みユーザー: auth.uid() 関数で現在のユーザーIDを取得
- 未認証ユーザー(anon): アクセス不可
[アクセスルール]
- SELECT: 自分のレコードのみ照会可能 (user_id = auth.uid())
- INSERT: 認証済みユーザーのみ可能、user_idは必ずauth.uid()と一致する必要あり
- UPDATE: 自分のレコードのみ修正可能
- DELETE: 自分のレコードのみ削除可能
[制約条件]
- ポリシー名は "[テーブル名]_[操作]_[ロール]" 形式で作成
- Supabase auth関数を使用: auth.uid(), auth.role()
- 管理者ロールは別途処理しないRLSポリシーの例(タイピングルーム基準)
sql
-- RLS有効化
ALTER TABLE typing_sessions ENABLE ROW LEVEL SECURITY;
-- SELECT: 自分のレコードのみ照会
CREATE POLICY "typing_sessions_select_authenticated"
ON typing_sessions
FOR SELECT
TO authenticated
USING (user_id = auth.uid());
-- INSERT: 自分のuser_idでのみ挿入
CREATE POLICY "typing_sessions_insert_authenticated"
ON typing_sessions
FOR INSERT
TO authenticated
WITH CHECK (user_id = auth.uid());
-- UPDATE: 自分のレコードのみ修正
CREATE POLICY "typing_sessions_update_authenticated"
ON typing_sessions
FOR UPDATE
TO authenticated
USING (user_id = auth.uid())
WITH CHECK (user_id = auth.uid());
-- DELETE: 自分のレコードのみ削除
CREATE POLICY "typing_sessions_delete_authenticated"
ON typing_sessions
FOR DELETE
TO authenticated
USING (user_id = auth.uid());よく使うパターン
公開読み取り+本人のみ書き込み
sql
-- 誰でも読み取り可能(ブログ投稿など)
CREATE POLICY "posts_select_public"
ON posts FOR SELECT TO anon, authenticated
USING (true);
-- 作成者のみ修正・削除
CREATE POLICY "posts_update_owner"
ON posts FOR UPDATE TO authenticated
USING (author_id = auth.uid())
WITH CHECK (author_id = auth.uid());チームベースのアクセス(結合テーブルの活用)
sql
-- team_membersテーブルにいるユーザーのみアクセス
CREATE POLICY "documents_select_team_member"
ON documents FOR SELECT TO authenticated
USING (
EXISTS (
SELECT 1 FROM team_members
WHERE team_members.team_id = documents.team_id
AND team_members.user_id = auth.uid()
)
);Service RoleはRLSをバイパス
Edge FunctionでSUPABASE_SERVICE_ROLE_KEYを使用するとRLSをバイパスします。管理者作業やバックグラウンド処理に活用します。
typescript
// Edge FunctionでService Roleを使用(RLSバイパス)
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! // ← RLSバイパス
)⚠️
SERVICE_ROLE_KEYはクライアント(ブラウザ)に絶対に公開してはいけません。Edge Functionのサーバーサイドでのみ使用してください。
RLSポリシーのテストプロンプト
作成後にClaudeへテストクエリの生成も依頼しましょう。
「先ほど作成したRLSポリシーが正しく動作するか確認するSQLテストケースを作成してください。
- 認証済みユーザーが自分のレコードを照会・修正・削除するケース
- 他のユーザーのレコードにアクセスしたときに拒否されるケース
- 未認証ユーザー(anon)がアクセスしたときに拒否されるケース」CLAUDE.mdに共通ルールを登録する
markdown
## Supabase RLSルール
- RLSポリシーは操作ごと(SELECT/INSERT/UPDATE/DELETE)に分けて作成
- ポリシー名: "[テーブル名]_[操作]_[ロール]" 形式(例: users_select_authenticated)
- 認証関数: auth.uid() を使用(current_userの使用禁止)
- 新しいテーブル作成時は必ずRLS有効化を含める
- Service Role KeyはEdge Functionでのみ使用、クライアントへの公開禁止