Firebase: how to send reset-link through email template sent from nodemailer? - javascript

I implemented sending emails via nodemailer.
Now when I create new user, that new user get "welcome email".
Problem is cus that "welcome email" should contain option for
resetting password.
How to add Firebase Resetting link in nodemailer email template?
This is my Email Template code for nodemailer
const output = `
<p>You have access to the Church Mutual Assignment Tool.</p>
<p>Follow this link to create new password for your account ${userRecord.email}:</p>
<a href="${resetPasswordLink}">
${resetPasswordLink}
</a>
<p>Thanks,</p>
<p>Your Church Mutual Assignment Tool team</p>
`
let message = {
from: 'nyik6nntutmq3vz6#ethereal.email',
to: `${user.email}`,
subject: 'Welcome to the Church Mutual Assignment Tool',
text: 'Plaintext version of the message',
html: output
}
This is my Nodemailer code:
var mailer = require('nodemailer')
var mailConfig = {
host: 'smtp.ethereal.email',
port: 587,
auth: {
user: 'nyik6nntutmq3vz6#ethereal.email',
pass: '3cbRjkZdPquDqA725s'
}
}
var transporter = mailer.createTransport(mailConfig)
module.exports = transporter

The Admin SDK now has some methods that allow you to do just this exact thing. 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 = 'user#example.com';
admin.auth().generatePasswordResetLink(email, actionCodeSettings)
.then((link) => {
// Do stuff with link here
})
.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 for you.

Related

Picture missing from credential token with GSI

I have been using the new GIS library with the One Tap UX. I have followed all the steps outlined in the setup guide and the authentication works as expected, in fact I like the new flow; Nonetheless I am experiencing a very peculiar issue. For some reason, the picture property is missing from the credential response.
A couple of things to note are:
This is an internal application, which means it is only being used by the google workspace users of the respective domain.
This happens with all users whose profile picture is set using the Admin Directory API.
What I have in the front end is the following:
const promptParent = "signInBox";
const gsiInitializeConfig = {
client_id: '4xxyy55zzz.apps.googleusercontent.com',
callback: handleCredentialResponse,
prompt_parent_id: promptParent,
auto_select: true,
cancel_on_tap_outside: false
};
let idClient;
const doSignIn = ()=>{
idClient = google.accounts;
idClient.id.initialize(gsiInitializeConfig);
idClient.id.prompt();
}
const handleCredentialResponse = (response)=>{
const {credential} = response;
//send idToken to the backend for verification
});
When the DOMContent is loaded, I programatically invoke the doSignIn function. And the one tap shows and it works great. It returns the idToken which then I send to the backend for verification. In the backend I am using express and I have the following:
const token = getTokenFromBody();
if(!token){ return unauthenticated(res); }
const auth2Client = new OAuth2Client(GOOGLE_CLIENT_ID);
const ticket = await auth2Client.verifyIdToken({
audience: GOOGLE_CLIENT_ID,
idToken: token
}).catch(error=>{
console.error(`Failed to verify google id token: ${error}`);
unauthorized(res, "Invalid grant");
});
if(!ticket){ return; }
const ticketPayload = ticket.getPayload();
console.log("ticketPayload", JSON.stringify(ticketPayload));
The logged object for the ticketPayload above looks like this:
{
iss: "https://accounts.google.com",
nbf: 1378,
aud: "44xxyy55zz.apps.googleusercontent.com",
sub: "1559234417",
hd: "domain.com",
email: "user#domain.com",
email_verified: true,
azp: "44xxyy55zz.apps.googleusercontent.com",
name: "User Name",
given_name: "User",
family_name: "Name",
iat: 1664828678,
exp: 1664832278,
jti: "f0549d2544c905sadfcbc13110"
}
That is the response for all the users in the domain, however, for my user, whose photo was set using the page "https://myaccount.google.com", the response is the following:
{
iss: "https://accounts.google.com",
nbf: 1378,
aud: "44xxyy55zz.apps.googleusercontent.com",
sub: "1559234417",
hd: "domain.com",
email: "user#domain.com",
email_verified: true,
azp: "44xxyy55zz.apps.googleusercontent.com",
name: "User Name",
picture: "https://lh3.googleusercontent.com/a-/N5pZpE-zbJUg3=s96-c", // <-----!!!
given_name: "User",
family_name: "Name",
iat: 1664828678,
exp: 1664832278,
jti: "f0549d2544c905sadfcbc13110"
}
In comparisson with the old google sign in library, this behavior is different. How can I get the picture property for all users in the domain?
I reached out to Google Workspace support were they educated me with the following:
Admin-set user profile picutres (either set by an Admin via the Admin Console itself, or by the Admin SDK API call) are private and are not returned in the credential response of the Google Sign In flow.
The reason for that is that when an administrator sets a profile photo to a user's account, the photo becomes visible only to users in the organization and to external users they use Google Chat with. In contrast, (and only if users are allowed to manage their own profile photo) user-set photos are public by default. That is explained under the "Where a user's photo appears section of this support article.
This behavior cannot be changed for privacy reasons. An admin-set photo is neither public information, nor is set by the user, hence is not available in the token.
Although the solution provided makes sense, there is something that I feel is wrong. The fact that the old google sign in library never presented this behavior makes me feel that way. Why should the new sign in library present this behavior now? It is really absurd.

