This error occurs when a user attempts to access or modify files in Firebase Storage without the required permissions. The primary cause is misconfigured Firebase Storage security rules that do not authorize the current user's operation.
The "storage/unauthorized" error indicates that Firebase Storage has rejected an operation because the current user lacks the necessary permissions to access or modify the requested resource. This is a security feature that enforces the Firebase Storage Security Rules you've defined in the Firebase Console. Unlike network errors or service outages, this error occurs when your security rules are explicitly denying the operation. The error is thrown before the operation reaches Google Cloud Storage, meaning Firebase has evaluated your rules and determined the user is not authorized to proceed. This error can occur for several reasons: the user is not authenticated when your rules require authentication, the user is authenticated but doesn't have access to the specific file path, or your rules use conditions (like checking a user ID in the file path) that don't match the current user's identity.
First, confirm that your user is properly authenticated before attempting Storage operations. Check whether authentication is actually required by your security rules.
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
console.log("User authenticated:", user.uid);
console.log("User email:", user.email);
} else {
console.log("User not authenticated");
}
});If the user is not authenticated and your rules require authentication, implement sign-in before attempting Storage operations:
import { signInAnonymously } from "firebase/auth";
try {
const result = await signInAnonymously(auth);
console.log("Anonymous sign-in successful:", result.user.uid);
} catch (error) {
console.error("Sign-in failed:", error);
}Navigate to the [Firebase Console](https://console.firebase.google.com/), select your project, go to Storage, and click the Rules tab at the top.
Look at your current rules and understand what access they grant. Common rule patterns:
Allow all reads/writes (test mode only - not for production):
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write;
}
}
}Allow only authenticated users:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}Allow users to only access their own files:
service firebase.storage {
match /b/{bucket}/o {
match /users/{userId}/{allPaths=**} {
allow read, write: if request.auth.uid == userId;
}
}
}If your rules are too restrictive, you will receive the storage/unauthorized error. Note that "test mode" rules are temporary and expire after 30 days.
If your rules check request.auth != null (authenticated users only), ensure your user is signed in before accessing Storage.
Important: request.auth is specifically for Firebase Authentication. If you're not using Firebase Authentication, request.auth will always be null, and authenticated-only rules will deny access.
Sign in your user with Firebase Authentication:
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
const auth = getAuth();
try {
const userCredential = await signInWithEmailAndPassword(auth, email, password);
console.log("Signed in:", userCredential.user.uid);
} catch (error) {
console.error("Sign-in error:", error.code);
}After successful sign-in, Firebase will automatically include the user's authentication token in Storage requests, and your rules can verify request.auth != null.
If your rules restrict access to specific paths (e.g., user/{userId}/...), ensure your code accesses files using the correct path structure.
Rule example:
match /users/{userId}/{allPaths=**} {
allow read, write: if request.auth.uid == userId;
}Correct code (accessing own files):
import { getStorage, ref, uploadBytes } from "firebase/storage";
import { getAuth } from "firebase/auth";
const auth = getAuth();
const storage = getStorage();
const user = auth.currentUser;
if (user) {
// Upload to user-specific path
const storageRef = ref(storage, `users/${user.uid}/myfile.txt`);
await uploadBytes(storageRef, file);
}Incorrect code (different path):
// This will fail because it doesn't match users/{uid}/... pattern
const storageRef = ref(storage, `files/myfile.txt`);
await uploadBytes(storageRef, file); // ✗ storage/unauthorizedEnsure your file references match the path structure defined in your security rules.
The Firebase Console includes a Rules Playground to test your security rules without affecting your live app.
1. Open the Rules tab in the Storage section
2. Click Rules Playground or scroll down to see the playground panel
3. Select your simulation settings:
- Authentication: Choose "Custom token" or "None"
- For "Custom token", enter a user UID
- Resource path: Enter the full path you're trying to access (e.g., users/abc123/file.txt)
- Operation: Select "read" or "write"
4. Click Run to simulate the request
The playground will show whether your rule allows or denies the operation and why. This helps identify if path matching or authentication checks are the issue.
For more thorough testing, use the [Firebase Local Emulator Suite](https://firebase.google.com/docs/emulator-suite):
npm install -D @firebase/rules-unit-testing
firebase emulators:startThis allows you to test your rules in a local environment before deploying to production.
Once you've identified the issue, update your rules to allow the necessary operations.
Example: Allow authenticated users to read and write:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}Example: User-specific paths with role-based access:
service firebase.storage {
match /b/{bucket}/o {
// Users can access their own folders
match /users/{userId}/{allPaths=**} {
allow read, write: if request.auth.uid == userId;
}
// Public files anyone can read
match /public/{allPaths=**} {
allow read;
allow write: if request.auth != null && request.auth.token.admin == true;
}
}
}After updating:
1. Click Publish in the Firebase Console
2. Confirm the deployment (rules take effect immediately, within seconds)
3. Test your app again - the storage/unauthorized error should be resolved
Be careful with overly permissive rules in production. Always follow the principle of least privilege.
Custom Claims and Advanced Rules: If your rules check for custom claims (e.g., request.auth.token.admin), ensure the user has those claims set via the Firebase Admin SDK:
// Admin SDK - set custom claims
import * as admin from "firebase-admin";
await admin.auth().setCustomUserClaims(uid, { admin: true });After setting claims, the user must sign out and sign back in for the new claims to take effect.
Wildcard and Variable Matching: Storage paths can use wildcard variables:
- {variable} - Matches one path segment (single level)
- {variable=**} - Matches multiple path segments (any depth)
For example:
match /public/{year}/{month}/{file} {
allow read; // Matches /public/2025/12/photo.jpg
}Service Accounts and Admin Access: The Firebase Admin SDK bypasses security rules entirely. If you're using the Admin SDK and still getting storage/unauthorized, the issue is likely IAM permissions at the Google Cloud level, not Storage Security Rules:
// Admin SDK bypasses rules (should not get storage/unauthorized)
import * as admin from "firebase-admin";
const bucket = admin.storage().bucket();
await bucket.file("path/to/file").download(); // ✓ No rules checkMigration from Anonymous to Authenticated: If you're migrating an app from allowing anonymous access to requiring authentication, update your rules gradually to avoid breaking existing anonymous users:
service firebase.storage {
match /b/{bucket}/o {
match /legacy/{allPaths=**} {
allow read; // Keep legacy content public
}
match /authenticated/{allPaths=**} {
allow read, write: if request.auth != null; // New content requires auth
}
}
}Debugging Permission Issues: Enable detailed logging in your app to identify which user is making the request:
import { getAuth } from "firebase/auth";
import { getStorage, ref, getBytes } from "firebase/storage";
const auth = getAuth();
const user = auth.currentUser;
const storage = getStorage();
const fileRef = ref(storage, "path/to/file");
console.log("Attempting read with user:", user?.uid || "anonymous");
try {
const data = await getBytes(fileRef);
} catch (error) {
console.error("Read failed - user:", user?.uid, "error:", error.code);
}Use this information to compare against your rules and confirm the user has the required permissions.
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