Flutterアプリケーション開発概論

【permission-denied】権限エラーが出る原因と確認方法を理解する

47LINE風チームタスク管理アプリを作りながら、ログイン・データベース・権限管理を学ぶ
FlutteriOSAndroidMacOSWindows基礎から学ぶ開発アプリ開発

このページでやること

このページでは、Firebase / Firestoreでよく出るエラー permission-denied について理解します。

permission-denied は、簡単に言うと、

Firestoreが「この操作は許可されていません」と止めた

という意味です。

エラーが出ると焦りますが、原因を順番に見れば確認できます。


今日のゴール

permission-denied が出たときに、どこを確認すればよいか分かるようにします。

permission-deniedが出た
↓
ログイン状態を確認
↓
Firestore Rulesを確認
↓
uidを確認
↓
teamIdを確認
↓
members/{uid}/roleを確認
↓
保存場所を確認

npmや環境変数は必要?

このページでは、npmは使いません。

環境変数も設定しません。

確認する場所は、この3つです。

Flutterアプリ
Firebase Console
Firestore Rules

permission-deniedとは?

permission-denied は、Firestore Security Rulesによって操作が拒否されたときに出ます。

たとえば、次のような場合です。

未ログインでデータを読もうとした
自分以外のusers情報を書き換えようとした
チームメンバーではないのにtasksを読もうとした
viewerなのにタスクを作成しようとした
memberなのにタスクを削除しようとした

つまり、Firestore側で「この人には許可できません」と判断された状態です。


まず覚えること

permission-denied は、必ずしも悪いエラーではありません。

権限のない人を止められている場合は、正常です。

viewerがタスク削除しようとした
↓
permission-denied
↓
正しく守れている

ただし、許可したい操作まで止まっている場合は、設定やデータを確認します。


1. まずログイン状態を確認する

Step 1:ログインしているか確認する

Firestore Rulesでよく使う条件がこれです。

request.auth != null

意味はこうです。

ログインしている人だけOK

そのため、ログインしていないと permission-denied が出ます。


Step 2:Flutter側でログイン状態を確認する

Flutter側では、次のコードでログイン中ユーザーを確認できます。

final user = FirebaseAuth.instance.currentUser;

確認用に、一時的にログを出すならこうです。

final user = FirebaseAuth.instance.currentUser;
debugPrint('現在のuid: ${user?.uid}');
debugPrint('現在のemail: ${user?.email}');

uid が表示されればログイン中です。

現在のuid: abc123

null の場合は、ログインできていません。

現在のuid: null

Step 3:AuthGateを確認する

このアプリでは、AuthGate でログイン状態を見ています。

stream: FirebaseAuth.instance.authStateChanges(),

ログインしていない場合は、LoginPage が表示されます。

未ログイン
↓
LoginPage

ログイン済み
↓
TeamListPage

まず、アプリがログイン済み画面に進んでいるか確認します。


2. Firestore Rulesを確認する

Step 4:Firebase Consoleを開く

ブラウザでFirebase Consoleを開きます。

https://console.firebase.google.com/

今回のプロジェクトを選びます。


Step 5:Rules画面を開く

次の順番で開きます。

Build
↓
Firestore Database
↓
Rules

日本語表示の場合です。

構築
↓
Firestore Database
↓
ルール

Step 6:今どのRulesか確認する

学習中は、まず現在のRulesを確認します。

開発用Rulesなら、こうです。

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {

    match /{document=**} {
      allow read, write: if true;
    }

  }
}

この場合、基本的に permission-denied は出ません。

出る場合は、別のFirebaseプロジェクトを見ている可能性があります。


Step 7:ログイン必須Rulesの場合

ログイン必須Rulesなら、こうです。

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {

    match /{document=**} {
      allow read, write: if request.auth != null;
    }

  }
}

この場合、未ログインだと permission-denied が出ます。

ログイン中なら、基本的に読み書きできます。


3. Firebaseプロジェクトが合っているか確認する

Step 8:違うプロジェクトを見ていないか確認する

意外と多い原因がこれです。

Flutterアプリが使っているFirebaseプロジェクト
↓
Aプロジェクト

Firebase Consoleで見ているプロジェクト
↓
Bプロジェクト

この場合、Rulesを直してもアプリには反映されません。


Step 9:firebase_options.dartを確認する

Flutter側で、次を開きます。

lib/firebase_options.dart

中の projectId を確認します。

projectId: 'your-project-id',

Firebase Consoleで開いているプロジェクトIDと同じか確認します。

Flutter側のprojectId
Firebase ConsoleのprojectId

この2つが同じならOKです。


4. uidを確認する

Step 10:ログイン中のuidを確認する

Firestore Rulesでは、よく request.auth.uid を使います。

これは、ログイン中のユーザーIDです。

Flutter側では、次で確認できます。

final user = FirebaseAuth.instance.currentUser;
debugPrint('uid: ${user?.uid}');

