NodeJS firebase-admin cannot send email verification link - node.js

I'm using node firebase-admin to manage user as follow.
function (req, res, next) {
const { email, password } = req.body;
const auth = getAuth();
const userRecord = await auth.createUser({ email, password });
auth.generateEmailVerificationLink(email).then(console.log);
// this log verify link successfully xyz.firebaseapp.com/__/auth/action?mode=verifyEmail&oobCode=...
await accountService.createAccount({ id: userRecord.uid, email })
res.status(200).json(userRecord);
}
I also tried different email and also debug auth.generateEmailVerificationLink function and saw it invoked to firebase auth api and succeed. But cannot find email in mail box or spam.

The generateEmailVerificationLink method of the Firebase Admin SDK only generates the link you would put into a message. Once the link is generated, you then need to embed that link into a message and then send the email using whatever service you desire.
If you want to use the built-in email verification methods, you must use the appropriate sendVerificationEmail method of the client-side SDKs.
The Firebase Client SDKs provide the ability to send users emails containing links they can use for password resets, email address verification, and email-based sign-in. These template-based emails are sent by Google and have limited customizability.

Related

Send email from NestJS, deployed to Firebase functions to the associated Gmail account's address

I've a NestJS application deployed to Firebase wired up with Firebase functions. I've an API which accepts a form data from a different Firebase Angular frontend project (I prefer the BE and FE projects to be separated).
I'd like to send that contact form data through my NestJS backend due to validation purposes via email to the Firebase admin email address (my Google account email) with all the form data, after the validation is succeeded.
So it's basiacally a contact form, by the user, to the admin. I've digged through the documentation and I've found solutions only for the other direction, so the app sends email to the users after something triggers this function (with Nodemailer, and with a 3rd party SMTP mail service).
Is there a solution to send the contact form data to my Gmail (associated with the Firebase account too) in the desired way? Do I really need to use Nodemailer and a 3rd party service to send an email to myself with the data from the contact form?
The process flow should be the following:
Users fills out the contact form
After FE validation the data is sent to the NestJS API
After BE validation the data is sent to my Gmail email address
Thanks for the suggestions in advance!
elyndel
Using Nodemailer would be the easiest option to send an email. You can use the same recipient email as the sender so you don't need any other mail service like SendGrid. Just specify the same email in both from and to:
const mailOptions = {
from: "user#gmail.com",
to: "user#gmail.com",
subject: "New registration",
text: "<Details>",
};
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
console.log(error);
} else {
console.log("Email sent: " + info.response);
}
});

Google Action Builder Account Linking with custom authentication in Node js

