Firebase Node.JS Admin SDK send verification email - node.js

I am using the sdk to create a user and a db entry for the user which all works perfectly. Upon creation of the database entry I call a function to sendEmailVerification() but I am guessing this is a client side function as it returns null when being called.
What is the process to send the verify email directly from the admin sdk (if even possible). Currently what I do is send some JSON back to the client to say if the verification email sent successfully or not. But calling the function does not work so it doesn't get that far. Here is my function within node.
function verifiyEmail(email, res) {
var user = admin.auth().currentUser;
user.sendEmailVerification().then(function() {
// Email sent.
var jsonResponse = {
status: 'success',
alertText: '1',
email: email
}
res.send(jsonResponse);
}, function(error) {
// An error happened.
var jsonResponse = {
status: 'success',
alertText: '0',
email: email
}
res.send(jsonResponse);
});
}
UPDATE
I am guessing this isn't possible so I generate a custom token in node and send that back to the client. I then use the token I get back to try and sign the user in by calling the below however the signInWithCustomToken() fuction doesnt get called. Heres my code am I missing something. Seems like a lot of work just to send out the verification email!
function signInUserWithToken(token) {
firebase.auth().signInWithCustomToken(token).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorCode);
console.log(errorMessage);
verifiyEmail();
});
}
UPDATE 2
I scraped the token idea. All i do now is use the onAuthStateChanged() function and handle the email verification there in the client implementation. Its not perfect as this method gets called several times. However adding a flag seems to do the trick. Like the below.
function authListenerContractor() {
// Listening for auth state changes.
$('#not-verified').css('display','none');
var flag = true;
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is verified.
var displayName = user.displayName;
var email = user.email;
var emailVerified = user.emailVerified;
var photoURL = user.photoURL;
var isAnonymous = user.isAnonymous;
var uid = user.uid;
var providerData = user.providerData;
console.log("Email Verified?: " + emailVerified);
if(emailVerified) {
window.location.href = "http://www.my-redirect-url.com";
} else {
if (flag == true) {
$('#not-verified').css('display','inherit');
verifiyEmail();
flag = false;
}
}
} else {
console.log("User is signed out.");
}
});
}
function verifiyEmail() {
var user = firebase.auth().currentUser;
user.sendEmailVerification().then(function() {
// Email sent.
console.log("Verification email sent");
$('#not-verified').text('**Email verification sent. Please check your email now!**');
}, function(error) {
// An error happened.
console.log("Email verification not sent. An error has occurred! >>" + error);
});
}

