From 344958943c0269c7b77fd461e58875e7c064b1e9 Mon Sep 17 00:00:00 2001 From: keyan Date: Thu, 10 Mar 2022 16:47:00 -0600 Subject: [PATCH] provide 'plain text' email login link for users using embedded browsers in their mobile email clients --- pages/api/auth/[...nextauth].js | 92 +++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/pages/api/auth/[...nextauth].js b/pages/api/auth/[...nextauth].js index 5c0a8125..cad4a78a 100644 --- a/pages/api/auth/[...nextauth].js +++ b/pages/api/auth/[...nextauth].js @@ -2,6 +2,7 @@ import NextAuth from 'next-auth' import Providers from 'next-auth/providers' import Adapters from 'next-auth/adapters' import prisma from '../../../api/models' +import nodemailer from 'nodemailer' export default (req, res) => NextAuth(req, res, options) @@ -90,6 +91,7 @@ const options = { Providers.Email({ server: process.env.LOGIN_EMAIL_SERVER, from: process.env.LOGIN_EMAIL_FROM, + sendVerificationRequest, profile: profile => { return profile } @@ -105,3 +107,93 @@ const options = { signIn: '/login' } } + +function sendVerificationRequest ({ + identifier: email, + url, + token, + baseUrl, + provider +}) { + return new Promise((resolve, reject) => { + const { server, from } = provider + // Strip protocol from URL and use domain as site name + const site = baseUrl.replace(/^https?:\/\//, '') + + nodemailer.createTransport(server).sendMail( + { + to: email, + from, + subject: `login to ${site}`, + text: text({ url, site, email }), + html: html({ url, site, email }) + }, + (error) => { + if (error) { + return reject(new Error('SEND_VERIFICATION_EMAIL_ERROR', error)) + } + return resolve() + } + ) + }) +} + +// Email HTML body +const html = ({ url, site, email }) => { + // Insert invisible space into domains and email address to prevent both the + // email address and the domain from being turned into a hyperlink by email + // clients like Outlook and Apple mail, as this is confusing because it seems + // like they are supposed to click on their email address to sign in. + const escapedEmail = `${email.replace(/\./g, '​.')}` + const escapedSite = `${site.replace(/\./g, '​.')}` + + // Some simple styling options + const backgroundColor = '#f5f5f5' + const textColor = '#212529' + const mainBackgroundColor = '#ffffff' + const buttonBackgroundColor = '#FADA5E' + const buttonBorderColor = '#FADA5E' + const buttonTextColor = '#212529' + + // Uses tables for layout and inline CSS due to email client limitations + return ` + + + + + +
+ ${escapedSite} +
+ + + + + + + + + + + + + +
+ login as ${escapedEmail} +
+ + + + +
login
+
+ Or copy and paste this link: ${url} +
+ If you did not request this email you can safely ignore it. +
+ +` +} + +// Email text body –fallback for email clients that don't render HTML +const text = ({ url, site }) => `Sign in to ${site}\n${url}\n\n`