Step 11:Firestoreのuidと一致しているか確認する

たとえば、users/{uid} を確認します。

Firestore Database
↓
users
↓
自分のuid

Flutter側のuidと、FirestoreのドキュメントIDが同じか見ます。

Flutterのuid: abc123
Firestore: users/abc123

同じならOKです。

違う場合、Rulesで拒否されることがあります。


5. usersでpermission-deniedが出る場合

Step 12:usersの保存場所を確認する

ユーザープロフィールはここです。

users/{uid}

本人だけ許可するRulesの場合、条件はこうなります。

request.auth.uid == userId

つまり、

ログイン中のuid
↓
abc123

アクセス先
↓
users/abc123

このように一致している必要があります。


usersでよくある原因

原因確認すること
未ログインcurrentUser が null ではないか
uid違いusers/{uid} のIDがログイン中uidと同じか
usersが作られていない新規登録時のプロフィール保存を確認
Rulesが厳しいusersルールを確認

6. teamsでpermission-deniedが出る場合

Step 13:teamsの保存場所を確認する

チーム情報はここです。

teams/{teamId}

チーム一覧では、次のように取得しています。

FirebaseFirestore.instance
    .collection('teams')
    .where('memberIds', arrayContains: uid)
    .snapshots()

ここで大事なのが memberIds です。


Step 14:memberIdsに自分のuidがあるか確認する

Firebase Consoleで確認します。

Firestore Database
↓
teams
↓
対象のteamId
↓
memberIds

中に自分のuidが入っているか確認します。

memberIds: [自分のuid]

入っていない場合、チーム一覧に出なかったり、Rulesで拒否されたりします。


teamsでよくある原因

原因確認すること
memberIdsにuidがないteams/{teamId}/memberIds を確認
teamIdが違う開いているチームIDを確認
ログインuidが違うcurrentUser.uid を確認
Rulesがチームメンバーだけ許可membersやmemberIdsを確認

7. membersでpermission-deniedが出る場合

Step 15:membersの保存場所を確認する

チームメンバー情報はここです。

teams/{teamId}/members/{uid}

ここに、チーム内の権限が入っています。

role: owner
role: admin
role: member
role: viewer

Step 16:自分のmembersドキュメントがあるか確認する

Firebase Consoleで確認します。

Firestore Database
↓
teams
↓
対象のteamId
↓
members
↓
自分のuid

自分のuidのドキュメントがなければ、チームメンバーとして扱えません。


Step 17:roleを確認する

members/{uid} の中の role を確認します。

role: owner

または、

role: admin
role: member
role: viewer

権限操作で止まる場合は、この role が原因かもしれません。


membersでよくある原因

操作必要なrole
メンバー追加owner または admin
権限変更owner
メンバー削除owner
メンバー一覧を見るチームメンバー

8. tasksでpermission-deniedが出る場合

Step 18:tasksの保存場所を確認する

タスク情報はここです。

teams/{teamId}/tasks/{taskId}

タスク作成・編集・削除では、権限によって許可が変わります。


Step 19:自分のroleを確認する

タスク操作で止まる場合は、まず自分のroleを確認します。

teams/{teamId}/members/{自分のuid}/role

設計ではこうです。

操作owneradminmemberviewer
タスク作成×
タスク編集×
タスク削除××

tasksでよくある原因

操作permission-deniedの原因
タスク作成viewer になっている
タスク編集viewer になっている
タスク削除member または viewer になっている
タスク一覧表示チームメンバーではない
タスク取得teamIdが違う

9. コードの参照先を確認する

Step 20:collection名を確認する

Firestoreでは、文字が1文字違うだけで別の場所になります。

正しい名前はこちらです。

users
teams
members
tasks

悪い例です。

user
team
member
task

単数形と複数形を間違えると、データが見つからなかったり、Rulesに合わなかったりします。


Step 21:teamIdを確認する

タスクやメンバーは、必ずチームの中にあります。

teams/{teamId}/members
teams/{teamId}/tasks

teamId が違うと、別チームを見に行ってしまいます。


Step 22:doc.idを確認する

タスクやメンバーを更新・削除するときは、正しいIDを使っているか確認します。

doc.id

メンバーの場合は、基本的に uid がドキュメントIDです。

members/{uid}

タスクの場合は、自動生成された taskId です。

tasks/{taskId}

10. 一時的に開発用Rulesで切り分ける

Step 23:Rulesが原因か確認する

原因が分からないときは、一時的に開発用Rulesで確認します。

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {

    match /{document=**} {
      allow read, write: if true;
    }

  }
}

これでエラーが消える場合は、Rulesが原因の可能性が高いです。


Step 24:それでも消えない場合

開発用Rulesでもエラーが消えない場合は、Rules以外を疑います。

Firebaseプロジェクトが違う
collection名が違う
teamIdが違う
uidが違う
Firestoreにデータがない
Firebase初期化が違う

