eozzy
eozzy

Reputation: 68660

Public access to Firestore collection doc on condition

I need to allow public access to a collection but only for querying a specific document and only if it matches a precondition.

So for example I have a collection userdata which contains the document:

5a70afcd3b26d6a5: {
    id: 7799389,
    email: [email protected]
    ...
}

I need to create a rule to allow read access only if the collection is queried (even before authentication) with the id and email, not otherwise. Is that possible at all?

Upvotes: 0

Views: 299

Answers (3)

ch_g
ch_g

Reputation: 1440

I am just wandering if your actual need is to allow user to view her own record only?

If so, I'd suggest -

  1. Require user to be authenticated by firebase first to get a firebase auth token.

  2. User must log in to your server with the firebase token obtained in the above step.

  3. In you authentication logic, map firebase UID embedded in the token to user id and email. I assume you have a table (or collection, to be exact, in firebase) to save such relations.

  4. Save user id and email obtained in the above step as firebase token claims. Here is C# code example, you should be able to do it similarly in javascript -

    await FirebaseAdmin.Auth.FirebaseAuth.DefaultInstance.SetCustomUserClaimsAsync( uid, new Dictionary<string, object> { {"user_id", userId}, {"user_email", userEmail } });

  5. Create the following data rule to guarantee user only can read her own record -

    match /userdata/{user} {
       allow read: if resource.data.userid == request.auth.token.userid &&
                      resource.data.userEmail == request.auth.token.userEmail 
     }     
    

If userid is enough to uniquely identify a user, you do not need to save userEmail in token claim and do not need to check it in the rule.

Upvotes: 0

emil
emil

Reputation: 6364

That's not possible with your current structure. However you can have a workaround by leveraging request.query in security rule.

For example

service cloud.firestore {
  match /databases/{database}/documents {
    match /userdata/{userid} {
      // Deny any query not limited 1 and contains offset or orderBy.
      allow list: if request.query.limit == 1 && !request.query.offset && !request.query.orderBy;
    }
  }
}

By doing this, you are exposing only first document in your collection to public, others are only queryable with certain conditions.

Another possible solution is to re-organize your collection just like @Oliver answered, and allow querying by ID only. Be ware of using special characters like . or [ in ID though.

Last but not least, write a cloud function that accepts email and id as parameter and returns doc as result. It might be slower than using firestore directly.

Upvotes: 0

l1b3rty
l1b3rty

Reputation: 3642

My understanding of your requirements: you want users to be able to retrieve a document from a given collection only if they know in advance some specific fields.

For your example, with the fields id and email a solution would be to name the document {id}_{email} and only provide a rule for get. The document's name itself becoming the secret.

[email protected]: {
    id: 7799389,           // May be omitted as already in the doc name
    email: [email protected]   // May be omitted as already in the doc name
    ...
}

get: allow if...     // additional conditions if needed

If you don't want to or cant change the document id, you would need to match the document with some condition based on the user requesting which is hardly possible if the user is unauthenticated.

Upvotes: 1

Related Questions