40

Im trying to create a Cloud Function trigger that will execute after email has been verified.

In the Cloud Functions samples I could only find examples on triggers for onCreate and onDelete.

Within the documentation I found something about creating custom action handlers but I don't actually want to replace the standard email verification dialog they have by default, I just want to change the property of a "user" after the email is verified.

Does anyone have any experience with this, and is this even possible? Or is my only option to create my custom verification view/dialog webpage?

Frank van Puffelen
  • 418,229
  • 62
  • 649
  • 645
deloki
  • 1,603
  • 2
  • 18
  • 26
  • 14
    Unfortunately you can't have a Cloud Function trigger on email verification. The team is looking into making this possible, as it's been requested a few times. Please also cast your vote with a feature request: https://firebase.google.com/support/contact/bugs-features/ – Doug Stevenson Apr 19 '17 at 23:15
  • There is a functions-samples script for deleting unused accounts. You could tweak that to go over the accounts every certain period of time and check all verified emails: https://github.com/firebase/functions-samples/tree/master/delete-unused-accounts-cron Perhaps this is helpful. – bojeil Apr 20 '17 at 02:42
  • @DougStevenson is there some official way to vote on specific feature requests? Similar to Google Issue tracker where people can star features [like this one](https://issuetracker.google.com/issues/35827350). – Pat Needham Jul 23 '18 at 16:30
  • 1
    @DougStevenson has this feature progressed since 3 years ago? Looking for something like this at the moment and stumbled upon your comment here. – Vlad Jan 09 '20 at 21:37

3 Answers3

12

I faced this problem and took me a long time to figure it out how to solve so I hope this could help anyone that could get stuck into this too:

1 -> I created a function that was triggered with onCreate() for a new user

exports.sendConfirmationEmail = functions.auth.user()
                    .onCreate((user) => {
                        const actionCodeSettings = {
                            url: 'https://appNextURL.com/',
                            handleCodeInApp: false//ensure that the link will open into browser
                        };
                        return admin.auth().generateEmailVerificationLink(user.email, actionCodeSettings)
                            .then(async (link) => {
                                await db.collection('users').doc(user.uid).set({
                                    verificationLink: link,
                                    emailVerified: false
                                }, {merge: true});
                                return sendCustomVerificationEmail(user.email, user.displayName, link);
                            })
                            .catch((err) => {
                                console.error("Error:", err);
                                return Promise.reject(err);
                            });
                    });
  • The generateEmailVErificationLink() will generate the link based on the link we will save on step 3.

  • The function sendCustomVerificationEmail() is just an internal function that overcomes the standard email firebase send

2 -> Then I created a function that will receive a manual http trigger with the data that would be generated automatically by firebase when sending an automatic email

exports.verifyEmail = functions.https.onRequest((req, res) => {
                        const {mode, oobCode, apiKey, continueUrl, lang} = req.query;
                        const link = "https://us-central1-projectId.cloudfunctions.net/verifyEmail/?mode=" + encodeURIComponent(mode) + "&oobCode=" + encodeURIComponent(oobCode) + "&apiKey=" + encodeURIComponent(apiKey) + "&continueUrl=" + encodeURIComponent(continueUrl) + "&lang=" + encodeURIComponent(lang);
                        return db.collection("users")
                            .where("verificationLink", "==", link)
                            .get()
                            .then(function (querySnapshot) {
                                querySnapshot.forEach(function (user) {
                                    const userData: UserData = user.data();
                                    console.log("email verified: ", userData.userId);
                                    return admin.auth().updateUser(userData.userId, {
                                        emailVerified: true
                                    }).then(function (userRecord) {
                                        return db.collection('users').doc(userData.userId).set({emailVerified: true}, {merge: true});
                                    });
                                });
                                return res.sendStatus(200).end();
                            }).catch(function (err) {
                                console.log("error:", err);
                                return res.sendStatus(403).end();
                            });
                    });
  • As I saved the link in the onCreate() I can now query that link to get who is the user that I am authenticating

3 -> the third step is to change the link in to Firebase Authentication template to the link generated into the 2nd step:

Navigate to Authentication>Templates:

  • Click on edit icon> Click on customize action URL:

  • Navigation

  • Paste the link generated into the step 2 and save:

  • Save link

Now every link generated automatically will go trought that function you created on step 2 and you will be able to handle the actions you want to happen.

I hope I could be clear.

Rodrigo S
  • 193
  • 1
  • 7
  • 1
    I was looking at your answer and wondered how your sendCustomVerificationEmail looks like? Cheers! – Vlad Jan 07 '20 at 12:22
  • 2
    But then if the user want to resend the url, in which case a new url gets generated, then you'll have a different url than the one you saved in onCreate. The query won't work in this case. – lzl Apr 20 '20 at 19:32
1

you could still check for the verification status (at least) on Android with interface UserInfo method isEmailVerified(); eg. in order to send another verification email upon successful login, in case the current user has not yet verified the email address - and show the login screen again. one could as well HTTP trigger a cloud function or update values in the Firebase directly, through the client library. this might also apply to other platform clients, where one can check for the verification status.

this would not be exactly the event when the email just had been verified, but upon each single login attempt one knows the verification status and this value might be merely relevant on the client-side.

Martin Zeitler
  • 49,224
  • 12
  • 97
  • 156
-1

Create a publish button so your users trigger your cloud function

Instead of firing the cloud function immediately upon auth.emailVerified, I'm giving my users a 'Publish Profile' button which fires an http cloud function (passing in user.uid). This function looks up the user auth using the passed in user.uid

if user.uid && auth.emailVerified

write auth.emailVerified to each user.post

By default, post document "post.emailVerified" fields start out false, and cannot be written to except via adminFirestore in a cloud function.

Corey Bo
  • 105
  • 1
  • 6
  • I don't think burdening the user with a worst experience is a proper solution to a technical challenge. – BBish937 Apr 16 '21 at 01:15