Firebase Auth Email Verification - I need a SDK admin to change email verification user?

I was working in the email verification. I coded the part to login and create account.
When the user create account, the app init the email auth.
I recive the email and click in link. But my currentUser.emailVerified = false,
even when I log in again (I haven't programmed to save credentials yet).
I'm following this Auth with FB Using Email Link in JS - firebase docs
I put the return url with the domain of the vercel that the site is hosted on.
Then it opens the site with some parameters / queries.
I was in doubt if the right thing would be to have to do the firebase function to be this return url and from it change the verification state with the SDK admi, but the doc only says the things I've already done.
Edit:
.env
NEXT_PUBLIC_AUTH_DOMAIN_FIREBASE=my_domain
when app create the account, the app init
await sendSignInLinkToEmail(email);
function definition:
import fireApp from '../fireApp';
async function sendSignInLinkToEmail(email){
const actionCodeSettings = {
url: 'https://' + process.env.NEXT_PUBLIC_AUTH_DOMAIN_FIREBASE,
// This must be true.
handleCodeInApp: true
};
return fireApp.auth().sendSignInLinkToEmail(email, actionCodeSettings)
}
export default sendSignInLinkToEmail;
the account create works, the email is sended. But when i click on link to login, currentUser.emailVerified = false, even when I log in again.
oh, account created but not verified yet. An email will be sent to you. authResponse.user = Im {N: Array(0), l: "xxxxx", m: "[DEFAULT]", s: "votimsg.vercel.app", a: Ii, …}
And when I login from the dev server, emailVerified: false
(auth response of a new login request, not the first)
Login works, but still not verified.

Gmail with Heroku to send mail

I want to add to my web app that after order I'm sending a mail.
I choose Nodemailer because it's the most famous npm to use.
I coded my request and in the local environment, it's working.
I uploaded the code to Heroku and I get an Error.
Error: Invalid login: 534-5.7.14 <https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbs
I checked people told me to disable the captcha wish I did here: UnlockCaptcha
And now I still get the same error, and I get a mail that google blocked the connection what can I do?
const nodemailer = require('nodemailer');
const { sendLog } = require('../middleware/sendLog');
const { coupons, actions } = require('../constant/actionCoupon');
var simple = function () {
var textMultiple = {
text1: 'text1',
text2: 'text2',
};
return textMultiple;
};
// send mail system for the (REQUEST ACCEPTED SYSTEM)
const sendMail = (mail, action) => {
let mailTransporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.MAIL,
pass: process.env.PASSWORD,
},
});
let mailDetails = {
from: process.env.MAIL,
to: mail,
subject: `Thank you for your purchase. with love FameGoal`,
text: "for any probleme please reply on this message",
};
mailTransporter.sendMail(mailDetails, function (err, data) {
if (err) {
console.log(err);
console.log(`error sent mail to ${mail}`, 'error');
} else {
console.log('succeed');
console.log(`succesfully sent mail to ${mail}`, 'info');
}
});
};
exports.sendMail = sendMail;
Using Gmail as an SMTP relay isn't the most ideal because Google servers may reject basic username/password authentication at times.
There are some workarounds. The most ideal is to use OAuth2 to send emails.
OAuth2
OAuth2 uses access tokens to perform authentication instead of a password.
I won't go over the steps to set up OAuth2 because it can take some time but if you're interested, this answer: https://stackoverflow.com/a/51933602/10237430 goes over all of the steps.
App passwords
If the Google account you're trying to send emails from has two step verification enabled, using a password to send emails will not work. You instead need to generate a app-specific password on Google's site and pass that in the password field.
More info on that here: https://support.google.com/accounts/answer/185833?hl=en
Enabling less secure apps
If you still want to use your current setup, you have to make sure you enable less secure apps on the Google account you're sending emails from. This will let you authenticate with Google using just an email and a password.
More info on that here: https://support.google.com/accounts/answer/6010255?hl=en
Basic password authentication will not work until you enable less secure apps.

Nativescript {N} oAuth plugin - Retrieve User Facebook Data

