2

I have some simple rules set up to check if a user can create or delete a certain profile document.

service cloud.firestore {
  match /databases/{database}/documents {
    function isSignedIn() {
      return request.auth != null
    }

    function isOwner(userId) {
      return request.auth.uid == userId
    }

    function isVerified() {
      return request.auth.token.email_verified
    }

    match /profiles/{profileId} {
      allow read;
      allow create: if isSignedIn()
                    && isVerified();
      allow update,
            delete: if isSignedIn()
                    && isVerified()
                    && isOwner(request.resource.data.userId);
    }
  }
}

The check for isVerified() in the update part of the profiles collection fails...even when the logged in user has a verified email address set.

Doing the following from within my app results in a security rules error:

profilesCollection.doc(currentUser.uid).update({
  name: 'Michael'
})

If I remove the isVerified() check on update it works. However, I definitely want a user to have a verified email address to allow this.

If I check the status of auth().currentUser.emailVerified it returns true. Only by logging out and back in again does it actually pass the security rule check. It's almost as if the frontend JavaScript app's verified status is not agreeing with what the Firestore server sees.

UPDATE:

This all happens immediately after a user creates an account:

async signup() {
  const createUser = await auth.createUserWithEmailAndPassword(this.signup.email, this.signup.password)
  await createUser.user.sendEmailVerification()
  this.complete = true
}

After signing up a user will check their email and follow the verification link.

Coming back to the app (and still signed in), emailVerified is false. auth.onAuthStateChanged() does NOT get triggered.

A "hard refresh" of the page shows that emailVerified is now set to true. Trying to create a profile document fails though...due to the isVerified() check, in the security rules.

Only by physically signing out and then in again can a user create documents, which is a little absurd.

So essentially, signing up and verifying your email does not update the status of the users email verification and even after a hard refresh still does not pass the security rules.

I can't understand why it works like this, if it's a bug...or something else?

Any help greatly appreciated!

Michael Giovanni Pumo
  • 14,338
  • 18
  • 91
  • 140
  • 1
    Can you show how you verified that the user's email address is set as verified? Also: when was it verified? Note that it may take up to an hour for the user's ID token to reflect that fact that they verified. See https://stackoverflow.com/q/48530554 and https://stackoverflow.com/a/41875733 – Frank van Puffelen Mar 20 '20 at 23:10
  • 1
    Cloud Firestore Security Rules is only for the Android, iOS, and Web client libraries. Not for server side(cloud functions using firebase admin SDK). So I think that the user's email address is not verified. See https://firebase.google.com/docs/firestore/security/overview and https://stackoverflow.com/questions/60191098/firestore-database-rules-create-rule-not-working-for-cloud-functions/60191536#60191536 – zkohi Mar 21 '20 at 11:02
  • Thanks @FrankvanPuffelen - If I sign up as a user, then verify my email from the email sent to me...I refresh the app and see that firebase.auth user has emailVerified set to true. Despite that, making requests to the app to update 'profiles' collection results in a permission error. If I log out and log back in again, it seems to work. What gives? – Michael Giovanni Pumo Mar 21 '20 at 17:30
  • Hmmm.... if the client shows the email as verified, it should also be sending that to the server for database operations (as both come directly from the ID token). Can you show in code how you verified it client-side, just so that we can double check it (as I'm not sure what else might be causing this)? – Frank van Puffelen Mar 21 '20 at 18:03
  • @FrankvanPuffelen I am just checking the console from the result of fb.auth.onAuthStateChanged. Is that what you mean? Is there any way I can check on the client if the ID token is being correctly sent? – Michael Giovanni Pumo Mar 21 '20 at 22:53
  • You said "I refresh the app and see that firebase.auth user has emailVerified set to true". Is that something you can show in code? I don't think you've done something wrong there, but given that I'm not sure what might be going on, I'd rather see it tested/logged in code. – Frank van Puffelen Mar 22 '20 at 02:10
  • Hi @FrankvanPuffelen when I said refresh, I meant even after a literal refresh of the browser whilst I'm still logged in. Once I do that I can see in the console that it knows I have a verified email address...yet the rules error. Logging out and back in though does work...so is it cached somehow? I know it's not very SO but I could invite you to the repo if you want to see and if it gets solved I will post the answer here? Thank you! – Michael Giovanni Pumo Mar 22 '20 at 16:51
  • Hi @FrankvanPuffelen, I updated my question with more clarity. Hoping you could help? There’s definitely still an issue with how Firebase checks a valid email in security rules vs how it does on the frontend. Almost like the front and back are out of sync. – Michael Giovanni Pumo Apr 27 '20 at 07:42
  • Without seeing the code that I asked for [here](https://stackoverflow.com/questions/60782267/custom-check-for-verified-email-address-is-failing-in-firestore-security-rule?noredirect=1#comment107562342_60782267) I can only guess. My best guess is that your client-side token hasn't been refreshed yet, which may take up to an hour. But your client-side code would in that case also not see the user as verified. – Frank van Puffelen Apr 27 '20 at 15:11
  • Hello @FrankvanPuffelen...I have updated my question with some code that I'm using to sign the user up. It's after signing up and verifying the email that a user is set to verify but it still does not pass the security rules. I can't understand why. – Michael Giovanni Pumo Apr 27 '20 at 17:14
  • 1
    Before making the database request, have you tried force refreshing the user's ID token? `firebase.auth().currentUser.getIdToken(true).then(()=>firebase.database().ref('profile/someId').update({ /* new data * / })).catch(console.error)`. This is what Frank was referring to by the local authentication ID token taking up to an hour to be updated because the client won't get a new token until the old one expires (even though the server's emailVerified will be updated properly). – samthecodingman Apr 27 '20 at 22:08
  • @samthecodingman Thanks I will give that a try but is your example Firebase DB? I'm using Firestore DB. Not sure it makes a difference? – Michael Giovanni Pumo Apr 28 '20 at 10:17
  • 1
    Ah whoops. However the same process applies. Force refresh the token once you've validated the email and then make any changes. – samthecodingman Apr 28 '20 at 21:54
  • @FrankvanPuffelen just FYI re: your comment on the answer: voting to close yielded error "can't close question with an open bounty." Also, attempted to tag MartijnPieters here, but system didn't auto-suggestion, so not sure if that worked. I've noted the comments there, but just wanted you to know that it's confusing to not be able to close bountied-yet-duplicate questions. – ultraGentle May 07 '20 at 15:17

0 Answers0