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:
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;
}
}
}
}
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()
andexists()
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()
andexists()
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)