Context
I'm attempting to create an Android app with Nativecript using JavaScript. On the first page, it asks the user to connect with Facebook, and I intend to verify whether or not an account exists with their email address.
Tools
I'm using the nativescript-oauth package to handle the OAuth connection to Facebook. I'm working on a Windows 10 machine via command line.
Code
app.js
var tnsOAuthModule = require("nativescript-oauth");
var facebookInitOptions = TnsOAuthOptionsFacebook = {
clientId: 'REDACTED',
clientSecret: 'REDACTED',
scope: ['email']
};
tnsOAuthModule.initFacebook(facebookInitOptions);
application.start({ moduleName: "views/start/start" });
start.js
//...
var tnsOAuthModule = require("nativescript-oauth");
//...
exports.fbConnect = function(){
console.log("Facebook Connect button tapped");
tnsOAuthModule.login()
.then(()=>{
console.log('logged in');
var token = tnsOAuthModule.accessToken();
console.log("FB Auth token: " + token);
console.log(JSON.stringify(tnsOAuthModule));
})
.catch((er)=>{
console.log(er);
});
console.log("Login sucessful");
}
What goes wrong
The above outputs the following:
JS: Facebook Connect button tapped
...
JS: logged in
JS: FB Auth token: EAAC50oamJosBAF1F3lrGAOntENgSAZA40w4iE3rNOLP1W_REDACTED_Cb7yS9ZB1Ro4qhLroOMwZD
JS: {"instance":{"tokenResult":{"accessToken":"EAAC50oamJosBAF1F3lrGAOntENgSAZA40w4iE3rNOLP1W_REDACTED_Cb7yS9ZB1Ro4qhLroOMwZD","accessTokenExpiration":"2017-03-24T18:27:04.176Z","refreshTokenExpiration":"2017-03-24T18:27:04.176Z"},"credentials":{"authority":"https://www.facebook.com/dialog","tokenEndpointBase":"https://graph.facebook.com","authorizeEndpoint":"/oauth","tokenEndpoint":"/v2.3/oauth/access_token","clientId":"REDACTED","clientSecret":"REDACTED","redirectUri":"https://www.facebook.com/connect/login_success.html","scope":"email"}}}
JS: Application started successfully
As you can see, I successfully authorise the Facebook app and retrieve a working access key and can parse the object that is returned - however I'm trying to retrieve the users' email address. I can see that the "email" is within the scope.
Question
How can I use the nativescript-oauth plugin, or the data from the above object, to retrieve the users' email address, as defined the in scope?
Resources
Nativescript homepage - https://www.nativescript.org/
nativescript-oauth GitHub page - https://github.com/alexziskind1/nativescript-oauth
Nativescript official release of OAuth plugin - https://www.nativescript.org/blog/introducing-the-nativescript-oauth-plugin
You must change the scope to get more details(add "user_friends" to get their friend's list, add "public_profile" for profile info)
var facebookInitOptions = TnsOAuthOptionsFacebook = {
clientId: 'REDACTED',
clientSecret: 'REDACTED',
scope: ['email', 'user_friends', 'public_profile']
};
Lastly, in your "App review" section of your facebook developer page, ensure those scope fields are active and shown(they will have a green dot next to them with description of data). You might need to make the app live/start a submission to get approval first if the above code doesn't work.

Google account gets suspended when using Gmail API from Node.js Nodemailer

I have an Express web application on a Node.js server. It supports sending email notifications for registration, password reset, etc. For this, I use Nodemailer and XOAuth2 to connect to Google API using a normal Google account. The connection works well and emails are being sent as expected. But after a while (1-3 days), Google sends me a message saying my account "has been suspended because of a violation of our Terms of Service" (screenshot). So I have to manually recover the account using the link in the notification and then the emails that were blocked during suspension are sent.
After getting 3 of those notifications, I noticed that they follow only if I triggered my email sending function (see below)
I tried to log XOAuth2's token refresh and seems like it works
I tried to renew the refreshToken from the playground several times but the problem remains
IMHO, it might be that I'm using a free account and Google wants me to pay for using its API, it thinks I do some kind spams, or it just doesn't want me to use its API. I'm I wrong?
So my questions are:
Why does my account get suspended?
What can I do to fix this problem?
Code details (approximate):
var bluebird = require('bluebird');
var nodemailer = require('nodemailer');
var xoauth2 = require('xoauth2');
// clientId, clientSecret taken from: https://console.developers.google.com/
// Create credentials > OAuth client ID > Web application >
// Name = Nodemailer,
// Authorised redirect URIs = https://developers.google.com/oauthplayground
//
// refreshToken taken from: https://developers.google.com/oauthplayground/
// Access token location: Autorization header w/ Bearer prefix
// Access type: Offline
// Force prompt: Consent Screen
// Use your own OAuth credentials: yes
// Authorized APIs: everything inside Gmail API v1
// Auto refresh the token after it expires: yes
xoauth2Gen = xoauth2.createXOAuth2Generator({
user: 'example#gmail.com', // my real google account
clientId: '84037...t.com', // my real clientId
clientSecret: 'c3Yo...KP', // my real clientSecret
refreshToken: '1/ex...Wk' // my real refreshToken
// nothing more here
});
var mail_transport: {
service: 'Gmail',
auth: { xoauth2: xoauth2Gen }
};
var mailer = bluebird.promisifyAll(nodemailer.createTransport(mail_transport));
mailer.sendMail({
from : '"Example User" <example#gmail.com>',
to : 'recipient#gmail.com',
subject : 'Example subject',
text : 'Example\nplain\ntext',
// could it be bad html?
html : 'Example<br><strong>html</strong><br>version'
}).then(function (info) { console.log(info); });

Categories

Resources