Sendgrid 供應商
概觀
Sendgrid 供應商使用電子郵件傳送包含帶有驗證權杖 URL 的「魔法連結」,可用於登入。
除了使用一或多個 OAuth 服務外,新增電子郵件登入支援,可讓使用者在無法存取其 OAuth 帳戶時(例如,帳戶被鎖定或刪除)也能登入。
Sendgrid 供應商可以與一或多個 OAuth 供應商一起使用(或取代)。
運作方式
在初次登入時,系統會將**驗證權杖**傳送至提供的電子郵件地址。根據預設,此權杖的有效期為 24 小時。如果在此時間內使用驗證權杖(即,點擊電子郵件中的連結),則會為該使用者建立帳戶,並讓使用者登入。
如果有人在登入時提供*現有帳戶*的電子郵件地址,系統會傳送電子郵件,並且在他們點擊電子郵件中的連結時,登入與該電子郵件地址相關聯的帳戶。
Sendgrid 供應商可與 JSON Web Token 和資料庫管理的工作階段一起使用,但是**您必須設定資料庫**才能使用它。如果未使用資料庫,則無法啟用電子郵件登入。
設定
-
首先,您需要將您的網域新增到您的 Sendgrid 帳戶。Sendgrid 要求這樣做,並且此網域必須是您在
from
供應商選項中使用的地址的網域。 -
接下來,您必須在Sendgrid 儀表板中產生 API 金鑰。您可以將此 API 金鑰儲存為
AUTH_SENDGRID_KEY
環境變數。
AUTH_SENDGRID_KEY=abc
如果您將環境變數命名為 AUTH_SENDGRID_KEY
,供應商將會自動擷取它,而且您的 Auth.js 設定物件可以更簡化。但是,如果您想要將它重新命名為其他名稱,則必須在您的 Auth.js 設定中手動將它傳遞至供應商。
import NextAuth from "next-auth"
import Sendgrid from "next-auth/providers/sendgrid"
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: ...,
providers: [
Sendgrid({
// If your environment variable is named differently than default
apiKey: COMPANY_AUTH_SENDGRID_API_KEY,
from: "no-reply@company.com"
}),
],
})
-
請勿忘記設定其中一個資料庫配接器,用於儲存電子郵件驗證權杖。
-
您現在可以使用
/api/auth/signin
的電子郵件地址開始登入流程。
直到使用者第一次驗證其電子郵件地址時,才會為該使用者建立使用者帳戶(即,在 Users
表格中新增項目)。如果電子郵件地址已與帳戶相關聯,則當使用者點擊魔法連結電子郵件中的連結並使用完驗證權杖時,會登入該帳戶。
自訂
電子郵件內文
您可以透過將自訂函式做為 sendVerificationRequest
選項傳遞至 Sendgrid()
,完全自訂所傳送的登入電子郵件。
import NextAuth from "next-auth"
import Sendgrid from "next-auth/providers/sendgrid"
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
Sendgrid({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
sendVerificationRequest({
identifier: email,
url,
provider: { server, from },
}) {
// your function
},
}),
],
})
例如,以下顯示我們的內建 sendVerificationRequest()
方法的來源。請注意,我們正在此方法中呈現 HTML (html()
) 並進行網路呼叫 (fetch()
) 到 Sendgrid,以實際執行傳送。
export async function sendVerificationRequest(params) {
const { identifier: to, provider, url, theme } = params
const { host } = new URL(url)
const res = await fetch("https://api.sendgrid.com/v3/mail/send", {
method: "POST",
headers: {
Authorization: `Bearer ${provider.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
personalizations: [{ to: [{ email: to }] }],
from: { email: provider.from },
subject: `Sign in to ${host}`,
content: [
{ type: "text/plain", value: text({ url, host }) },
{ type: "text/html", value: html({ url, host, theme }) },
],
}),
})
if (!res.ok) throw new Error("Sendgrid error: " + (await res.text()))
}
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>
`
}
// Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)
function text({ url, host }: { url: string; host: string }) {
return `Sign in to ${host}\n${url}\n\n`
}
如果您想要使用與許多電子郵件用戶端相容的 React 產生美觀的電子郵件,請查看 mjml 或 react-email。
驗證權杖
根據預設,我們會產生隨機驗證權杖。如果您想要覆寫它,您可以在供應商選項中定義 generateVerificationToken
方法。
import NextAuth from "next-auth"
import Sendgrid from "next-auth/providers/sendgrid"
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
Sendgrid({
async generateVerificationToken() {
return crypto.randomUUID()
},
}),
],
})
正規化電子郵件地址
根據預設,Auth.js 會正規化電子郵件地址。它將地址視為不區分大小寫(這在技術上不符合 RFC 2821 規格,但實際上這會導致更多問題,例如,從資料庫中依電子郵件查詢使用者時。)並移除任何可能以逗號分隔清單傳入的次要電子郵件地址。您可以使用 Sendgrid
供應商上的 normalizeIdentifier
方法來套用自己的正規化。下列範例顯示預設行為
import NextAuth from "next-auth"
import Sendgrid from "next-auth/providers/sendgrid"
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
Sendgrid({
normalizeIdentifier(identifier: string): string {
// Get the first two elements only,
// separated by `@` from user input.
let [local, domain] = identifier.toLowerCase().trim().split("@")
// The part before "@" can contain a ","
// but we remove it on the domain part
domain = domain.split(",")[0]
return `${local}@${domain}`
// You can also throw an error, which will redirect the user
// to the sign-in page with error=EmailSignin in the URL
// if (identifier.split("@").length > 2) {
// throw new Error("Only one email allowed")
// }
},
}),
],
})
請務必確保這會傳回單一電子郵件地址,即使傳入了多個地址也是如此。