この順番で確認します。


確認フローチャート

permission-deniedが出た
↓
ログインしている?
├─ いいえ → ログインする
└─ はい
   ↓
Firebaseプロジェクトは合っている?
   ├─ いいえ → projectIdを合わせる
   └─ はい
      ↓
Rulesを確認した?
      ├─ いいえ → Firebase ConsoleでRulesを見る
      └─ はい
         ↓
自分のuidは正しい?
         ├─ いいえ → currentUser.uidを確認
         └─ はい
            ↓
チーム操作ならmembers/{uid}がある?
            ├─ いいえ → メンバー追加・memberIdsを確認
            └─ はい
               ↓
roleは操作に必要な権限?
               ├─ いいえ → roleを確認
               └─ はい
                  ↓
teamId / taskId / collection名を確認

よくあるエラーと直し方

エラー内容よくある原因確認場所
permission-denied未ログインFirebaseAuth.instance.currentUser
permission-deniedRulesが厳しいFirebase Console → Rules
permission-denieduidが違うusers/{uid}
permission-deniedチームメンバーではないteams/{teamId}/members/{uid}
permission-deniedroleが足りないmembers/{uid}/role
permission-denied別プロジェクトを見ているfirebase_options.dart
permission-deniedmemberIdsにuidがないteams/{teamId}/memberIds

初心者向け:まず見る場所3つ

読むのが大変な人は、まずここだけ見てください。

1. ログインしているか
2. Firebase ConsoleのRules
3. Firestoreのmembers/{自分のuid}/role

この3つで、かなりの原因が分かります。


確認用ログコード

Flutter側で一時的に確認したい場合は、処理の前に入れます。

final user = FirebaseAuth.instance.currentUser;

debugPrint('--- 権限確認 ---');
debugPrint('uid: ${user?.uid}');
debugPrint('email: ${user?.email}');
debugPrint('teamId: ${widget.teamId}');

タスク一覧やメンバー一覧の画面で確認すると便利です。

確認が終わったら、消してOKです。


permission-deniedが出たときの考え方

大事なのは、焦らず順番に見ることです。

誰が
どこのデータに
何をしようとして
Rulesに止められたのか

この4つで考えます。

例です。

誰が:member
どこ:teams/{teamId}/tasks/{taskId}
何を:delete
結果:memberは削除できないので拒否

この場合、Rulesは正しく動いています。


最短作業まとめ

1. ログイン状態を見る

final user = FirebaseAuth.instance.currentUser;
debugPrint('uid: ${user?.uid}');

2. Firebase ConsoleでRulesを見る

Build
↓
Firestore Database
↓
Rules

3. 自分のroleを見る

teams/{teamId}/members/{自分のuid}/role

4. memberIdsを見る

teams/{teamId}/memberIds

5. 一時的に開発用Rulesで切り分ける

allow read, write: if true;

チェックリスト

□ permission-deniedの意味を理解した
□ ログイン状態を確認した
□ currentUser.uidを確認した
□ Firebase ConsoleのRulesを確認した
□ firebase_options.dartのprojectIdを確認した
□ users/{uid}を確認した
□ teams/{teamId}/memberIdsを確認した
□ teams/{teamId}/members/{uid}を確認した
□ roleを確認した
□ tasksのteamIdを確認した
□ collection名を確認した
□ 必要なら開発用Rulesで切り分けた

ミニ確認問題

Q1. permission-denied は何のエラーですか?

回答

Firestore Security Rulesによって、読み書きが拒否されたときのエラーです。


Q2. 未ログインでFirestoreを読むと、なぜ拒否されますか?

回答

Rulesに request.auth != null がある場合、ログインしていない人は許可されないからです。


Q3. チーム一覧に出ないとき、まず確認する配列は何ですか?

回答

memberIds です。

teams/{teamId}/memberIds

Q4. タスク削除で拒否されたとき、確認する項目は何ですか?

回答

自分の role です。

teams/{teamId}/members/{自分のuid}/role

memberviewer はタスク削除できません。


Q5. 開発用Rulesにしてもエラーが消えない場合、何を疑いますか?

回答

Rules以外の原因を疑います。

Firebaseプロジェクト違い
collection名違い
teamId違い
uid違い
データ未作成

このページのまとめ

  • permission-denied は、Firestore Rulesに拒否されたエラー。
  • まずログイン状態を見る。
  • 次にFirebase ConsoleでRulesを見る。
  • Firebaseプロジェクトが合っているか確認する。
  • チーム操作では memberIdsmembers/{uid}/role が重要。
  • viewer は作成・編集・削除できない。
  • member はタスク削除できない。
  • 開発用Rulesで一時的に切り分けできる。
  • このページではnpmや環境変数は不要。

次のページでやること

次のページでは、users/{uid} のSecurity Rulesを作ります。

本人だけが自分のプロフィールを読み書きできるようにします。

教材トップへ戻る