'setting firebase storage rule to watch for a piece of state in React Firebase

Is it possible to set a firebase storage rule to watch the value of a piece of state?

I am not using firebase auth for my app I just want to use a bucket for file storage. I have a state variable within my app:

  const [state, setState] = useState({
    currentUser: null,
    isAuthed: false
  });

If the user is authenticated the isAuthed value will flip to true. Therefore would it be possible to write a rule set that looks as so:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if state.isAuthed === true;
    }
  }
}


Solution 1:[1]

Your post raises two questions:
How to pass data to storage rules?
How to check for authentication status without using firebase authentication?

?? Passing data to storage rules

File path

You could save your file to the path /userfiles/authenticated/... to signal that the file was uploaded by an authenticated user. In the storage rule, you have access to the path through the match clause:

match /userfiles/authenticated/{allPaths=**} {
  allow read, write: if true;
}

Custom metadata

When uploading a file you can set custom metadata this way:

const metadata = { customMetadata: { isAuthed: true } };
const uploadTask = uploadBytes(storageRef, file, metadata);

Then you can read the metadata in the storage rules:

match /{allPaths=**} {
  allow read, write: if request.resource.metadata.isAuth == true;
}

Custom claims or custom tokens

Custom claims or custom tokens allow assigning data to a user in a secure way, this data is then passed to the storage rule. Custom claims necessitate using firebase authentication, but custom tokens allow you to assign a token from your server without using firebase authentication. To read the data:

match /{allPaths=**} {
  allow read, write: if request.auth.token.isAuth == true;
}

? Checking authentication status

Use custom token

The easiest way to ensure only authenticated users can upload is through custom claims or custom token, as detailed above.

Cryptographic trick

?? For fun only, use at your own risks
Let's roll our own crypto protocol to have a secure way of allowing upload only to authenticated users. NB: this does not prevent read access because we cannot provide metadata.

1- An user requests an upload token from your server:

const crypto = require("crypto");
const SECRET = "S3CRET"; // a secret shared by your server and security rules

// If the user is authenticated, send them this upload token:
const nonce = crypto.randomBytes(9).toString('base64');
const data = `${nonce},${filepath},${SECRET}`
const token = { nonce, hash: crypto.createHash('sha256').update(data).digest('base64') };

2- You pass the upload token to the storage rule via the file path or custom metadata as described above

3- In the storage rule, you validate the hash:

match /{allPaths=**} {
  allow read: if true;
  allow write: if verifyHash(request, allPaths);
}

function verifyHash(request, path){
  let nonce = request.resource.metadata.nonce;
  let hash = request.resource.metadata.hash;
  let hash2 = hashing.sha256(nonce + "," + path + ",S3CRET")).toBase64();
  return hash == hash2; 
}

4- profit: only users who have an upload token can upload a file, as a bonus you also enforce the file path, and you could also enhance the token with a timestamp, and enforce some kind of rate limit.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1