I have implemented email verification in my app but i've noticed to email providers have bots that open the link before the user gets to see (thus verifying the email).
is there a way to prevent such behavior? or email verification by code or have action on the screen the user would need to click to verify?
Rather than providing a direct link to Firebase (the default), you can customize your email verification template to direct the user to another location, such as your application, where they must press a button to complete the verification process. Then you can use the Auth.applyActionCode() method with the oobCode that was supplied in the query parameters.
You'll have to process the verifications yourself if you wish to customize it. You can try it by generating email verification link using Admin SDK in a Cloud Function/Server
// Admin SDK API to generate the email verification link.
const useremail = 'user#example.com';
admin
.auth()
.generateEmailVerificationLink(useremail, actionCodeSettings)
.then((link) => {
// Construct email verification template, embed the link and send
// using custom SMTP server.
return sendCustomVerificationEmail(useremail, displayName, link);
})
.catch((error) => {
// Some error occurred.
});
Now that you've sent the email, you'll have to self-host a page to that opens after opening that link.
You can find detailed explanation in the documentation
That being said, you can implement reCaptcha or any verification service you want to use to make sure the user is not a bot and verify it in your custom handler.
Related
I have a Flutter mobile application and I am using Firebase authentication. I have decided to use email template for verifying user email. In the backend, I call generateEmailVerificationLink(email ,actionCode) to create an email verification link, then I pass the link to a good looking email template and send it to the user.
I would like to do the same thing for updating user email. But I am not sure which function to call in the backend to create the proper link that I need to pass to the email temple. The mode in the action code should be "verifyAndChangeEmail"
Does any one know?
I found this link https://firebase.google.com/docs/auth/admin/email-action-links
but it does not say how to generate a link for updating user email. Does that mean that I can't have a custom email for updating user email??
Please help.
You can use the same generateEmailVerificationLink() method, and, in the page opened via the link, you need to trigger, in the back-end, the updateUser() method of the Admin SDK.
More concretely:
In a Cloud Function (or a server you own), generate an email verification link for the new email (as explained in the doc you mention in your question) and build and send an email containing this link (for example by calling a microservice like Sendgrid).
The user receives the email in his mailbox. When he clicks on the link, the user is redirected to a web page you host somewhere (for example with Firebase hosting): the email is then verified, with applyActionCode(), as shown in the code found in this page in the Firebase doc (see "4. Handle email address verification by calling applyActionCode.").
Then, in the .then((resp) => {...}) block of this page, implement a call to a callable Cloud Function in which you use the updateUser() method to update the user's email. In the callable cloud function you must check that the uid (User ID) that you pass to the updateUser() method is the uid of the caller (with const uid = context.auth.uid; see the doc).
I'm using firebase auth in my app and i'm setting up password-less email sign up.
I have managed to set the email from my own domain but how do i change the text sent in the email for magic link?
I can see the configuration for the other template emails but not this one.
The email in question is this one:
Hello,
We received a request to sign in to teamname using this email address. If you want to sign in with your youremail account, click this link: link
If you did not request this link, you can safely ignore this email.
Thanks,
There is no way to edit any of the email templates that Firebase Authentication uses. The reason for this is that this allows bad actors to use Firebase to spam people, which would put the service at risk.
To control what message gets sent, you'll have to send it yourself and handle the verification flow with a custom email action handler. See How to modify Email Confirmation message - Firebase.
You could also take full control of the verification flow, and then use the Admin SDK to set the emailVerified flag of the user's profile.
Following on from Frank's answer, you will have to create your own email action link using the Firebase Admin SDK and then put that link in your custom email which you will then send using whatever service (Sendgrid, Mailgun, etc.).
See how to create the action link here: Generating Email Action Links
the only way to customize your email body is to install firebase extension called Trigger Email but it'll put you on the Blaze plan because it makes requests to third-party APIs, and as they specified on the extension's page you'll only be charged for usage that exceeds Firebase's free tier.
There are two ways to customize the email body when using the Firebase authentication service. However, the drawback is that you will have to create a backend to send the email yourself rather than just using a function from the SDK that handles everything automatically.
Generate email action links:
This is the most efficient method, it requires that you use the Firebase Admin SDK to generate a link that will be embedded in emails sent to your users.
Here is an example code that shows how to create an API to send customized verification emails using Express as the backend:
// Creating a POST route that sends customized verification emails
app.post('/send-custom-verification-email', async (req, res) => {
const {userEmail, redirectUrl} = req.body
const actionCodeSettings = {
url: redirectUrl
}
try{
// generate action like with the Firebase Admin SDK
const actionLink = await getAuth()
.generateEmailVerificationLink(userEmail, actionCodeSettings)
// embedding action link to customized verification email template
const template = await ejs.renderFile('views/verify-email.ejs', {
actionLink
})
// send verification email using SendGrid, Nodemailer, etc.
await sendVerificationEmail(userEmail, template, actionLink)
res.status(200).json({message:'Email successfully sent'})
}catch(error){
// handle errors
}
})
You can read more about it in the article I wrote here
Take full control over the workflow:
With this, the Firebase Admin SDK won't be used to generate an action link, but rather, you will generate the link yourself and create an API that uses the Admin SDK to handle the action to be taken whenever the link is clicked.
To do this you will have to create two API routes. One is a route that sends the emails and the other is a route that uses the Firebase Admin SDK to handle the action to be taken when the link attached to the email is clicked.
When the user wants to update the email, I'm using verifyBeforeUpdateEmail to verify before updating it.
await FirebaseAuth.instance.currentUser.verifyBeforeUpdateEmail(
email,
ActionCodeSettings(
androidInstallApp: true,
androidPackageName: 'com.example',
iOSBundleId: 'com.example',
handleCodeInApp: true,
url: 'https://example.page.link/emailVerify',
));
Can I let the user know if the email has been verified and updated? I've tried dynamic_links but it does not pick up the email verification.
The verifyBeforeUpdateEmail() method sends a verification email to a new email address. So the user needs to execute an action in order to verify the email (click on the link provided in the email). So normally the user will know he/she has verified the new email.
If you want to add an extra mechanism to "let the user know if the email has been verified and updated" you could adopt one of the following approaches:
#1 Implement a custom email action handler
As explained in the doc, "by default, user management emails link to the default action handler, which is a web page hosted at a URL in your project's Firebase Hosting domain. You can instead create and host a custom email action handler to do custom processing and to integrate the email action handler with your website".
This way, you could implement any business logic in parallel of the email verification, like sending a confirmation email or updating a flag, etc..
#2 Use a Cloud Function
There is no Authentication Cloud Functions trigger in response to the verification of an email, unfortunately. We can only trigger a Cloud Function upon the creation and deletion of Firebase user accounts. But you could implement a scheduled Cloud Function which checks if the email has been verified.
Firebase provides email templates for most user email such as password reset but doesn't provide one for the email sent via the sendSignInLinkToEmail method.
It is possible to either customise the content of this email or even better simply get generated link and then use your own email delivery system to sent the email?
You can't modify the built-in templates, however you can make use of the Admin SDKs to generate the action links.
This process is documented here.
const destEmail = 'user#example.com';
admin.auth().generateSignInWithEmailLink(destEmail, actionCodeSettings)
.then((link) => {
// Construct sign-in with email link template, embed the link and
// send using custom SMTP server.
return sendSignInEmail(destEmail, displayName, link);
})
.catch((error) => {
// Some error occurred.
});
I'm trying to implement verification of a user's email (with the default verification URL in the email template), AND an ActionCodeSetting URL (dynamic link) to bring the user back to the app. I'm baffled by how Firebase's email verification with ActionCodeSetting is supposed to work. I have read every available page of documentation and it is still unclear to me how to properly configure the "continue URL" to NOT preempt and override the default verification URL.
What I have done:
I tested the email verification with the automatically generated email verification link. It worked.
I then added an ActionCodeSetting URL that uses a domain that is added to the Associated Domains of the XCode project. This worked to bring the user back to the app after clicking the verification link.
Problem: the email verification no longer works.
Here is the code I have implemented:
var actionCodeSettings = ActionCodeSettings.init()
actionCodeSettings.handleCodeInApp = true
let user = Auth.auth().currentUser
let urlString = "https://blaproject.page.link/zCB4"
actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
actionCodeSettings.setAndroidPackageName("com.example.android", installIfNotAvailable:true, minimumVersion:"12")
Auth.auth().currentUser?.sendEmailVerification(with: actionCodeSettings, completion: { (error) in
print("verification email sent")
print("action code setting URL is: \(String(describing: actionCodeSettings.url))")
})
Here is the default verification URL from the email template in the Firebase console:
https://blaproject-ea9d6.firebaseapp.com/__/auth/action?mode=&oobCode=
And here is the verification URL that gets sent by the above code:
https://blaproject.page.link?link=https://blaproject-ea9d6.firebaseapp.com//auth/action?apiKey%3DAIzaSyAi1fxd-HdkfXzYJxTpwmB3_mVCy5gvWxA%26mode%3DverifyEmail%26oobCode%3DqjvGoqc1n3ya0OIi_tWIYTpp59DYKgB6Sbj0EymN2IkAAAFkYNutMA%26continueUrl%3Dhttps://blaproject.page.link/zCE4%26lang%3Den&apn=com.example.android&amv=12&ibi=blaproject.blaprojectV0-2&ifl=https://blaproject-ea9d6.firebaseapp.com//auth/action?apiKey%3DAIzaSyAi1fxd-HdkfXzYJxTpwmB3_mVCy5gvWxA%26mode%3DverifyEmail%26oobCode%3DqjvGoqc1n3ya0OIi_tWIYTpp59DYKgB6Sbj0EymN2IkAAAFkYNutMA%26continueUrl%3Dhttps://blaproject.page.link/zCE4%26lang%3Den
So my question is, why does this URL not verify the user's email and then use the continue URL (and the associated domain) to trigger the app to open? It only triggers the app to open, without verifying the user's email.
Thanks for any tips you can provide to help me understand what I'm not understanding :)
When the link triggers your app to open. You need to parse the oobCode from the deep link. You can use FDL client library to get the deep link. Refer to the following documentation on the format of the deep link and how to parse the code: https://firebase.google.com/docs/auth/custom-email-handler
Once you get the code, you need to apply it using the auth.applyActionCode(code) API. This will verify the email.
After verification, you can call user.reload() to update the emailVerified property on the user. You can also force user.getIdToken(true) to force refresh the token with the updated verified email if you are using Firebase security rule.
For get correct 'oobCode', when you handle the URL in the App, make sure, that you are decoding URL from UTF-8. Because it has other symbols in the URL query.
Example how to encode: url.absoluteString.removingPercentEncoding