2

I'm trying to have users of my app sign in by email + link, password-less. The client (written in React Native) asks for the sign-in link using the Javascript SDK like so, based on this answer:

  var actionCodeSettings = {
    // URL you want to redirect back to. The domain (www.example.com) for this
    // URL must be in the authorized domains list in the Firebase Console.
    url: `${BASE_URL}/verifyEmail?email=${email}`,
    // This must be true.
    handleCodeInApp: true,
    iOS: {
      bundleId: BUNDLE_ID
    },
    android: {
      packageName: BUNDLE_ID,
      installApp: true,
      minimumVersion: '12'
    },
    dynamicLinkDomain: 'myapp.page.link'
  };
  await firebase.auth().sendSignInLinkToEmail(email, actionCodeSettings)
}

Then the user opens the email and follows the sign-in link. Ideally it deep links back to the app, but in case that doesn't happen (e.g. they do it on a different device), I still need it to verify their email address on my server, which is NodeJS. My /verifyEmail handler should do that. According to this answer, the Firebase admin SDK can't verify action codes anymore. It suggests using the Firebase admin REST API, which says to effectively do:

curl 'https://identitytoolkit.googleapis.com/v1/accounts:update?key=AIzaSyCrFbTvuObVGlmGv9MK2sZ9MyS-VCkN--8' \
-H 'Content-Type: application/json' --data-binary '{"oobCode":"9LRoycB4fFrycmyLreYnjcAzxsGnwIpd9ub5tkpX_EQAAAF2-d9GoQ"}'

But I'm getting INVALID_OOB_CODE as a result. To test, I copied the OOB code from the email link, without clicking it, directly into that cURL command.

What am I doing wrong? Looking at the Javascript client SDK's source, it seems that applyActionCode calls the same endpoint I'm calling here in the same way. I'm thinking maybe this method only works for verifying an email/password signup, but if so, how do I sign up someone without a password?

The only workaround I can think of is in /verifyEmail to hash the oobCode, truncate the hash down to a user-friendly verification code, store the verification link on the server side, and ask the user to input that code in the app in order to retrieve the verification link to be used with EmailAuthProvider.credentialWithLink. And reject if too much time passes. That's undesirable.

hot_gril
  • 89
  • 1
  • 3
  • https://github.com/firebase/firebase-admin-node/issues/46#issuecomment-414718648 Refer this answer if it helps – Darshan Jain Jan 14 '21 at 03:21
  • @DarshanJain That's for sending an email verification link from the server, not for verifying a clicked email link on the server. But, if I could customize the email link via that method, I could put my own token into it and verify it when the user clicks the link. Still a workaround but better than the one I described since it doesn't require the user to copy-paste anything. – hot_gril Jan 14 '21 at 03:49

1 Answers1

0

I ended up doing the workaround mentioned in the last paragraph of the post. It works, and the one downside is that the user has to copy the code. It's the oobCode SHA256 hashed and truncated to a 6-digit hexadecimal string. With my 5 minute expiration time, an attacker would have to make 16^6 / (5 * 60) = ~55924 guesses per second to crack it. Good enough for my app; it's not a high-stakes situation.

This really needs to be easier from Firebase. I should just be able to call their API on my backend to check the validity of the email verification code.

hot_gril
  • 89
  • 1
  • 3