こんにちは、リベロジックのまっつんです。
前回の記事ではSupabase AuthとCloudflareによる認証付きWebアプリを紹介しました。今回はその続きで少し触れた**RLS(Row Level Security)**の内容です。
Supabase Authを使えば、
「誰がログインしているのか」
は確認できますが業務アプリでは認証だけでは不十分です。
「その人が、どのデータを見ていいのか」
「どのデータを変更していいのか」
まで決める必要があり、そこで使うのがRLSです。
今回は業務アプリにおけるSupabaseのRLSの使い方を紹介します。
RLSとは
RLSはRow Level Securityの略でデータベースの「行」単位でアクセスを制御する仕組みです。
例えば申請データがこんな感じで入っているとします。
申請ID, 申請者, 所属会社, 所属拠点, 申請内容, ステータス
同じテーブルの中に全ユーザーの申請が入っていますが、
- 一般社員がログインした時には自分の申請だけ表示したい。
- 上長なら自分の部署や拠点の申請を見せたい。
- 本部管理者ならすべての申請を見せたい。
こういう制御をデータベース側で行うのがRLSであり、業務アプリでは重要な機能のひとつです。
AuthとRLSは役割が違う
前回の記事でも書きましたがSupabase AuthとRLSは役割が違います。
役割を分けると以下のような関係です。
Supabase Auth → 誰なのか確認する
RLS → その人がどのデータにアクセスできるか決める
例えば森本さんがログインしたとします。Supabase Authによって、
ユーザーID:12345
というユーザーだということが分かります。RLSではそのユーザーIDを使って、
申請者ID = 12345
のデータだけ取得できるようにします。
これで同じテーブルに1万件の申請データが入っていても森本さんからは自分の申請しか見えない状態を作れます。
一番分かりやすいのは「自分のデータだけ」
RLSで最初にイメージしやすいのは、自分のデータだけ見せるパターンです。
例えば会員向けマイページなら、
- 自分の登録情報
- 自分の申込履歴
- 自分の契約情報
- 自分の問い合わせ履歴
- 自分がアップロードしたファイル
だけを表示します。
アプリケーション側でも当然ユーザーIDを指定してデータを取得しますが、それだけに頼らず、データベース側でも**「ログインしている本人のデータだけ」**というルールを持たせます。
アプリケーションとデータベースの両方で制御することが重要です。
画面側の実装を間違えたとしてもRLSによって別のユーザーのデータを取得できない状態にしておけます。
会社単位で分ける
取引先向けポータルなどでは、ユーザー単位ではなく会社単位でデータを見せることがあります。
例えば、
A株式会社
├ 森本さん
├ 大塚さん
└ 山田さん
という3人のユーザーがいた場合、3人ともA株式会社の案件を見ることができますが、B株式会社の案件は見ることができないという制御です。データとしては、
ユーザー → 所属会社 → 案件
という関係を持たせておきます。
そしてRLS側で、ログインしているユーザーが所属している会社のデータだけアクセスできるようにします。
取引先向けポータルでよく使われる構成であり、同じWebアプリを何十社、何百社が使っていてもログインした会社によって見えるデータが変わります。
拠点単位で分ける
もう少し業務アプリらしくなってくると会社だけではなく拠点も入ってきます。例えば、
本部
東京支店
├ 担当者A
└ 担当者B
大阪支店
├ 担当者C
└ 担当者D
という構成です。
- 東京支店の担当者は東京支店のデータだけ。
- 大阪支店の担当者は大阪支店のデータだけ。
- 本部は全部見られる。
という店舗管理・営業所管理・代理店管理などではよくある構成です。このあたりから、
ユーザー → 会社 → 拠点 → 役割 → アクセスできるデータ
という権限設計になり、業務アプリではこの権限設計がかなり重要になります。
管理者は全部見られる
当然管理者という役割もあります。例えば、
- 一般ユーザー
- 拠点管理者
- 本部管理者
- システム管理者
のように権限を分けます。
- 一般ユーザーなら自分のデータ。
- 拠点管理者なら自分の拠点。
- 本部管理者なら全拠点。
- システム管理者ならユーザー管理などもできる。
という形です。
一般ユーザー → 自分だけ
拠点管理者 → 所属拠点
本部管理者 → 全拠点
こうしたルールをアプリ側だけではなくRLSにも持たせます。
管理画面のメニューを非表示にしただけでは権限制御にはならないため、データそのものにアクセスできないようにしておく必要があります。
「見る」だけではなく「変更できるか」も制御する
RLSはデータを見る時だけに使うわけではありません。
例えば申請アプリなら、
一般社員は、
- 自分の申請を作成できる
- 自分の下書きを編集できる
- 承認済みの申請は変更できない
上長は、
- 部下の申請を閲覧できる
- 承認・差し戻しできる
本部管理者は、
- 全件確認できる
といったルールがあります。つまり、
見る・登録する・変更する・削除する
それぞれに対して考える必要があり、単純に「この人はこのデータを見られる」というだけではなく、業務上そのデータをどう扱っていいのかまで権限設計に含めます。
閲覧だけでなく登録や更新などの操作権限も設計する必要があります。
アプリ側だけで権限を管理しない
例えばフロント側で、
if 管理者 → 管理ボタンを表示
とすること自体は普通にありますが、画面上の表示制御だけでは不十分です。
- ブラウザからボタンが見えなくても、APIを直接呼び出されたらどうするのか。
- URLを直接指定されたらどうするのか。
- 別のユーザーIDを指定してデータを取得されたらどうするのか。
という話が出てきます。そのため実際には、
画面
↓
アプリケーション
↓
API
↓
RLS
↓
Database
というように複数の場所で確認します。
RLSはその一番奥のデータベースに近いところでデータを守る役割です。
業務アプリでは「画面で隠れているから大丈夫」ではなく、そもそも取得できない状態にしておくことが重要です。
RLSだけですべて解決するわけでもない
逆にRLSだけですべての権限制御をやろうとする必要もありません。
例えば、
- この画面を開いていいのか
- このAPIを実行していいのか
- このボタンを表示するのか
- この処理を実行する前に承認済みか確認する
といったところはアプリケーション側でも判定します。
Cloudflare Workersを使っている場合には、Workers側で外部APIとの通信やサーバー側の処理を行うこともあります。
なので実際には、
Cloudflare
↓
Next.js
↓
Supabase Auth
↓
アプリケーション側の権限確認
↓
RLS
↓
Database
みたいな感じです。
全部を一か所で何とかしようとするより、それぞれの層で必要なところを守ります。
管理処理ではRLSを通さないこともある
少しだけ技術的な話です。
業務アプリではユーザー本人が操作する処理とは別にシステム側で動かす処理があります。
例えば、
- 管理者向けの一括処理
- バッチ処理
- 外部システムとの連携
- データ移行
- 管理用API
などです。
こうした処理ではRLSの制約を受けずにデータを扱う場合もあります。
Supabaseにはサーバー側の管理処理で利用するための権限がありますが、これは当然ブラウザ側に出してはいけません。
ユーザーからの操作
↓
RLSを通す
管理・システム処理
↓
サーバー側で必要な権限を使う
というように分けています。
何でも管理権限で動かしてしまうと、せっかくRLSを設定している意味がなくなってしまいます。
業務アプリでは最初に権限表を作る
RLSを書く前にまず業務上の権限を整理します。
例えば申請アプリならこんな感じです。
自分 拠点 全体 承認
一般ユーザー ○ × × ×
上長 ○ ○ × ○
本部管理者 ○ ○ ○ ○
実際にはもっと細かく、
- 閲覧
- 登録
- 更新
- 削除
- 承認
- 出力
などに分けます。
これを決めてから、
どのテーブルに
どんな情報を持たせるか
↓
どのユーザーが
どの行にアクセスできるか
↓
RLSをどう設定するか
を考えます。
RLSの設定より先に業務上の権限を整理します。
RLSはあくまで業務上決めた権限をデータベースに落とし込む仕組みです。
権限設計は最初から考えた方が楽
業務アプリを作っていると、「とりあえず最初は全員見られるようにして後から権限を付けましょう」という話になることがあります。
小さな試作であればそれでもいいのですが本番運用を考えると権限設計は早めに考えておいた方が楽です。
後から、
- 会社を分ける
- 拠点を分ける
- 管理者を追加する
- 代理店を追加する
- 外部スタッフを追加する
となるとデータ構造そのものを変更しなければならないことがあります。
SupabaseでRLSを使う場合も同じで、
誰と誰が、どの単位でデータを共有するのか
を先に考えておくと設計がかなりきれいになります。
まとめ
SupabaseのRLSを使うと、業務アプリで必要になる細かなデータアクセスの制御をデータベース側に持たせることができます。
例えば、
- 自分のデータだけ見せる
- 同じ会社のデータだけ見せる
- 同じ拠点のデータだけ見せる
- 管理者は全件見られる
- 特定の人だけ更新できる
といった制御です。
Supabase Authは「誰なのか」を確認し、RLSは「その人がどのデータを扱えるのか」を制御します。
この組み合わせによって、業務アプリに必要なアクセス制御を実装できます。
ただし大事なのはRLSを書くこと自体ではありません。
まず、
誰が、何を、どこまでできるのか
という業務上の権限を整理することです。
そこが決まれば、Supabase Auth、アプリケーション側の権限制御、RLSの役割も自然に決まってきます。
次回はCloudflare WorkersとSupabaseを組み合わせる理由について取り上げます。
普段はわりとマイペースだけど気になることは納得するまで調べるタイプ。試行錯誤を重ねながら今日も静かに開発に向き合っている。
まっつん
フロントエンドエンジニア