Last active
June 15, 2024 13:33
-
-
Save tomfa/1d6b67093f643172ce28dbcf18175693 to your computer and use it in GitHub Desktop.
Next Auth Email OTP Provider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const authOptions: NextAuthOptions = { | |
// [...other options] | |
providers: [ | |
env.NODE_ENV === "development" | |
? ConsoleOtpProvider() | |
: EmailOtpProvider({ from: env.EMAIL_FROM, server: env.EMAIL_URL }), | |
], | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { EmailOtpProvider } from "~/server/email-otp.auth"; | |
export const ConsoleOtpProvider = () => | |
EmailOtpProvider({ | |
server: { host: "localhost", port: 25, auth: { user: "", pass: "" } }, | |
from: "NextAuth <[email protected]>", | |
sendVerificationRequest: async ({ token }) => { | |
console.log(`----------- <ConsoleEmail> -----------`); | |
console.log(JSON.stringify({ message: `OTP ${token}` }, undefined, 2)); | |
console.log(`----------- </ConsoleEmail> -----------`); | |
}, | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createTransport } from "nodemailer"; | |
import EmailProvider, { | |
type SendVerificationRequestParams, | |
} from "next-auth/providers/email"; | |
import { type EmailUserConfig } from "@auth/core/providers"; | |
const generateOtpCode = async () => { | |
return Math.floor(1000 + Math.random() * 9000).toString(); | |
}; | |
export const EmailOtpProvider = ( | |
options: Omit<EmailUserConfig, "sendVerificationRequest"> & { | |
sendVerificationRequest?: ( | |
options: SendVerificationRequestParams, | |
) => Promise<void>; | |
}, | |
) => | |
EmailProvider({ | |
maxAge: 10 * 60, | |
generateVerificationToken: generateOtpCode, | |
sendVerificationRequest: options.sendVerificationRequest | |
? options.sendVerificationRequest | |
: async ({ identifier: email, url, token, provider }) => { | |
const baseUrl = new URL(url).origin; | |
const host = baseUrl.replace(/^https?:\/\//, ""); | |
const transport = createTransport(provider.server); | |
await transport.sendMail({ | |
to: email, | |
from: provider.from, | |
subject: `${token} is your authentication code`, | |
text: text({ host, token }), | |
html: html({ host, token }), | |
}); | |
}, | |
...options, | |
}); | |
function text({ host, token }: { host: string; token: string }) { | |
return `Use ${token} to sign in to ${host}`; | |
} | |
/** | |
* Email HTML body | |
* Insert invisible space into domains 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 it to sign in. | |
* | |
* @note We don't add the email address to avoid needing to escape it, if you do, remember to sanitize it! | |
*/ | |
function html({ | |
theme, | |
token, | |
host, | |
}: { | |
theme?: { brandColor: string; buttonText: string }; | |
token: string; | |
host: string; | |
}) { | |
const escapedHost = host.replace(/\./g, "​."); | |
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing | |
const brandColor = theme?.brandColor || "#4470b8"; | |
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing | |
const buttonText = theme?.buttonText || "#fff"; | |
const color = { | |
background: "#f9f9f9", | |
text: "#444", | |
mainBackground: "#fff", | |
buttonBackground: brandColor, | |
buttonBorder: brandColor, | |
buttonText, | |
}; | |
return ` | |
<body style="background: ${color.background};"> | |
<table width="100%" border="0" cellspacing="20" cellpadding="0" | |
style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;"> | |
<tr> | |
<td align="center" | |
style="padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};"> | |
Use code below to sign in to <strong>${escapedHost}</strong> | |
</td> | |
</tr> | |
<tr> | |
<td align="center" style="padding: 20px 0;"> | |
<table border="0" cellspacing="0" cellpadding="0"> | |
<tr> | |
<td align="center" style="border-radius: 5px;" bgcolor="${color.buttonBackground}"><span | |
style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;">${token}</span></td> | |
</tr> | |
</table> | |
</td> | |
</tr> | |
<tr> | |
<td align="center" | |
style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};"> | |
If you did not request this email you can safely ignore it. | |
</td> | |
</tr> | |
</table> | |
</body> | |
`; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment