Supabase + RLSでユーザー管理を実装した
Supabase + RLSでユーザー管理を実装した
求人AIツールにユーザー管理機能を追加した。管理者・一般ユーザー・閲覧者の3つの権限で、データアクセスを制御する仕組み。
データベースにはSupabaseを使い、セキュリティはRLS(Row Level Security)で実装した。
なぜSupabaseを選んだのか
1. 認証が楽
Supabase Authを使えば、メール認証・OAuth・マジックリンク認証が設定だけで実装できる。自前でJWT管理する必要がない。
2. RLSが強力
PostgreSQLのRLS機能を使えば、「このユーザーはこの行だけ見える」みたいなルールをSQL側で定義できる。
アプリケーションコードに認可ロジックを書かなくていいので、セキュリティホールが減る。
3. ローカル開発が簡単
supabase start でローカルにPostgreSQLが立ち上がるので、本番環境と同じ構成で開発できる。
RBACの実装
ロール定義
-- supabase/migrations/20260309000001_user_management_rbac.sql
CREATE TYPE user_role AS ENUM ('admin', 'user', 'viewer');
ユーザーテーブル
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
role user_role DEFAULT 'user',
created_at TIMESTAMPTZ DEFAULT NOW()
);
RLSポリシー
-- 管理者は全データを見れる
CREATE POLICY admin_all ON job_postings
FOR ALL USING (
EXISTS (SELECT 1 FROM users WHERE id = auth.uid() AND role = 'admin')
);
-- 一般ユーザーは自分のデータだけ
CREATE POLICY user_own ON job_postings
FOR ALL USING (user_id = auth.uid());
-- 閲覧者は読み取りのみ
CREATE POLICY viewer_read ON job_postings
FOR SELECT USING (
EXISTS (SELECT 1 FROM users WHERE id = auth.uid() AND role IN ('viewer', 'user', 'admin'))
);
これでアプリケーション側では、supabase.from('job_postings').select() と書くだけで、自動的に権限に応じたデータだけが返ってくる。
詰まったポイント
1. RLSを有効にし忘れる
デフォルトではRLSが無効なので、ALTER TABLE job_postings ENABLE ROW LEVEL SECURITY; を実行し忘れると全データが見えてしまう。
マイグレーションファイルで必ず有効化するように徹底した。
2. service_roleキーでRLSがバイパスされる
サーバー側の処理で service_role キーを使うと、RLSが無視される。
これは意図的な設計だが、クライアント側で間違って使わないように注意が必要。
3. ポリシーの優先順位
複数のポリシーが存在するとき、OR条件で評価される。
「管理者は全部見えるけど、一般ユーザーは自分のだけ」という設計なら問題ないが、複雑な権限設計だと混乱しやすい。
まとめ
Supabase + RLSは、アプリケーションコードをシンプルに保ちながら、セキュアなユーザー管理を実現できる。
認証・認可をフロントエンドで頑張るより、データベース側で守る方が安全。
タグ: #開発実績 #Supabase #セキュリティ #RBAC