FirebaseError: Firebase Storage: User does not have permission to access 'vehicles/LwDaUZiAn8WIfye5Oj6S/backgroundPic'. (storage/unauthorized)
I am completly stuck with firebase rules. It's not working at all. I already checked if userid, carid is correct and it was correct. Firestore path is also correct.
const fetchCar = async (user: User, carId: string) => {
setLoading(true);
setError(null);
try {
const carRef = doc(db, "vehicles", carId);
const carSnap = await getDoc(carRef);
if (carSnap.exists()) {
let carData: any = { id: carSnap.id, ...carSnap.data() };
// Try to fetch backgroundPic from storage
try {
const url = await getDownloadURL(
storageRef(storage, `vehicles/${carId}/backgroundPic`)
);
carData.image = url;
} catch (e) {
carData.image = "/logo.png";
}
setCar(carData);
document.title = `IDMOTO | ${carData.manufacturer} ${carData.model}`;
} else {
setError("Car not found.");
}
} catch (err) {
console.error("Error fetching car data:", err);
setError("Failed to fetch car data.");
}
setLoading(false);
};
const handleUpload = async (file: File) => {
if (!user) {
alert("You must be logged in!");
return;
}
if (!carId) {
alert("No car ID!");
return;
}
await uploadPhoto(file, user, carId);
await fetchCar(user, carId);
// do something with the url
};
export const uploadPhoto = async (file: File, user: User, carid: string): Promise<string> => {
if (!file) throw new Error("No file provided.");
if (!user) throw new Error("User is not authenticated. Please log in.");
if (!carid) throw new Error("Car ID is not provided.");
const storageRef = ref(storage, `vehicles/${carid}/backgroundPic`);
const uploadTask = uploadBytesResumable(storageRef, file);
return new Promise<string>((resolve, reject) => {
uploadTask.on(
"state_changed",
(snapshot: UploadTaskSnapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log(`Upload is ${progress.toFixed(0)}% done`);
},
(error) => {
console.error("Upload failed", error);
reject(error);
},
async () => {
try {
const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
resolve(downloadURL);
} catch (error) {
reject(error);
}
}
);
});
};
Firestore rules:
rules_version = '2';
// Allow read/write access to a document keyed by the user's UID
service cloud.firestore {
match /databases/{database}/documents {
// User private data
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
match /users/{userId}/vehicles/{vehicleId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
// Public vehicles collection
match /vehicles/{vehicleId} {
allow read: if true;
allow get: if request.auth != null && request.auth.uid == resource.data.userID;
allow write: if request.auth != null
&& (
// Creating or updating: userID in the data being written
request.auth.uid == request.resource.data.userID
// Updating or deleting: userID in the existing document
|| (resource.data.userID != null && request.auth.uid == resource.data.userID)
);
}}}
Firestorage rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /vehicles/{carId}/{documents=**} {
allow read: if true;
allow write: if request.auth != null &&
get(/databases/(default)/documents/vehicles/$(carId)).data.userID == request.auth.uid;
}
}
}
When firestorage rules only include "allow write: if true" or "allow write: if request.auth != null" it is working. Only after typing get line, it stops working.
CarId is param and it looks like this:
Tried a lot of things. Nothing works. Is there any way to make it work like that?
According to the documentation, the syntax for getting a document from Firestore from within Firebase Storage rules is firestore.get(...)
(not get(...)
as you have now).
You can access documents in Cloud Firestore to evaluate other authorization criteria.
Using the
firestore.get()
andfirestore.exists()
functions, your security rules can evaluate incoming requests against documents in Cloud Firestore. Thefirestore.get()
andfirestore.exists()
functions both expect fully specified document paths. When using variables to construct paths forfirestore.get()
andfirestore.exists()
, you need to explicitly escape variables using the$(variable)
syntax.
Also, don't use data
on the result of firestore.get()
. The example in the linked documentation shows what to do:
allow read: if club in
firestore.get(/databases/(default)/documents/users/$(request.auth.id)).memberships