0
votes

I am very new to Firebase and custom claims, so I hope someone can help.

I have an app that uses Firebase Auth as its authentication system. The app is built using Flutter. It is a subscription-based app, so the users must have an 'active' subscription status, in order to be logged in. The subscription information for each user, including the status, is stored in a Firestore collection called 'subscriptions'. Each user is assigned a document with their uid as its name when they create a subscription.

The way my app works is that a user will create an account on the WordPress website, using Firebase authentication. At this point, their user is created in Firebase Auth, but they have no assigned document in the 'subscriptions' Firestore collection. This is only created once they purchase a subscription with WooCommerce Subscriptions. I basically need them to be prevented from logging in if:

  • There is no 'subscriptions' Firestore document created for them (i.e. they have created an account but not purchased a subscription yet).
  • Their subscription status is not 'active', once they have purchased the subscription and the document has been created for them.

As far as I know, the best way to allow users to log in only if they have an active subscription, is to use custom claims. I've built the following two custom claims functions in javascript with VS Code, which are designed to check the subscriptions collection in Firestore for the user's subscription status, both when the document is created and if it is updated:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

// Claim when a user is created
exports.createUser = functions.firestore
    .document("subscriptions/{subscription}")
    .onCreate((snap, context) => {
      // Get an object representing the document
      const newValue = snap.data();

      // Access a particular field
      const userId = newValue.firebaseUid;
      console.log("New subscription ${UserId} created!");
      const status = newValue.status;

      // Assign claims to the user
      return admin.auth().setCustomUserClaims(userId, {status});
    });

// Claim when a user is updated
exports.updateUser = functions.firestore
    .document("subscriptions/{subscription}")
    .onUpdate((change, context) => {
      // Get an object representing the document
      const updatedValue = change.after.data();

      // Access a particular field
      const userId = updatedValue.firebaseUid;
      console.log("User subscription status updated!");
      const status = updatedValue.status;

      // Assign claims to the user
      return admin.auth().setCustomUserClaims(userId, {status});
    });

For some reason, these aren't working. They did successfully upload to my project in the Firebase console. However, they aren't adding a custom claim to the standard auth claim. I was expecting to see 'status: active/pending/cancelled' attached at the end of the claims map. They also aren't seemingly able to handle the case where no document exists for a user (i.e. when the user has only just created an account but hasn't yet subscribed).

Can someone show me why these aren't working and what I am doing wrong? How can I ensure the subscription status is added to my users' auth claims, so that I can guarantee they can only log in if they have an active subscription? Thanks!

Do you have any logs? How does your User object look like on the client when you call getIdTokenResult on the current user? - b2m9
"They also aren't seemingly able to handle the case where no document exists for a user (i.e. when the user has only just created an account but hasn't yet subscribed)." - I don't understand. Your two Cloud Functions are triggered on Firestore operations; so your never custom claims aren't set until you add a document to your 'subscriptions' collection. If you want to set claims on account creation, you can trigger a function on functions.auth.user().onCreate(...) - b2m9
I see, so I would perhaps need to create a new cloud function, to essentially set the subscription status when the user account is created, like you have suggested? Then this custom claim would be updated by the other functions? I am wondering if I can simplify the claims by using onWrite instead of onCreate and onUpdate? - James
Yes, you can add a claim when the user is created and then update the claim based on whatever is in your subscription collection. onWrite would work, you just have to differentiate between create, update, and delete subscription to set the claim properly (if that's relevant for your use case). - b2m9