代码之家  ›  专栏  ›  技术社区  ›  Relm

Firebase:如何使用NodeJs向后端发送密码重置电子邮件

  •  1
  • Relm  · 技术社区  · 8 年前

    我试图实现下面的代码: https://firebase.google.com/docs/auth/web/manage-users#send_a_password_reset_email

    var auth = firebase.auth();
    var emailAddress = "user@example.com";
    
    auth.sendPasswordResetEmail(emailAddress).then(function() {
      // Email sent.
    }).catch(function(error) {
      // An error happened.
    });
    

    但我找不到 sendPasswordResetEmail 方法。

    不管怎样,我可以在后台做这个吗?

    3 回复  |  直到 8 年前
        1
  •  13
  •   JeremyW    7 年前

    2018年7月的原始答案:

    这个 sendPasswordResetEmail() 方法是来自客户端的方法 auth 模块,您是对的,Admin SDK没有它——或者任何类似的东西。大多数人从前端调用这个函数。。。

    话虽如此,它 可以在后端完成。。。但你必须创建自己的功能。我以前做过类似的事情,我会从我的云函数中粘贴一些代码来帮助你。。。如果你选择走这条路。我创建自己的JWT,将其附加到URL,然后使用NodeEmailr向他们发送带有该链接的电子邮件。。。当他们访问该链接时( 密码重置页面 )他们输入新密码,然后单击 提交 按钮,我将JWT从URL中拉出,并将其传递给我的第二个云函数,该函数将对其进行验证,然后重置其密码。

    const functions = require('firebase-functions');
    const admin = require('firebase-admin');
    var jwt = require('jsonwebtoken');
    
    admin.initializeApp()
    
    // Email functionality
    const nodemailer = require('nodemailer');
    
    // Pull the gmail login info out of the environment variables
    const gmailEmail = functions.config().gmail.email;
    const gmailPassword = functions.config().gmail.password;
    
    // Configure the nodemailer with our gmail info
    const mailTransport = nodemailer.createTransport({
      service: 'gmail',
      auth: {
        user: gmailEmail,
        pass: gmailPassword,
      },
    });
    
    
    
    // Called from any login page, from the Forgot Password popup
    // Accepts a user ID - finds that user in the database and gets the associated email
    // Sends an email to that address containing a link to reset their password
    exports.forgotPassword = functions.https.onRequest( (req, res) => {
    
      // Make a query to the database to get the /userBasicInfo table... 
      admin.database().ref(`userBasicInfo`).once('value').then( dataSnapshot => {
        let allUsers = dataSnapshot.val() ? dataSnapshot.val() : {};
        let matchingUid = '';
        let emailForUser = '';
    
        // Loop over all of the users
        Object.keys(allUsers).forEach( eachUid => {
          // See if their email matches
          allUsers[eachUid]['idFromSis'] = allUsers[eachUid]['idFromSis'] ? allUsers[eachUid]['idFromSis'] : '';
          if (allUsers[eachUid]['idFromSis'].toUpperCase() === req.body.userIdToFind.toUpperCase()) {
            // console.log(`Found matching user! Uid: ${eachUid} with idFromSis: ${allUsers[eachUid]['idFromSis']}... setting this as the matchingUid`);
            matchingUid = eachUid;
            emailForUser = allUsers[eachUid]['email'] ? allUsers[eachUid]['email'] : '';
          }
        })
    
        // After loop, see if we found the matching user, and make sure they have an email address
        if (matchingUid === '' || emailForUser == '') {
          // Nothing found, send a failure response
          res.send(false);
        } else {
          // Send an email to this email address containing the link to reset their password
    
          // We need to generate a token for this user - expires in 1 hour = 60 minutes = 3600 seconds
          jwt.sign({ uid: matchingUid }, functions.config().jwt.secret, { expiresIn: 60 * 60 }, (errorCreatingToken, tokenToSend) => {
    
            if (errorCreatingToken) {
              console.log('Error creating user token:');
              console.log(errorCreatingToken);
              let objToReplyWith = {
                message: 'Error creating token for email. Please contact an adminstrator.'
              }
              res.json(objToReplyWith);
            } else {
    
              // Send token to user in email
    
              // Initialize the mailOptions variable
              const mailOptions = {
                from: gmailEmail,
                to: emailForUser,
              };
              // Building Email message.
              mailOptions.subject = 'LMS Password Reset';
              mailOptions.text = `
    Dear ${req.body.userIdToFind.toUpperCase()},
    
    The <system> at <company> has received a "Forgot Password" request for your account.
    Please visit the following site to reset your password:
    https://project.firebaseapp.com/home/reset-password-by-token/${tokenToSend}
    
    If you have additional problems logging into LMS, please contact an adminstrator.
    
    Sincerely,
    <company>
              `;
              // Actually send the email, we need to reply with JSON
              mailTransport.sendMail(mailOptions).then( () => {
                // Successfully sent email
                let objToReplyWith = {
                  message: 'An email has been sent to your email address containing a link to reset your password.'
                }
                res.json(objToReplyWith);
              }).catch( err => {
                // Failed to send email
                console.log('There was an error while sending the email:');
                console.log(err);
                let objToReplyWith = {
                  message: 'Error sending password reset email. Please contact an adminstrator.'
                }
                res.json(objToReplyWith);
              });
    
            }
    
          })
    
        }
    
      }).catch( err => {
        console.log('Error finding all users in database:');
        console.log(err);
        res.send(false);
      })
    
    });
    
    
    
    // Called when the unauthenticated user tries to reset their password from the reset-password-by-token page
    // User received an email with a link to the reset-password-by-token/TOKEN-HERE page, with a valid token
    // We need to validate that token, and if valid - reset the password
    exports.forgotPasswordReset = functions.https.onRequest( (req, res) => {
    
      // Look at the accessToken provided in the request, and have JWT verify whether it's valid or not
      jwt.verify(req.body.accessToken, functions.config().jwt.secret, (errorDecodingToken, decodedToken) => {
    
        if (errorDecodingToken) {
          console.error('Error while verifying JWT token:');
          console.log(error);
          res.send(false);
        }
    
        // Token was valid, pull the UID out of the token for the user making this request
        let requestorUid = decodedToken.uid;
    
        admin.auth().updateUser(requestorUid, {
          password: req.body.newPassword
        }).then( userRecord => {
          // Successfully updated password
          let objToReplyWith = {
            message: 'Successfully reset password'
          }
          res.json(objToReplyWith);
        }).catch( error => {
          console.log("Error updating password for user:");
          console.log(error)
          res.send(false);
        });
    
      });
    
    });
    

    2019年1月编辑:

    Admin SDK现在有一些方法可以让你生成一个“密码重置链接”,将人们引导到内置的Firebase密码重置页面。这并不是OP想要的解决方案,但已经很接近了。正如我最初的回答所示,你仍然需要构建和发送电子邮件,但你不必做其他任何事情。。。i、 例如:生成一个JWT,在应用程序中构建一个页面来处理JWT,以及另一个后端函数来实际重置密码。

    查看网站上的文档 email action links ,特别是“ 生成密码重置电子邮件链接 “节。

    // Admin SDK API to generate the password reset link.
    const email = 'user@example.com';
    admin.auth().generatePasswordResetLink(email, actionCodeSettings)
        .then((link) => {
            // Do stuff with link here
            // Construct password reset email template, embed the link and send
            // using custom SMTP server
        })
        .catch((error) => {
            // Some error occurred.
        });
    

    完全公开——我实际上没有使用任何这些功能,而且我有点担心这个页面涉及很多移动应用——所以你 可以 必须通过移动应用程序配置。

    const actionCodeSettings = {
        // URL you want to redirect back to. The domain (www.example.com) for
        // this URL must be whitelisted in the Firebase Console.
        url: 'https://www.example.com/checkout?cartId=1234',
        // This must be true for email link sign-in.
        handleCodeInApp: true,
        iOS: {
            bundleId: 'com.example.ios'
        },
        android: {
            packageName: 'com.example.android',
            installApp: true,
            minimumVersion: '12'
        },
        // FDL custom domain.
        dynamicLinkDomain: 'coolapp.page.link'
    };
    

    另一方面,该页面还表示,这些功能提供了以下功能:

    能够通过手机定制如何打开链接 应用程序或浏览器,以及如何传递其他状态信息等。

    这听起来很有希望,允许它在浏览器中打开。。。但是,如果你是为网络开发的,并且在没有提供iOS/Android信息的情况下,功能会出错。。。那么我担心你将不得不用老式的方法来做,并创建自己的实现。。。但我倾向于这个 .generatePasswordResetLink 现在应该可以了。

        2
  •  0
  •   thisismydesign    6 年前

    与在用户对象上工作的其他客户端功能不同(例如。 sending a verification mail ), sending a password reset email 工作在 auth 模块,不需要登录用户。因此,您只需从后端使用客户端库(前提是您有用户的电子邮件地址):

    const firebase = require('firebase');
    const test_email = "test@test.com";
    const config = {} // TODO: fill
    
    const app = firebase.initializeApp(config);
    app.auth().sendPasswordResetEmail(test_email).then(() => {
      console.log('email sent!');
    }).catch(function(error) {
      // An error happened.
    });
    
        3
  •  0
  •   help-info.de Jesús Serrano    6 年前
    public  void resetpasswoord()
    {
    
        string emailaddress = resest_email.text;
        FirebaseAuth.DefaultInstance.SendPasswordResetEmailAsync(emailaddress).ContinueWith((task =>
        {
    
            if (task.IsCompleted)
            {
                Debug.Log("Email sent.");
    
            }
            if (task.IsFaulted)
            {
                Firebase.FirebaseException e =
               task.Exception.Flatten().InnerExceptions[0] as Firebase.FirebaseException;
    
                GetErrorMessage((AuthError)e.ErrorCode);
                errorpanal = true;
                return;
            }
    
        }));
    
    }
      void GetErrorMessage(AuthError errorcode)
    {
        string msg = "";
        msg = errorcode.ToString();
    
        print(msg);
        errorpanal = true;
        ErrorText.text = msg;
    }
    
    推荐文章