(更新: 2026.03.21)

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