I'm creating Google Actions Builderaction console and I'm having some doubt in Account Linking. Should be like, need to authenticate account linking based on my database data.
Example:
While Account Linking if I wanted to pass a email (abc#gmail.com), that email should be active and only on that case Account Linking should be allow. So for this I want to write custom Node Js function.
So that I have used Link Type as oAuth and Authorization, Token URL I set with as my custom Node Js functions.
My doubt:
how to pass email id while link Account Linking.
After validate email how can I link account in Google Actions Builder.
My Node Js Code
Here I want to write function inside auth call back function inside if(result).
const express = require('express');
const port = 5003;
const app = express();
app.get('/', (req, res) =>{
res.send(`Welcome to Test App Nodejs`);
})
app.get('/auth', (req, res) =>{
var email = req.query.email;
userModel.findAll({
where: {
emailId: email,
status:1
}
}).then((result) =>{
if(result){
// Allow to account link
} else{
// to return Thanks for trying to account linking
}
}).catch((err)=>{
res.send(err);
})
});
app.listen(port, (req, res)=>{
console.log(`Test App runing with ${port}`)
})
There are a number of things about your question that don't fit with how Account Linking is meant to work, so it might make sense to get a brief overview of how Account Linking works.
The purpose of Account Linking is to provide a way that a user record that you maintain for your service gets associated with an Assistant account. This is done (broadly speaking) by the user authorizing Google to access basic information about the user's records in your system. This is done using OAuth2.
There are variants (authorizing using a mobile app, or authorizing the Google account to give you the user record), but they generally work the same way:
You authorize Google to get access to information
Google provides this information as part of the request sent to your webhook
So it does not exactly make sense for you to provide to your webhook an email address and expect it to link somehow. That isn't how this works. If anything - it makes it so you don't need to ask the user for their email address, you can just get it from the linked account.
If you are trying to build a webhook that does the authorization part, you'll need to have it handle OAuth2. This is a lot more than "pass an email address", however, and while it is not difficult, it can be tricky to get some security elements correct. This is usually best left to tools such as Auth0 or other identity providers.
You can also learn more about Account Linking and how it works in general with Action Builder.
Also, keep in mind that conversational actions will be shut down on June 13 2023.

Can I reset a user's password using the Firebase Admin SDK for Node?

The docs from Firebase suggest that the API offers the same features as the console:
It is not always convenient to have to visit the Firebase console in order to manage your Firebase users. The admin user management API provides programmatic access to those same users. It even allows you to do things the Firebase console cannot, such as retrieving a user's full data and changing a user's password, email address or phone number.
But the reference docs don't list a function to reset a user's password. Am I missing something?
EDIT: This answer is now out of date, see Andrea's answer below for how to send a password reset link through the Firebase SDK.
It depends on which definition of 'reset' you're using.
If you mean reset as in 'change', then yes - the updateUser function allows you to provide a new password. See the following example from the docs:
admin.auth().updateUser(uid, {
email: "modifiedUser#example.com",
phoneNumber: "+11234567890",
emailVerified: true,
password: "newPassword",
displayName: "Jane Doe",
photoURL: "http://www.example.com/12345678/photo.png",
disabled: true
})
.then(function(userRecord) {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully updated user", userRecord.toJSON());
})
.catch(function(error) {
console.log("Error updating user:", error);
});
If, on the other hand, you mean reset as in 'send a password reset email', then no, there doesn't seem to be a simple way of doing so via the Admin SDK.
Yes, you can. To generate a password reset link, you provide the existing user's email. Then you can use any email service you like to send the actual email. Link to documentation.
// Admin SDK API to generate the password reset link.
const userEmail = 'user#example.com';
admin.auth().generatePasswordResetLink(userEmail, actionCodeSettings)
.then((link) => {
// Construct password reset email template, embed the link and send
// using custom SMTP server.
return sendCustomPasswordResetEmail(email, displayName, link);
})
.catch((error) => {
// Some error occurred.
});

CRON Node.js Gmail API script

How do I properly setup Gmail API script that sends emails?
I am about to use this method and I started building my script from this quickstart guide.
Is there alternative way to do this without using OAuth 2 validation? Or a way to validate once for all?
Well, in using Gmail APi with your app, you need to use OAuth 2.0 because all request to the Gmail API must be authorized by an authenticated user. And if you notice the quickstart, there is a step here that you need to create a credentials/Outh client ID to make this API work.
For more information, there is another way to authorize your app with Gmail. You can do it with the help of Google+ Sign-in that provide a "sign-in with Google" authentication method for your app.
While asking for authorization from GMail, OAuth 2.0 gives one access token and one refresh token. To avoid validation every time, store the access token. Use the refresh token to get the new access token after it is expired (access token expires every one hour).
Read about this process here: https://developers.google.com/identity/protocols/OAuth2
I found solution using JWT to authorize OAuth2.
You need to have admin account to create Domain wide delegation service account. Then in Developer console you need to download service key JSON file which you load as credentials.
First fetch all users like this: (here you need to use account with admin directory rights)
const google = require('googleapis');
const gmail = google.gmail('v1');
const directory = google.admin('directory_v1');
const scopes = [
'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/admin.directory.user.readonly'
];
const key = require('./service_key.json');
var authClient = new google.auth.JWT(
key.client_email,
key,
key.private_key,
scopes,
"authorized#mail.com"
);
authClient.authorize(function(err, tokens){
if (err) {
console.log(err);
return;
}
directory.users.list(
{
auth: authClient,
customer: 'my_customer',
maxResults: 250,
orderBy: 'email'
}, (err, resp) => {
if (err) {
console.log(err);
return;
}
console.log(resp);
});
});
Then you need to fetch Thread lists (100 per request (page)). And for each thread object you need to call get method for full thread. When using Gmail API authorize as user you want to fetch emails from. In request as userId use value 'me'.

Nodemailer/Gmail - What exactly is a refresh token and how do I get one?

I'm trying to do a simple contact form in a node app, using nodemailer. I want all the msg to be sent from a gmail account I made for this purpose, to my personnal mail.
on the client side, all I do is to get the name/mail/message of the customer and send it to the server. It works fine locally but fails to work when deployed (on heroku btw).
After a quick search, it seems I have to generate a ClientId and ClientSecret from Google Developers Console - which I did - but when it comes to generating a "refresh token" iI'm completely lost.
var smtpTransport = nodemailer.createTransport("SMTP",{
service:"Gmail",
auth:{
XOAuth2: {
user:"myaccount#gmail.com",
clientId:"",
clientSecret:"",
refreshToken:""
}
}
});
I am confused : What exactly is a refresh token and how do I get one ?
Notes by this answer original's author:
So, I finally managed to figure it out. I'm surprised I couldn't find more ressources about that so for those who need to use Gmail with Nodemailer
I found the answer here: http://masashi-k.blogspot.fr/2013/06/sending-mail-with-gmail-using-xoauth2.html
Try creating a new User if you already had one and things ain't working fine. It was the case for me.
I hope this will be useful to someone,
Cheers
Question 1: What exactly is a refresh token?
From documentation found here:
A refresh token provides your app continuous access to Google APIs while the user is not logged into your application.
(...)
Considerations:
Be sure to store the refresh token safely and permanently, because you can only obtain a refresh token the first time that you perform the code exchange flow.
There are limits on the number of refresh token that are issued—one limit per client/user combination, and another per user across all clients. If your application requests too many refresh tokens, it may run into these limits, in which case older refresh tokens stop working.
See also Offline Access and Using a refresh token.
Question 2: How do I get one?
Step 1: Obtain OAuth 2.0 credentials at Google Developers Console
As stated here, you should:
Go to the Google Developers Console.
Select a project, or create a new one.
In the sidebar on the left, expand APIs & auth. Next, click APIs. Select the Enabled APIs link in the API section to see a list of all your enabled APIs. Make sure that the "Gmail API" is on the list of enabled APIs. If you have not enabled it, select the Gmail API from the list of APIs (under Google Apps APIs), then select the Enable API button for the API.
In the sidebar on the left, select Credentials.
If you haven't done so already, create your project's OAuth 2.0 credentials by clicking Create new Client ID, and providing the information needed to create the credentials.
Look for the Client ID and Client secret in the table associated with each of your credentials.
PAY SPECIAL ATTENTION TO specifying https://developers.google.com/oauthplayground
as a Redirect URI when you create a new User in the console.
Otherwise, you will have an error.
Step 2: Obtain the refresh token at Google OAuth2.0 Playground
Go to the Google Oauth2.0 Playground.
Click the Gear Button on the right-top. Set your Client ID and Client Secret obtained from the Google Developers Console, and select Access token location as Authorization header w/ Bearer prefix. Close this configuration overlay.
Set up the scopes. Use https://mail.google.com/ as it's the one need by nodemailer. Then click the Authorize APIs button.
After OAuth2.0 authorization, exchange authorization code for tokens and voilá! your refresh token is ready-to-use
For those who have been looking around for a working example/code snippet, follow Radioreve's Answer until you are able to get the access token and refresh token. (Basically, go to the playground, make sure it asks for access for sending mail and mail.google.com, give permission, exchange authorization code for tokens)
Note that the expires time I entered was new Date().getTime() + 2000 which was close to the expiration seconds seen on the playground. I am not sure if I had to enter access token and expiration time accurately as it seems to be refreshing the token automatically.
Use this sample code written in ECMAScript 6:
const user_name = 'something#gmail.com';
const refresh_token = '';
const access_token = '';
const client_id = '';
const client_secret = '';
const email_to = 'receiver#gmail.com';
const nodemailer = require('nodemailer');
let transporter = nodemailer
.createTransport({
service: 'Gmail',
auth: {
type: 'OAuth2',
clientId: client_id,
clientSecret: client_secret
}
});
transporter.on('token', token => {
console.log('A new access token was generated');
console.log('User: %s', token.user);
console.log('Access Token: %s', token.accessToken);
console.log('Expires: %s', new Date(token.expires));
});
// setup e-mail data with unicode symbols
let mailOptions = {
from : user_name, // sender address
to : email_to, // list of receivers
subject : 'Hello ✔', // Subject line
text : 'Hello world ?', // plaintext body
html : '<b>Hello world ?</b>', // html body
auth : {
user : user_name,
refreshToken : refresh_token,
accessToken : access_token,
expires : 1494388182480
}
};
// send mail with defined transport object
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
return console.log(error);
}
console.log('Message sent: ' + info.response);
});
You can Simple use Google SMTP to send email. Use nodemailer with smtp.google.com and email and App password (not gmail password).
How to Get App Password.
Now you have to enable 2 Step Verification in Google (How to Enable 2 Step Auth)
You need to generate App Specific Password. Goto Google My Account > Security
Click on App Password > Select Other and you will get App Password
You can use normal smtp with email and App password.

Resources