HTTP Email
We have a few built-in HTTP Email providers like Resend, SendGrid and Postmark, sometimes you may want to use your own HTTP endpoint to send emails.
To do this, we can write our own provider with a custom sendVerificationRequest
method. Don’t forget, an email
type provider requires a database adapter.
import NextAuth from "next-auth"
import { sendVerificationRequest } from "./lib/authSendRequest"
export const { handlers, auth } = NextAuth({
adapter,
providers: [
{
id: "http-email",
name: "Email",
type: "email",
maxAge: 60 * 60 * 24, // Email link will expire in 24 hours
sendVerificationRequest,
},
],
})
After we’ve setup the initial configuration, you’ve got to write sendVerificationRequest
function. Below is a simple version which just sends a text email with a link to the user.
export async function sendVerificationRequest({ identifier: email, url }) {
// Call the cloud Email provider API for sending emails
const response = await fetch("https://api.sendgrid.com/v3/mail/send", {
// The body format will vary depending on provider, please see their documentation
body: JSON.stringify({
personalizations: [{ to: [{ email }] }],
from: { email: "noreply@company.com" },
subject: "Sign in to Your page",
content: [
{
type: "text/plain",
value: `Please click here to authenticate - ${url}`,
},
],
}),
headers: {
// Authentication will also vary from provider to provider, please see their docs.
Authorization: `Bearer ${process.env.SENDGRID_API}`,
"Content-Type": "application/json",
},
method: "POST",
})
if (!response.ok) {
const { errors } = await response.json()
throw new Error(JSON.stringify(errors))
}
}
A more advanced sendVerificationRequest
can be seen below, this is a version of the builtin function.
export async function sendVerificationRequest(params) {
const { identifier: to, provider, url, theme } = params
const { host } = new URL(url)
const res = await fetch("https://api.resend.com/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${provider.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: provider.from,
to,
subject: `Sign in to ${host}`,
html: html({ url, host, theme }),
text: text({ url, host }),
}),
})
if (!res.ok)
throw new Error("Resend error: " + JSON.stringify(await res.json()))
}
function html(params: { url: string; host: string; theme: Theme }) {
const { url, host, theme } = params
const escapedHost = host.replace(/\./g, "​.")
const brandColor = theme.brandColor || "#346df1"
const color = {
background: "#f9f9f9",
text: "#444",
mainBackground: "#fff",
buttonBackground: brandColor,
buttonBorder: brandColor,
buttonText: theme.buttonText || "#fff",
}
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};">
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}"><a href="${url}"
target="_blank"
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;">Sign
in</a></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>
`
}
To sign in via this custom provider, you would refer to it by the id in when you are calling the sign-in method, for example: signIn('http-email', { email: 'user@company.com' })
.