I'm trying to impliment the code as found here: https://firebase.google.com/docs/auth/web/manage-users#send_a_password_reset_email
var auth = firebase.auth();
var emailAddress = "[email protected]";
auth.sendPasswordResetEmail(emailAddress).then(function() {
// Email sent.
}).catch(function(error) {
// An error happened.
});
But I can't find the sendPasswordResetEmail
method in firebase admin.
Anyway I can do this on the backend?
ORIGINAL JULY 2018 ANSWER:
The sendPasswordResetEmail()
method is a method from the client-side auth
module, and you're right, the Admin-SDK doesn't have it - or anything comparable. Most folks call this function from the front-end...
With that said, it is possible to accomplish on the backend... but you'll have to create your own functionality. I've done this sort of thing before, I'll paste some of my code from my cloud functions to help you along... should you choose to go down this road. I create my own JWT, append it to a URL, and then use NodeMailer to send them an email with that link... when they visit that link (a password reset page) they enter their new password, and then when they click the submit button I pull the JWT out of the URL and pass it to my 2nd cloud function, which validates it and then resets their password.
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);
});
});
});
JANUARY 2019 EDIT:
The Admin SDK now has some methods that allow you to generate a "password reset link" that will direct people to the built-in Firebase password reset page. This isn't exactly the solution OP was looking for, but it's close. You will still have to build and send the email, as my original answer shows, but you don't have to do everything else... i.e.: generate a JWT, build a page in your app to handle the JWT, and another backend function to actually reset the password.
Check out the docs on the email action links, specifically the "Generate password reset email link" section.
// Admin SDK API to generate the password reset link.
const email = '[email protected]';
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.
});
Full disclosure - I haven't actually used any of those functions, and I'm a little concerned that the page in question refers a lot to mobile apps - so you might have to pass it the mobile app config.
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'
};
On the other hand, the page also says these features provide the ability to:
Ability to customize how the link is to be opened, through a mobile app or a browser, and how to pass additional state information, etc.
Which sounds promising, allowing it to open in the browser... but if you are developing for web - and the function errors out when not provided iOS/Android information... then I'm afraid you'll have to do it the old fashioned approach and create your own implementation... but I'm leaning towards this .generatePasswordResetLink
should work now.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With