This is a classic case to use Firebase Cloud Functions
Sending Welcome Mail Example
const functions = require('firebase-functions');
const nodemailer = require('nodemailer');
const gmailEmail = encodeURIComponent(functions.config().gmail.email);
const gmailPassword = encodeURIComponent(functions.config().gmail.password);
const mailTransport = nodemailer.createTransport(
`smtps://${gmailEmail}:${gmailPassword}#smtp.gmail.com`);
const APP_NAME = 'My App';
exports.sendWelcomeEmail = functions.auth.user().onCreate(event => {
const user = event.data; // The Firebase user.
const email = user.email; // The email of the user.
const displayName = user.displayName; // The display name of the user.
return sendWelcomeEmail(email, displayName);
});
function sendWelcomeEmail(email, displayName) {
const mailOptions = {
from: `${APP_NAME} <noreply#firebase.com>`,
to: email
};
mailOptions.subject = `Welcome to ${APP_NAME}!`;
mailOptions.text = `Hey ${displayName || ''}! Welcome to ${APP_NAME}. I hope you will enjoy our service.`;
return mailTransport.sendMail(mailOptions).then(() => {
console.log('New welcome email sent to:', email);
});
}
Check this Link for More Info , used these functions to trigger a mail in this very app
UPDATE
const functions = require('firebase-functions');
const nodemailer = require('nodemailer');
const mailTransport = nodemailer.createTransport(
`smtps://emailid:password#smtp.gmail.com`);
const APP_NAME = 'My App';
exports.sendPageCountEmail = functions.database.ref('/yournode').onWrite(event => { // here we are specifiying the node where data is created
const data = event.data;
return sendEmail('emailid',data);
});
// Sends a welcome email to the given user.
function sendEmail(email,body) {
const mailOptions = {
from:`${APP_NAME}noreply#firebase.com`,
to: email
};
mailOptions.subject = `Welcome to ${APP_NAME}!`;
mailOptions.text = `Welcome to ${APP_NAME}.\n
return mailTransport.sendMail(mailOptions).then(() => {
console.log('New welcome email sent to:', email);
});
}

Related

Twilio: Invalid parameter with Node.js

I'm trying to use the verify API with twilio. Here is what i have
dotenv.config();
import twilio from "twilio";
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = twilio(accountSid, authToken);
try {
let { to } = req.body;
to = String(to).startsWith("+") ? to : "+" + to;
to = String(to).trim().replace(/\s/, ""); // no spaces
console.log(accountSid, to);
const response = await client.verify
.services(accountSid as string)
.verifications.create({
to,
channel: "call", // sms, call, or email
});
return res.status(200).json(response);
} catch (error) {
console.log(error);
}
I'm getting the following error:
{
error: Invalid parameter,
status: 400,
code: 60200,
moreInfo: 'https://www.twilio.com/docs/errors/60200',
details: undefined
}
Here is what my to looks like "+2661890...". What maybe possibly the problem?
I was using the wrong SERVICE_SID i used ACCOUNT_SID instead of SERVICE_SID. I changed my code to:
...
const { sid } = await client.verify.services.create({
friendlyName: "Verification Service",
});
const response = await client.verify.services(sid).verifications.create({
to,
channel: "call", // sms, call, or email
});

Error sending notification through firebase function flutter

I am trying to send push notification to the user whenever the new event is added into the firestore of the firebase.
Whenever I do so it returns me the error saying Error sending notification
node.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().functions);
var newData;
exports.messageTrigger = functions.firestore.document('Messages/{messageId}').onCreate(async(snapshot, context)=> {
if(snapshot.empty){
console.log('No Devices');
return;
}
var tokens = ['mobile-token'];
newData = snapshot.data();
var payload = {
notification : {title: 'Push Title', body: 'Push body', sound: 'default'},
data: {click_action: 'FLUTTER_NOTIFICATION_CLICK', message: newData.message},
};
try{
const response = await admin.messaging.sendToDevice(tokens, payload);
console.log('Notification sent successfully');
} catch (err) {
console.log('Error sending notifications');
}
});
I am passing the mobile token in place of mobile-token
SOLUTION
Changing admin.messaging.sendToDevice() to
const response = await admin.messaging().sendToDevice(tokens, payload)
As admin.messaging.sendToDevice() is not a method

How to reduce email sending time (using nodemailer and firebase)?

We have written code that sends emails to a user and their contacts, when a new node is added to a specific path in Firebase realtime database.
The average time to send the emails is 4 minutes.
We think the problem is due to awaiting for some needed promises.
We would like to get the run time down.
Do you have any advice? Thanks in advance!
This is our code:
const functions = require("firebase-functions");
const nodemailer = require('nodemailer');
require('dotenv').config()
//for fire store
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
const { SENDER_EMAIL, SENDER_PASSWORD } = process.env;
exports.sendEmails = functions.database.ref("/devices/{device_ID}/history/{alert_ID}")
.onWrite(
(snapshot, context) => {
sendMail(snapshot, context);
return true;
}
);
async function sendMail(snapshot, context){
const { before, after } = snapshot;
// new alert created
if (before.val() == null) {
console.log('DEBUG:: NEW ALERT');
// get owners uID from device ID
const deviceRef = db.collection('deviceToUid').doc(context.params.device_ID);
const uidDoc = await deviceRef.get();
if(!uidDoc.exists){
functions.logger.info("No such document!");
return;
}
// get users email from uID
const userRef = db.collection('users').doc(uidDoc.data()[context.params.device_ID]).collection('user-info');
// get users contact
const contactRef = db.collection('users').doc(uidDoc.data()[context.params.device_ID]).collection('contacts');
const [userInfo, contactList] = await Promise.all([userRef.get(), contactRef.get()]);
if(userInfo.empty){
functions.logger.info("No such collection!");
return;
}
const email = userInfo.docs[0].id; // owners email
let contacts = []; // initialize contact list
contactList.forEach(
(doc) => {
if(doc.data().confirmed){
contacts.push(doc.id);
}
}
)
const mailTransport = nodemailer.createTransport({
service: 'gmail',
auth: {
user: SENDER_EMAIL,
pass: SENDER_PASSWORD,
},
});
const mailOptions = {
from: 'ALERT <noreply#firebase.com>',
to: email,
bcc: contacts,
subject: `...Motion detected`,
html: `<p dir=ltr>New Alert...</p>`
};
mailTransport.sendMail(mailOptions, function (error, info) {
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
});
}
}
I'd also recommend learning a bit about list comprehensions, as this:
let contacts = []; // initialize contact list
contactList.forEach(
(doc) => {
if(doc.data().confirmed){
contacts.push(doc.id);
}
}
)
Can be reduced to a more concise:
let contacts = contactList.docs
.filter((doc) => doc.data().confirmed)
.map((doc) => doc.id);
You were getting pretty close, but were missing an await in the top-level function, and one inside sendMail for the call to mailTransport.sendMail.
I think this should be it:
exports.sendEmails = functions.database.ref("/devices/{device_ID}/history/{alert_ID}")
.onWrite(
async (snapshot, context) => {
await sendMail(snapshot, context);
return true;
}
);
async function sendMail(snapshot, context){
const { before, after } = snapshot;
// new alert created
if (before.val() == null) {
console.log('DEBUG:: NEW ALERT');
// get owners uID from device ID
const deviceRef = db.collection('deviceToUid').doc(context.params.device_ID);
const uidDoc = await deviceRef.get();
if(!uidDoc.exists){
functions.logger.info("No such document!");
return;
}
// get users email from uID
const userRef = db.collection('users').doc(uidDoc.data()[context.params.device_ID]).collection('user-info');
// get users contact
const contactRef = db.collection('users').doc(uidDoc.data()[context.params.device_ID]).collection('contacts');
const [userInfo, contactList] = await Promise.all([userRef.get(), contactRef.get()]);
if(userInfo.empty){
functions.logger.info("No such collection!");
return;
}
const email = userInfo.docs[0].id; // owners email
let contacts = []; // initialize contact list
contactList.forEach(
(doc) => {
if(doc.data().confirmed){
contacts.push(doc.id);
}
}
)
const mailTransport = nodemailer.createTransport({
service: 'gmail',
auth: {
user: SENDER_EMAIL,
pass: SENDER_PASSWORD,
},
});
const mailOptions = {
from: 'ALERT <noreply#firebase.com>',
to: email,
bcc: contacts,
subject: `...Motion detected`,
html: `<p dir=ltr>New Alert...</p>`
};
await mailTransport.sendMail(mailOptions, function (error, info) {
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
});
return true;
}
}
Since you were not using await in the top-level call, the Cloud Functions contains will/may shut down the container before the asynchronous calls have completed. For more on this, see the documentation on sync, async and promises - and how Cloud Functions are terminated.

sending email through firebase triggers using sendgrid

I am sending email to user using onCreate trigger in firebase.I am using sendgrid templates for sending emails. when a new document is created in the firestore it should trigger the email to the user.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const sgMail = require('#sendgrid/mail');
const SENDGRID_API_KEY = 'SG.ivqQZKFcSdqONZZ7IRtkjA.1RdSs50..kBaQ';
sgMail.setApiKey(SENDGRID_API_KEY);
exports.firestoreEmail = functions.firestore
.document('userAccount/{userId}')
.onCreate(event => {
const userID = event.params.userId;
if (userID === undefined) {
console.log('userID DOES NOT EXIST')
// This was a deletion event, we don't want to process this
return;
}
else{
console.log(userID )
return db.collection('userAccount').doc(userID)
.get()
.then(doc => {
const user = doc.data()
const msg = {
to: 'lekha.saraf#nexivo.co',
from: 'lekhasaraf09#gmail.com',
subject: 'NewFollower',
templateId: '8...............d760e',
substitutionWrappers: ['{{', '}}'],
substitutions: {
name: user.UserName
// and other custom properties here
}
};
return sgMail.send(msg)
})
// .then(() => console.log('email sent!') )
} // .catch(err => console.log(err) )
});
The error I am getting is:
TypeError: Cannot read property 'userId' of undefined
You have 2 parameters from the .onCreate() method. A snapshot from the created document and the event.
exports.firestoreEmail = functions.firestore
.document('userAccount/{userId}')
.onCreate((documentSnapshot, event) => {
const userID = event.params.userId;
const documentData = documentSnapshot.data();
I do not know if the event.params.userId works for Firestore.
If you need the userId from the user which created the document you could use const userID = event.auth.uid.
If you need the userId from the user you will send the mail to you should write the userId in the document.

How to get push notification in FCM when child is added in Firebase?

Notification is to be sent when a child is added to /ADMIN/Orders
The notification has to be sent to each user who have the same Token ID
My node.js code is
'use strict'
var topic = "Users";
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.updateOrder =
functions.database.ref('/ADMIN/Orders/{order_id}').onWrite(event =>{
const order_id = event.params.order_id;
console.log("Today's order is updated!",order_id);
const deviceToken = admin.database().ref(`/USERS/{user_id}/TokenId`).once('value');
return deviceToken.then(result => {
const TokenId = result.val();
const payload = {
notification: {
title: "Order is updated!",
body: "Click to enter Yes/No",
icon: "default"
}
};
// return admin.messaging().sendToDevice(TokenId,payload).then(response =>{
return admin.messaging().sendToTopic(topic, payload).then(response =>{
console.log('This was the notification feature');
});
});
});
My cloud function(.js file) is showing successful execution, but still the push notificaiton is not appearing on my phone.

Resources