How to use the sendPasswordResetEmail() function on the server using the firebase-admin SDK?

In the past, I have used firebase.auth in the web client and once a user creates another user, I link certain security logic:

That has worked well for me so far, but now for many reasons I need to move that logic to my server, for that I'm developing a backend with cloud functions and I'm using the Node.js Firebase Admin SDK version 6.4.0, but I can not find a way to use the functions of user.sendEmailVerification() and sendPasswordResetEmail() to implement the same logic on the server, the closest thing I found was:

But it only generates a link for each one, which by the way the only emailVerification() serves me, the one from generatePasswordReset always tells me:

Try resetting your password again

Your request to reset your password has expired or the link has already been used.

Even though be a new link, and it has not been used.

My 3 questions would be:

  1. How can I make the sendEmailVerification () and sendPasswordResetEmail () functions work on the server?
  2. How can I make the link generated with auth.generatePasswordResetLink (email) work correctly on the server?
  3. Is there any way to use templates and emails on the server that are in firebase auth?

Thank you in advance for sharing your experience with me, with all the programmers' community of stack overflow.

Upvotes: 1

Views: 2962

Answers (3)

KomaCola
KomaCola

Reputation: 1

I'm sure it doesn't matter anymore, but I had a headache doing this so I'd like to share even if it isn't the greatest answer.

await admin.auth().createUser(
            {email, password, displayName, phoneNumber, photoURL}
        ).then(function(userRecord) {
            admin.auth().createCustomToken(userRecord.uid).then(function(customToken){
                createdToken=customToken; 
                firebase.auth().signInWithCustomToken(createdToken).catch(function(error){
                    return console.log(error)
                })
                firebase.auth().onAuthStateChanged(function(user) {
                    user.sendEmailVerification().then(function(){
                        return console.log('It worked')
                    },function(error) {
                        return console.log(error)
                    })
                  });
            })
        })

Upvotes: 0

Ruben
Ruben

Reputation: 9186

The is a workaround provided here https://github.com/firebase/firebase-admin-node/issues/46

I found a work-around that works well enough for my use case, see below. I'm not sure if this is best practice, but I wanted to keep the emails exactly the same between the server and client requests. Would love to hear about any flaws with this implementation 💡



As suggested above, it uses a three step process to do this:

Acquire a custom token via the admin sdk's createCustomToken(uid)
It converts this custom token to an idToken via the API
It invokes the send email verification endpoint on the API
const functions = require('firebase-functions');
const fetch = require('node-fetch');
const admin = require('firebase-admin');

const apikey = functions.config().project.apikey;
const exchangeCustomTokenEndpoint = `https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${apikey}`;
const sendEmailVerificationEndpoint = `https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=${apikey}`;

module.exports = functions.auth.user().onCreate(async (user) => {
  if (!user.emailVerified) {
    try {
      const customToken = await admin.auth().createCustomToken(user.uid);

      const { idToken } = await fetch(exchangeCustomTokenEndpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          token: customToken,
          returnSecureToken: true,
        }),
      }).then((res) => res.json());

      const response = await fetch(sendEmailVerificationEndpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          requestType: 'VERIFY_EMAIL',
          idToken: idToken,
        }),
      }).then((res) => res.json());

      // eslint-disable-next-line no-console
      console.log(`Sent email verification to ${response.email}`);
    } catch (error) {
      // eslint-disable-next-line no-console
      console.log(error);
    }
  }
});

Upvotes: 0

Hiranya Jayathilaka
Hiranya Jayathilaka

Reputation: 7438

  1. Those functions are not available in firebase-admin, but you should be able to run the client-side SDK (firebase) on the server as well. Not exactly a best practice, but it will get the job done. There's a long standing open feature request to support this functionality in the Admin SDK. You will find some helpful tips and workarounds there.
  2. Could be a bug. I would consider reporting it along with a complete and minimal repro. The Admin SDK does have an integration test case for this use case, but it works slightly differently.
  3. Not at the moment. Hopefully, this will be covered when the above feature request is eventually fulfilled.

Upvotes: 3

Related Questions