Send sign-in mail as HTML so quoted-printable wrapping cannot break the verify URL.

Staging was delivering magic links that looked expired because the token query string was encoded and wrapped in plaintext MIME. Also read rate-limit retry from the API error body, mention spam in the success copy, and document SES relay DNS.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilallo
2026-09-01 10:01:25 -06:00
co-authored by Cursor
parent 511c3efb4c
commit d920e39f09
10 changed files with 258 additions and 112 deletions
+1
View File
@@ -125,6 +125,7 @@ describe("LoginForm", () => {
await screen.findByRole("heading", { name: /check your email/i }),
).toBeInTheDocument();
expect(screen.getByText(/we sent a sign-in link/i)).toBeInTheDocument();
expect(screen.getByText(/check spam or promotions/i)).toBeInTheDocument();
});
it("submits a long email without treating length as invalid", async () => {
+103
View File
@@ -0,0 +1,103 @@
import { afterEach, describe, expect, it } from "vitest";
import nodemailer from "nodemailer";
import {
buildVerifyLinkParts,
resolveMailFrom,
} from "../../lib/server/mail";
const VERIFY_URL =
"https://staging.communityrule.info/api/auth/magic-link/verify?token=5IdE_BHowaw-QJj7Rwue7CbB8wDXvYITvnxRb1FGqxA";
function decodeQuotedPrintable(value: string): string {
return value
.replace(/=\r?\n/g, "")
.replace(/=([0-9A-Fa-f]{2})/g, (_, hex: string) =>
String.fromCharCode(Number.parseInt(hex, 16)),
);
}
const MAIL_FROM_KEYS = ["SMTP_FROM", "CLOUDRON_MAIL_FROM"] as const;
const ORIGINAL_FROM = Object.fromEntries(
MAIL_FROM_KEYS.map((key) => [key, process.env[key]]),
) as Record<(typeof MAIL_FROM_KEYS)[number], string | undefined>;
afterEach(() => {
for (const key of MAIL_FROM_KEYS) {
const original = ORIGINAL_FROM[key];
if (original === undefined) delete process.env[key];
else process.env[key] = original;
}
});
describe("buildVerifyLinkParts", () => {
it("puts the exact verify URL in both text and the HTML href", () => {
const { text, html } = buildVerifyLinkParts(
VERIFY_URL,
"Open this link to sign in (it expires in 15 minutes):",
"If you did not request this, you can ignore this email.",
"Sign in",
);
expect(text).toContain(VERIFY_URL);
expect(html).toContain(`href="${VERIFY_URL}"`);
expect(html).toContain(">Sign in</a>");
});
it("escapes HTML in the intro and href", () => {
const { html } = buildVerifyLinkParts(
'https://example.test/verify?token=a&b="c"',
'View "Rule <beta>"',
"Ignore if unexpected.",
"Open",
);
expect(html).toContain("View &quot;Rule &lt;beta&gt;&quot;");
expect(html).toContain(
'href="https://example.test/verify?token=a&amp;b=&quot;c&quot;"',
);
});
});
describe("MIME encoding of verify-link mail", () => {
it("keeps a clickable href after quoted-printable encoding", async () => {
const { text, html } = buildVerifyLinkParts(
VERIFY_URL,
"Open this link to sign in (it expires in 15 minutes):",
"If you did not request this, you can ignore this email.",
"Sign in",
);
const transporter = nodemailer.createTransport({
streamTransport: true,
buffer: true,
newline: "unix",
});
const info = await transporter.sendMail({
from: "Community Rule <staging.app@communityrule.info>",
to: "member@example.com",
subject: "Sign in to Community Rule",
text,
html,
});
const raw = Buffer.isBuffer(info.message)
? info.message.toString("utf8")
: String(info.message);
const decoded = decodeQuotedPrintable(raw);
expect(decoded).toContain(`href="${VERIFY_URL}"`);
expect(decoded).toContain(VERIFY_URL);
expect(decoded).not.toContain("token=3D");
});
});
describe("resolveMailFrom", () => {
it("prefers SMTP_FROM, then CLOUDRON_MAIL_FROM", () => {
delete process.env.SMTP_FROM;
delete process.env.CLOUDRON_MAIL_FROM;
expect(resolveMailFrom()).toBe("noreply@localhost");
process.env.CLOUDRON_MAIL_FROM = "staging.app@communityrule.info";
expect(resolveMailFrom()).toBe("staging.app@communityrule.info");
process.env.SMTP_FROM = "Community Rule <hello@communityrule.info>";
expect(resolveMailFrom()).toBe(
"Community Rule <hello@communityrule.info>",
);
});
});