Search code examples
firebasegoogle-cloud-firestorefirebase-security

Access field key : value pair from an authenticated user from current user


I have a next.js web app where users have to pay to get access to some content. I am using firebase for authentication and stripe for one-time payment using the stripe firebase extension. I was wondering if there was a way to access a key-value pair like paid: true from the "payments collection" so that I could use that information to give access to the user or not. I have tried creating custom claims based off a key-value pair like paid: true but had the same issue of not knowing how to get access to the key-value per for the "current user"

I have included an image below and my rules from firestore: enter image description here

rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{uid} {
  allow read, write
  match /checkout_sessions/{id} {
    allow read, write: if request.auth.uid == uid;
  }
  match /subscriptions/{id} {
    allow read, write: if request.auth.uid == uid;
  }
  match /payments/{id} {
    allow read, write: if request.auth.uid == uid;
  }
}

match /products/{id} {
  allow read: if true;
  allow write: if false;

  match /prices/{id} {
    allow read: if true;
    allow write: if false;
  }

  match /tax_rates/{id} {
    allow read: if true;
  }
}
}
}

Solution

  • I was wondering if there was a way to access a key-value pair like paid: true from the "payments collection" so that I could use that information to give access to the user or not.

    Yes, as explained in the Firestore doc:

    Using the get() and exists() functions, your security rules can evaluate incoming requests against other documents in the database.

    In particular, with get() you can get the contents of a Firestore document. However, note that

    The get() and exists() functions both expect fully specified document paths.

    This means that you need to know the ID of the different docs, i.e. the user and payment docs, in order to get the value of the paid field of the payment doc. I guess you know the user doc ID (probably the user's uid).

    But it seems the payment doc ID is automatically generated by Firestore. This may make it difficult to point to the doc in the security rule. If you know that there is only one payment doc, you can very well assign a fixed doc ID (e.g. paymentDoc) in such a way it is easy (and feasible) to point to this document with the get() method.

    Something like:

    allow read: if get(/databases/$(database)/documents/users/$(request.auth.uid)/payments/paymentDoc).data.paid == true;
    

    Note that by using these security rules methods, each time the rule is evaluated, it counts for one Firestore document read. If you know that this rule will be evaluated many times, it may be better to use another system.

    Like you said, Custom Claims are one possible solution: you cloud have a Cloud Function which adds a specific Custom Claim to the user when the payment is confirmed. (Then you need to deal with another side effect! The claim takes time to propagate to an authenticated user on the client side via the ID token, see here for solutions)