This error occurs when a user lacks authorization to read or write a specific Firestore document. It's caused by security rules denying access, unauthenticated users, or insufficient user permissions. Fix it by reviewing security rules, ensuring proper user authentication, and configuring rules to grant appropriate access.
This error means Firebase Firestore has rejected your read or write operation because the security rules do not permit the current user to access that document. Firestore enforces security rules server-side before any data operation occurs—no client-side workaround can bypass them. The error typically indicates one of three issues: (1) the user is not authenticated when the rules require it, (2) the security rules explicitly deny access to the requested document or collection, or (3) the user's identity (UID, email, or custom claims) doesn't match the rule conditions required for access. Unlike the Realtime Database, Firestore permission denied errors are document-specific. You might have access to one document but not another, depending on your security rules and the document's path or data.
Always check that the user is logged in before attempting database operations. Use onAuthStateChanged() to wait for authentication to complete:
import { getAuth, onAuthStateChanged } from "firebase/auth";
import { getFirestore, doc, getDoc } from "firebase/firestore";
const auth = getAuth();
const db = getFirestore();
onAuthStateChanged(auth, async (user) => {
if (user) {
// User is authenticated, safe to access Firestore
const docRef = doc(db, "users", user.uid);
const docSnap = await getDoc(docRef);
console.log(docSnap.data());
} else {
console.log("User not authenticated");
}
});Never attempt Firestore operations in the root of your app or before onAuthStateChanged fires.
Go to Firebase Console > Firestore Database > Rules tab. The most common pattern is allowing users to read/write only their own documents by matching UID:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{uid} {
allow read, write: if request.auth.uid == uid;
}
}
}This rule allows users to access /users/{uid} only if they're authenticated and their UID matches the document ID.
For public collections, you might allow authenticated reads:
match /public/{document=**} {
allow read: if request.auth != null;
allow write: if false;
}Never use this for production:
match /{document=**} {
allow read, write: if true;
}This makes your database publicly readable and writable—attackers can delete all your data.
In Firebase Console, go to Firestore > Rules and click the "Rules playground" button (or refresh after editing rules). Test if your rules allow the operation:
1. Click "Simulate read" or "Simulate write"
2. Enter the document path (e.g., users/user123)
3. Scroll down to the "Authentication" section and set the user UID:
- Click Edit next to Authentication
- Paste: { "uid": "user123" }
4. Click "Run"
The playground will tell you which rule lines allowed or denied the operation. Fix any issues before publishing.
Firestore passes the authenticated user information to your rules via request.auth. Make sure your app is signed in correctly:
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
import { getFirestore, doc, setDoc } from "firebase/firestore";
const auth = getAuth();
const db = getFirestore();
// 1. Sign in the user
await signInWithEmailAndPassword(auth, email, password);
// 2. Get the authenticated user
const user = auth.currentUser;
console.log("Signed in as:", user.uid);
// 3. Now try to write to Firestore
if (user) {
const userRef = doc(db, "users", user.uid);
await setDoc(userRef, {
email: user.email,
createdAt: new Date()
});
}If auth.currentUser is null or undefined, the write will be rejected because request.auth will be empty in your rules.
If your rules check for custom claims (e.g., role: "admin"), ensure the claims are set on the user's token:
// Rule that checks custom claims
match /admin/{document=**} {
allow read: if request.auth.token.admin == true;
}Custom claims must be set from a backend using Firebase Admin SDK, not from the client:
// Backend only (Node.js, Cloud Function, etc.)
const admin = require('firebase-admin');
const uid = 'user123';
await admin.auth().setCustomUserClaims(uid, { admin: true });
console.log('Custom claims set for user:', uid);After setting custom claims, the user must sign out and sign back in for the claims to appear in their ID token. Client-side SDKs cannot set custom claims—attempting to do so is a security vulnerability.
If your rules have multiple conditions or roles, test each scenario in the playground:
// Example rule with multiple conditions
match /documents/{documentId} {
allow read: if request.auth != null &&
request.auth.token.role in ['admin', 'editor'];
allow write: if request.auth.uid == resource.data.owner &&
request.auth.token.role == 'admin';
}Test each scenario:
- Unauthenticated user: Leave Authentication empty, expect Deny
- Regular user: Set { "uid": "user1", "token": { "role": "user" } }, expect Deny
- Editor user: Set { "uid": "user2", "token": { "role": "editor" } }, expect Allow for read only
- Admin user: Set { "uid": "doc_owner_id", "token": { "role": "admin" } }, expect Allow for write
Rule Propagation Delay: Updates to Firestore security rules can take up to a minute to affect new queries and listeners. If you just published new rules, wait a moment before testing—the error might be cached.
Not a Filter: Security rules are not query filters. You cannot write a query for all documents in a collection and expect Firestore to return only the ones you have permission to access. If any document in the result set is denied, the entire query fails with permission denied. Design queries to match your rule structure.
Service Account Access: If you're accessing Firestore from a backend (Node.js, Cloud Functions), use Firebase Admin SDK with a service account. Service accounts bypass client-side security rules:
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
// This bypasses security rules
await db.collection('users').doc('user123').set({
name: 'John',
role: 'admin'
});Service accounts use IAM permissions instead of Firestore rules. Always use the least-privilege principle when granting service account roles.
Debugging with Logs: Enable debug logging to see exactly why a rule denied access:
// In development only
import { enableLogging } from "firebase/firestore";
enableLogging(true);Check your browser console for detailed rule evaluation messages.
Testing with Firebase Emulator: For local development, use Firebase Emulator Suite to test rules without affecting production:
firebase emulators:startThe emulator lets you iterate on security rules quickly without deployment delays.
Callable Functions: INTERNAL - Unhandled exception
How to fix "Callable Functions: INTERNAL - Unhandled exception" in Firebase
auth/invalid-hash-algorithm: Hash algorithm doesn't match supported options
How to fix "auth/invalid-hash-algorithm: Hash algorithm doesn't match supported options" in Firebase
Hosting: CORS configuration not set up properly
How to fix CORS configuration in Firebase Hosting
auth/reserved-claims: Custom claims use reserved OIDC claim names
How to fix "reserved claims" error when setting custom claims in Firebase
Callable Functions: UNAUTHENTICATED - Invalid credentials
How to fix "UNAUTHENTICATED - Invalid credentials" in Firebase Callable Functions