1

I have set up my Firebase project with the 'Run Subscription Payments with Stripe' Firebase extension and everything is working well. This app requires a user to be able to purchase two types of subscription products:

  1. A membership subscription allowing access to the app in general. This subscription utilises the firebaseRole metadata property of the Stripe product to automatically apply a custom claim of stripeRole: 'member'.
  2. Additional subscriptions which are purchased in order to access different subcollections of the database. These can not utilise the automatic custom claim generation because the stripeRole claim is already utilised by the membership subscription above.

I am now looking to use the security rules to restrict access to the subcollections which require a subscription.

The problem I am facing is that I don't know how to reference the {userId}/subscriptions/ collection in the security rules to ensure that the user is actually subscribed to the data they are trying to access. The documents in the {userId}/subscriptions/ collection are named by the Firebase extension's cloud functions behind the scenes (such as sub_xxxxxxxxxxxxx which I assume are unique Stripe ids specific to that user's subscription.

So how can I check to see if the user possesses a subscription document given that to do so, I would need the rules to make a query on the user's subscriptions collection to find the subscription which has the metadata OR product reference which corresponds to the data they are trying to access, and as far as I know, queries in security rules are impossible?

I would like to avoid having to set up any sort of index collections to track who is subscribed to what as this somewhat defeats the purpose of having the extension synchronise the database with Stripe, and would require me to write an additional cloud function. (Perhaps that's the only option!)

Here are the rules that would do the job, if only they were possible. Hopefully this helps to illustrate the problem:

service cloud.firestore {
  match /databases/{database}/documents {

    match /products/{productId} {

      function userIsSubscribedToProduct() {

        // NOTE: subscriptionId is NOT available but is necessary somehow
        let subscription = get(/databases/$(database)/documents/users/$(request.auth.uid)/subscriptions/$(subscriptionId));

        return subscription.data.status == 'active'
      }

      allow read: if userIsSubscribedToProduct()
    }
  }
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
J Dawg
  • 525
  • 1
  • 5
  • 14

0 Answers0