firebase / firebase-admin-node

Firebase Admin Node.js SDK
https://firebase.google.com/docs/admin/setup
Apache License 2.0
1.63k stars 371 forks source link

Feature Request: Cloud Function should support user.sendEmailVerification() like client sdk #46

Open inlined opened 7 years ago

inlined commented 7 years ago

[Refiling for user @sun3 from https://github.com/firebase/functions-samples/issues/181]

Feature Request:

The user.sendEmailVerification() needs to be supported by Firebase Cloud Functions. This is available in client side but not server side. When using Cloud Functions to create new users we also need to automatically send Email Verification before new user can use our apps. This is currently as stopping block for our apps.

At this point the user.sendEmailVerification() gives error that function is not found.

    admin.auth().createUser({
        email: emailAddress,
        emailVerified: false,
        password: password,
        displayName: '', //name,
        disabled: false
    })
        .then(function (user) {
            // A error representation of the newly created user is returned
            console.log("Created Firebase User successfully with id: ", user.uid);
            console.log("user.emailVerified:", user.emailVerified);

            // Send Email Verification
            user.sendEmailVerification()
                .then(function (emailSent) {
                    console.log('emailSent ', emailSent);
                })
                .catch(function (error) {
                    console.log('emailSent error ', error);
                });

    // ... Additional code below

Thank you and I am open to any suggestions.

stehag commented 3 years ago

Not having this feature after so many years makes one wonder what is happening, the developer experience is really bad, of course this should have been implemented at the same time as it was possible to create users with the Admin SDK.

Ranguna commented 3 years ago

@stehag I'm not sure we should say that the developer experience is "really bad" just because of a few missing features here and there. But yeah, this one is a real must for a lot of things.

inlined commented 3 years ago

I've re-escalated this internally. Has anyone who has been waiting all this time tried just using the client SDK from the server side? I would expect that you could create a custom token in the admin SDK, pass it to the client SDK, and then call the appropriate method. I get that this is a kludge, but it's better than waiting on a complete backend API rewrite to support this edge case (sadly that's what would be required).

myspivey commented 3 years ago

I can confirm that works as it is what I did for the project I was on. It was very "hacky" and did not look good and only worked for custom auth flows not SSO. This was for a project where admins had to create users as self signup was not allowed. Once they had their custom signon, at that point they could switch to SSO.

dungahk commented 3 years ago

A few years ago I did exactly that, I guess that still works but I can confirm it used to work around 3 years ago. (Using the client SDK)

Ranguna commented 3 years ago

@inlined

Has anyone who has been waiting all this time tried just using the client SDK from the server side?

Isn't the client sdk rate limited for client use ?

inlined commented 3 years ago

Rate limiting shouldn't be a problem AFAICT.

Ranguna commented 3 years ago

So you are saying that a public facing lib is not rate limited by IP ? Doesn't sound very realistic, I'd search the docs but they usually aren't very explicit about these things.

inlined commented 3 years ago

I see a ratelimit error in their service definition, but don't know (and probably shouldn't say) what the limit is. Generally even IP ratelimts are set to "reasonable" levels because ISPs in some countries NAT massive numbers of clients to the same IPv4. Either way, the rate limit is guaranteed to be greater than 0, so this is better than waiting for an architecture rewrite.

Ranguna commented 3 years ago

@inlined , in that case, this should be a better solution https://github.com/firebase/firebase-admin-node/issues/46#issuecomment-625026299, since it shouldn't be rate limited the same way a public facing lib is. Unless they are using the same underlying endpoints.

crossan007 commented 3 years ago

So, I have https://github.com/firebase/firebase-admin-node/issues/46#issuecomment-625026299 working in local emulators when I manually specify the REST API key (const apikey = functions.config().project.apikey;).

Is there a way in a production environment to get the REST API key? or will I need to manually add that to the functions config via firebase functions:config:set?

inlined commented 3 years ago

The Firebase Config does not include an API key since the Firebase Config is for backend development and the API key is ostensibly to label a client. You can inject it with functions:config:set though.

MrVentzi commented 3 years ago

+1, we want to see this!

Migaelh commented 3 years ago

I found a method on the Firebase Auth documentation where you can generate a email verification link, email password link or email link for sign-in.

You can also pass a redirect URL (and other info) where you can handle any callbacks once the use clicks on the link.

You just need to send the email yourself.

This is what I have done:


let displayName = 'John Doe'
let email = 'to@mail.com'
//Generate the email verification link
let emailVerificationLink = await admin.auth().generateEmailVerificationLink(email, { url: `SOME_REDIRECT_URL?with=params` })
let mail = JSON.parse(process.env.FIREBASE_CONFIG).mail

//construct the email 
const mailTransport = nodemailer.createTransport({
    service: 'gmail',
    auth: {
      user: mail.email,
      pass: mail.password,
    },
  })

const mailOptions = {
    from: `"${APP_NAME}" <${mail.email}>`,
    to: email,
    subject: `Email verification for ${APP_NAME}`,
    text: `Hello ${displayName})}

        Please follow this link to verify your email address for the ${APP_NAME}
        ${emailVerificationLink}

        Thanks
        Your ${APP_NAME} team
    `,
    html: `
        <p>Hello ${displayName}</p>
        <p>Please follow this link to verify your email address for the ${APP_NAME}</p>
        <p><a href='${emailVerificationLink}'>Verify Email</a></p>
        <p>Thanks</p>
        <p>Your ${APP_NAME} team</p>
    `
  };

  try {
  //send the email
    await mailTransport.sendMail(mailOptions);
  } catch(error) {
    console.error('send email error', error) 
  }

I am using the NodeMailer package for sending the email

samasthwafer commented 8 months ago

7 years later and no feature still?

digimbyte commented 8 months ago

Possible solution is to install the firebase client and initiate the auth with a custom token from the admin sdk and then invoke an email request on that user instance

MadhavKanna commented 3 months ago

I really do need this, guess I'll just have to implement the workaround. Any updates on when the solution should come out?

digimbyte commented 3 months ago

I really do need this, guess I'll just have to implement the workaround. Any updates on when the solution should come out?

something like this can be done: (doesn't need the client sdk)

const admin = require('firebase-admin');
const axios = require('axios');

admin.initializeApp({
  credential: admin.credential.applicationDefault(),
});

const generateCustomToken = async (uid) => {
  try {
    const customToken = await admin.auth().createCustomToken(uid);
    console.log('Custom Token:', customToken);
    return customToken;
  } catch (error) {
    console.error('Error creating custom token:', error);
  }
};

const authenticateWithCustomToken = async (customToken) => {
  const url = `https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=${process.env.FIREBASE_API_KEY}`;
  try {
    const response = await axios.post(url, {
      token: customToken,
      returnSecureToken: true,
    });
    console.log('ID Token:', response.data.idToken);
    return response.data.idToken;
  } catch (error) {
    console.error('Error authenticating with custom token:', error);
  }
};

const sendVerificationEmail = async (idToken) => {
  const url = `https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=${process.env.FIREBASE_API_KEY}`;
  try {
    const response = await axios.post(url, {
      requestType: 'VERIFY_EMAIL',
      idToken: idToken,
    });
    console.log('Verification email sent:', response.data);
  } catch (error) {
    console.error('Error sending verification email:', error);
  }
};

// Replace with your user's UID
const uid = 'your-user-uid';

generateCustomToken(uid)
  .then(customToken => authenticateWithCustomToken(customToken))
  .then(idToken => sendVerificationEmail(idToken));