I'm trying to build a basic social media site with profile photos that any user can read, but each user can edit only their own photo. I wrote it after reviewing the documentation here
https://firebase.google.com/docs/storage/security/rules-conditions
and when I use the simulator on the storage settings with a test authenticated user I get a success
Here is a photo of the successful test read: https://i.stack.imgur.com/ZCF5L.png
But I also have tried running this on my deployed app and each request no matter read or write, authenticated or not, I keep getting my request declined
failed storage request from deployed app
These are my security rules:
service cloud.firestore {
match /b/{bucket}/o {
match /internal/profilePhoto/{imageId} {
allow read: if request.auth != null;
allow write: if request.auth.uid == imageId;
}
}
}
and here is my code with the address I'm requesting from
async function upload(file, currentUser, setLoading) {
const fileRef = ref(storage, 'internal/profilePhoto/'+currentUser.uid);
setLoading(true);
console.log(currentUser.uid)
const snapshot = await uploadBytes(fileRef, file);
const photoURL = await getDownloadURL(fileRef);
updateProfile(currentUser, {photoURL});
setLoading(false);
alert("Uploaded file!");
}
Any guidance would be greatly appreciated.
I figured it out. I opened another app with the default testing storage settings and my code let me upload to that database. I looked back at my FIREBASE STORAGE security settings and noticed that that first line said
service cloud.fireststore {
That was my problem. I accidentally copied in a rule setting for cloud firestore which left my storage database without any security rules which I assume made the default no access. If you want to set a rule setting for firebase storage than that rule needs to be
service firebase.storage {
I hope this saves at least one person a few hours of head banging.
The full rule setting I'm using for testing is
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**}{
allow read, write: if true;
}
}
}
Related
I have developed a single crud project(Login screen and crud operation screen HTML) and hosted on firebase hosting. where User signing with email and password, I am using firebase signing with email and password and its working as expected.
But now issue is I want to secure backend with auth but its not passing auth in setDoc() deleteDoc() etc, My requirement is without auth. no one should do any operation on database.
import { doc, setDoc } from "firebase/firestore";
await setDoc(doc(db, "cities", "LA"), {
name: "Los Angeles",
state: "CA",
country: "USA"
});
below rules are working but its not secured for production env. :
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true
}
}
}
If set rules like below it give me insufficient permission error. I don't know how to pass UID in setDoc() or any operation.
allow read, write: if request.auth != null
Update : If i put below code before setDoc() below code is not executing because currentUser has user data.
function addCity(){
if (!firebase.auth().currentUser) {
// this code not executing because user is signed
alert("Login required");
window.href = "login.html";
return;
}
// i can print UID and it is showing means user is logged.
await setDoc(doc(db, "cities", "LA"), {
name: "Los Angeles",
state: "CA",
country: "USA"
});
}
This is in detail covered in the Firebase documentation on Security & Rules, which I would recommend you to check out.You can secure your data with Security Rules,Firebase Security Rules are evaluated and based on that the rules language it is validated whether the current user can access your data.
Security Rules give you access to a set of server variables to check your rules against. The most commonly used one is the auth variable which lets you check against the currently authenticated user. You can also create wildcard variables with the $, which acts as a route parameter creating.
{ "rules": { "users": { // users can read and write their own data, but no one else. "$uid": { ".read": "auth.uid == $uid", ".write": "auth.uid == $uid" } } } }
You can also check the feature called Firebase App Check, it will let you limit access to your Realtime Database to only those coming from iOS, Android and Web apps that are registered in your Firebase project.
You can combine this with the user authentication based security described above, so that you have another shield.
Also check these similar examples below:
How to prevent unauthorized access to Firebase database
How to prevent users from changing the Database data
Allow only authenticated users to modify database data
Finally,
I found solution. I was using different version library of firebase. like I was using web v8 library for login and modular lib for database access. I just moved all firebase SDK to same version and modular.
I am working on a react firebase project.
the error I am getting is
"FirebaseError: Missing or insufficient permissions."
Here is my code to get the data
const data = await getDocs(
query(collectionGroup(db, "posts"), orderBy("upvote", "desc"), limit(5)));
Here is my firebase security rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /posts/{docId}{
allow read;
allow update: if request.auth.uid != null;
}
}
}
I have no idea what is going on I allow read in firebase rules but when I go and get the data it does not work
You should check if in the Database under the rules if read: true and write: true if not make those true.
So go to DataBase-> Rules
make
Read:true
Write:true
But for production databases allow only for authenticated users
I am trying to get a list of all files from folder using listAll() method but can't do it.
Here is the code
$(document).ready(function () {
var storageRef = firebase.storage().ref(userid + "/");
console.log(storageRef.listAll());
storageRef.listAll().then(function (result) {
result.items.forEach(function (imageRef) {
imageRef.getDownloadURL().then(function (url) {
$("#album").append("<div class='col-sm-12 col-lg-4'><div class='card'><div class='card-img-top'><img src='" + url + "'></div></div></div>")
}).catch(function (error) {
});
});
}).
catch(function (error) {
});
});
Current Firebase Rule :
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write;
}
}
}
I got this error Listing objects in a bucket is disallowed for rules_version = "1". Please update storage security rules to rules_verison = "2" to use list. inside console.
So what is this error tells? Am i need to change rule version inside firebase console? or should i make read and write rule to allow only authenticated user?
In your security rules, you need to declare that you want to use security rules version 2. According to the documentation:
As of May 2019, version 2 of the Firebase security rules is now
available. Version 2 of the rules changes the behavior of recursive
wildcards {name=**}. You must use version 2 if you plan to use
collection group queries. You must opt-in to version 2 by making
rules_version = '2'; the first line in your security rules:
rules_version = '2';
So, your minimal rules will have to look like this:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write;
}
}
}
Bear in mind that listing files was very recently released in the JavaScript SDK, but it has not been announced or fully documented.
I am using firebase phone auth for the very first time and I see captcha verification is must proceed with the process, as per firebase official documentation. Though it serves a good purpose, but sometimes it becomes very bad for the user experience when it starts asking about the road signs, bridges and all. Is there a way to directly skip to the verification code right after getting user's number? As per the documentation, the code is mentioned below. Thanks.
var phoneNumber = getPhoneNumberFromUserInput();
var appVerifier = window.recaptchaVerifier;
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
.then(function (confirmationResult) {
// SMS sent. Prompt user to type the code from the message, then sign the
// user in with confirmationResult.confirm(code).
window.confirmationResult = confirmationResult;
}).catch(function (error) {
// Error; SMS not sent
// ...
});
var code = getCodeFromUserInput();
confirmationResult.confirm(code).then(function (result) {
// User signed in successfully.
var user = result.user;
// ...
}).catch(function (error) {
// User couldn't sign in (bad verification code?)
// ...
});
Go to Firebase console -->to your project-->project overview settings-->project settings --> App check -->overview (Register your app for SafetyNet).
Then your app will stop redirecting to web for captcha verification
method 1:
firebase.auth().settings.appVerificationDisabledForTesting = true;
Firebase docs
https://firebase.google.com/docs/auth/web/phone-auth?authuser=0#web-v8_6
// Turn off phone auth app verification.
firebase.auth().settings.appVerificationDisabledForTesting = true;
var phoneNumber = "+16505554567";
var testVerificationCode = "123456";
// This will render a fake reCAPTCHA as appVerificationDisabledForTesting is true.
// This will resolve after rendering without app verification.
var appVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
// signInWithPhoneNumber will call appVerifier.verify() which will resolve with a fake
// reCAPTCHA response.
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
.then(function (confirmationResult) {
// confirmationResult can resolve with the fictional testVerificationCode above.
return confirmationResult.confirm(testVerificationCode)
}).catch(function (error) {
// Error; SMS not sent
// ...
});
method 2:
https://firebase.google.com/docs/auth/web/phone-auth#use-invisible-recaptcha
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': (response) => {
// reCAPTCHA solved, allow signInWithPhoneNumber.
onSignInSubmit();
}
});
I had the same problem while integrating iOS SDK.
If google has same architecture and classes of the firebase SDK across languages, this solution might work for you.
Auth.auth().settings?.isAppVerificationDisabledForTesting = true
firebase.initializeApp(firebaseConfig);
// Create a Recaptcha verifier instance globally
// Calls submitPhoneNumberAuth() when the captcha is verified
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
"recaptcha-container",
{
size: "invisible",
callback: function(response) {
submitPhoneNumberAuth();
}
}
);
use size: "normal" to size: "invisible"
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
"recaptcha-container",
{
size: "invisible",
callback: function(response) {
submitPhoneNumberAuth();
}
}
);
Firebase provides two properties for captcha size
Normal - which is visible and captcha code visible to the user and manually perform the captcha process.
Invisible - which is invisible to the user, automated captcha process, and code will auto render in DOM.
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
"recaptcha-container", {
size: "invisible"
}
);
For more details, refer to this Official Link
Use isAppVerificationDisabledForTesting = TRUE in auth settings as the below given snippet:
Auth.auth().settings.isAppVerificationDisabledForTesting = TRUE
Please check the below official information for more details:
JavaScript - https://firebase.google.com/docs/reference/js/firebase.auth.AuthSettings
SDK reference - https://firebase.google.com/docs/auth/ios/phone-auth#integration-testing
Actually you can't. But, some of the devices it does not work. Instead, setup Safety and enable API key. Then back to your project setting in Firebase and copy and paste SHA-25 from Android Gradle to there if it does not exist. In this manner, in app browser redirecting will be no more irritating to you...
According To Google Official Docs 2 things are There :
Add Sha-256 Key to Firebase
Enable SafetyNet : https://console.cloud.google.com/apis/library/androidcheck.googleapis.com
To use phone number authentication, Firebase must be able to verify that phone number sign-in requests are coming from your app. There are two ways Firebase Authentication accomplishes this:
SafetyNet: If a user has a device with Google Play Services installed, and Firebase Authentication can verify the device as legitimate with Android SafetyNet, phone number sign-in can proceed.
To enable SafetyNet for use with Firebase Authentication:
In the Google Cloud Console, enable the Android DeviceCheck API for your project. The default Firebase API Key will be used, and needs to be allowed to access the DeviceCheck API.
If you haven't yet specified your app's SHA-256 fingerprint, do so from the Settings Page of the Firebase console. Refer to Authenticating Your Client for details on how to get your app's SHA-256 fingerprint.
For More Details : https://firebase.google.com/docs/auth/android/phone-auth
Following the post here I created a simple security rule and cloud function that gets called to see if a username already exists. The problem is that the security rule write check always passes and just sets the new value in that location (/username_lookup/user1).
When I try to write at this location using the realtime database rules simulator it works as expected, i.e. the write is blocked.
Can someone spot the problem?
The firebase security rule
"rules": {
"username_lookup": {
"$username": {
// not readable, cannot get a list of usernames!
// can only write if this username is not already in the db
".write": "!data.exists()",
// can only write my own uid into this index
".validate": "newData.val() === auth.uid"
}
}
}
And the cloud function
var fb = admin.database().ref();
createUser(uid, username);
function createUser(userId, usrname) {
fb.child('username_lookup').child(usrname).set(userId, function(unerr) {
if(unerr) {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({error: "the_error_code" }));
}
});
}
Screenshot of the username_lookup object/index
You Cloud Functions access the Firebase database through:
var fb = admin.database().ref();
As you can see, the module is admin which indicates that you're using the Firebase Admin SDK. One of the key traits of the Firebase Admin SDK is:
Read and write Realtime Database data with full admin privileges.
source: https://firebase.google.com/docs/admin/setup
So the Admin SDK actually bypasses your security rules.
It's also a pretty bad practice to use an error handler for basic flow control.
Instead, use a Firebase transaction to read/write the location with the name in an atomic way:
fb.child('username_lookup').child(usrname).transaction(function(value) {
if (value) {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({error: "the_error_code" }));
return; // abort the transaction
}
else {
return userId;
}
});