2

So i want to integrate a stripe subscription to my project. I'm using firebase 9 and next-auth(I login only with google provider for now.)Since google provider with next-auth return only 3 data(name, email and image), i add a callback to [..nextauth.ts] file to get more data like sub since i can't get the UID(like this ).

export const authOptions = {
  // Configure one or more authentication providers
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_ID!,
      clientSecret: process.env.GOOGLE_SECRET!,
    }),

    // ...add more providers here
  ],
  callbacks: {
    async session({ session, token, user }: any) {
      session.user.token = token;
      return session;
    },
  },

};

export default NextAuth(authOptions);

{

And it's ok, it worked..I got the token object(see below )

{
  user: {
    name: 'Lionel messi',
    email: 'random@gmail.com',
    image: 'https:/.googleusercontent.com/z/Arandom',
    token: {
      name: 'Lionel messi',
      email: 'random@gmail.com',
      picture: 'https:/.googleusercontent.com/z/Arandom',
      sub: '111random57437563475920',
      iat: "random",
      exp: "random",
      jti: 'c17388fc-dde4-4d9d-aadc-random'
    }
  }
}

But the sub data doesn’t seem to work with firebase/stripe security rules(I tried this ) I replaced the "uid" with "request.auth.token.sub" to get the sub from the token and get this error on the website each time i tried to subscribe : "There is no user record corresponding to the provided identifier."

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow write, read: if true
         allow write: if request.auth !=null && request.auth.uid == ""
    }
  
    match /customers/request.auth.token.sub {
      allow read: if request.auth.uid == request.auth.token.sub


      match /checkout_sessions/{id} {
        allow read, write: if request.auth.uid == request.auth.token.sub;
      }
      match /subscriptions/{id} {
        allow read: if request.auth.uid == request.auth.token.sub;
      }
      match /payments/{id} {
        allow read: if request.auth.uid == request.auth.token.sub;
      }
    }

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

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

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

Here is my loadCheckout function:

  const loadCheckout = async (priceId: string) => {
    // @ts-ignore
    const uid = session?.user?.token?.sub;
    console.log(uid, "user ID");
    const payRef = await addDoc(
      collection(db, `customers/${uid}/checkout_sessions`),
      {
        price: priceId,
        success_url: window.location.origin,
        cancel_url: window.location.origin,
      }
    );

And i called it like this : onClick={() => loadCheckout(productData?.prices?.priceId)}

I have to mention here that I am able to get the productData.prices.PriceId data(this is not the problem).

I'm sure that the error is somewhere in the security rules or the sub data is not taken properly..

Do you you guys have suggestions on how to fix this mess and go through the checkout ? or there is any other way to get the UID with a callback ?

Can i get the UID directly ? what is the proper way to get the sub in the rules ?

See above for more information

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
pion-dev
  • 21
  • 3

1 Answers1

0

I didn't test your entire code, but since you write to the

customers/${uid}/checkout_sessions collection with uid = session?.user?.token?.sub

I think that you need to adapt your security rule as follows, using a wildcard expression (e.g. {usersSub}) in order to make it available in rules as a variable

match /customers/{usersSub} { 

     match checkout_sessions/{id} { 
        allow read: if request.auth.token.sub == usersSub
        // ...
Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
  • Hi Renaud! Thank you for the suggestion , i just tried it but same result...Is usersSub a protect variable as UID ? I mean what’s inside ? – pion-dev Apr 21 '23 at 13:24
  • Hi, `usersSub`is just a simple string. Have a look at the [documentation](https://firebase.google.com/docs/firestore/security/rules-conditions#authentication) which explains how to use a wildcard expression. You can put whatever string you want as soon as it used in the path and in the rule – Renaud Tarnec Apr 21 '23 at 13:30
  • 1
    I've just realized that there was an error in my response! Can you try with the new rule. – Renaud Tarnec Apr 21 '23 at 13:31
  • Hmm Not working..I will take a look to the documentation you link above..Thanks you – pion-dev Apr 21 '23 at 14:54
  • I suggest to try with `rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /customers/{usersSub}/checkout_sessions/{id} {allow read: if request.auth.token.sub == usersSub;}}}` – Renaud Tarnec Apr 21 '23 at 15:30
  • I tried it but not working: This is the path i'm using in my code : collection(db, `customers/${usersSub}/checkout_sessions`), with usersSub = session.user.token.sub; And for the the security rules : – pion-dev Apr 21 '23 at 16:52
  • rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow write, read: if true allow write: if request.auth !=null && request.auth.token.sub == "" } match /customers/{usersSub} { allow read: if request.auth.token.sub == usersSub match /checkout_sessions/{id} { allow read, write: if request.auth.token.sub == usersSub; } ......... } – pion-dev Apr 21 '23 at 16:53