import assert from "node:assert/strict"; import { test } from "node:test"; import { confirmLinkFor, normalizeEmail, parseConfirmTarget, requestOrigin, sentTooRecently, } from "./signin-link.ts"; const now = new Date("2026-10-15T12:00:00Z"); const expiringIn = (seconds: number) => new Date(now.getTime() + seconds * 1000); test("sends when the address has no live token", () => { assert.equal(sentTooRecently(null, now), false); }); test("holds back a second link within a minute of the first", () => { assert.equal(sentTooRecently(expiringIn(20 * 60), now), true); assert.equal(sentTooRecently(expiringIn(19 * 60 + 1), now), true); }); test("sends again once the last link is a minute old", () => { assert.equal(sentTooRecently(expiringIn(19 * 60), now), false); assert.equal(sentTooRecently(expiringIn(5 * 60), now), false); }); test("normalizes addresses to trimmed lowercase", () => { assert.equal(normalizeEmail(" Masao@Example.COM "), "masao@example.com"); }); const callback = "http://localhost:3120/api/auth/callback/email?callbackUrl=%2Faccount&token=abc123&email=a%40b.co"; test("the emailed link points at the confirm page on the callback's origin", () => { assert.equal( confirmLinkFor(callback), "http://localhost:3120/signin/confirm?url=http%3A%2F%2Flocalhost%3A3120%2Fapi%2Fauth%2Fcallback%2Femail%3FcallbackUrl%3D%252Faccount%26token%3Dabc123%26email%3Da%2540b.co", ); }); test("the confirm page accepts the email callback on its own origin", () => { assert.deepEqual(parseConfirmTarget(callback, "http://localhost:3120"), { email: "a@b.co", params: [ ["callbackUrl", "/account"], ["token", "abc123"], ["email", "a@b.co"], ], }); }); test("the confirm page refuses anything else", () => { const origin = "http://localhost:3120"; assert.equal(parseConfirmTarget(undefined, origin), null); assert.equal(parseConfirmTarget("not a url", origin), null); assert.equal( parseConfirmTarget(callback.replace("localhost:3120", "evil.test"), origin), null, ); assert.equal( parseConfirmTarget(callback.replace("callback/email", "signout"), origin), null, ); assert.equal( parseConfirmTarget("/api/auth/callback/email?token=a&email=b", origin), null, ); assert.equal( parseConfirmTarget( `${origin}/api/auth/callback/email?email=a%40b.co`, origin, ), null, ); }); test("the request origin follows the forwarded headers, as Auth.js does", () => { assert.equal( requestOrigin( new Headers({ host: "localhost:3120", "x-forwarded-proto": "http" }), ), "http://localhost:3120", ); assert.equal( requestOrigin( new Headers({ host: "internal:80", "x-forwarded-host": "schematax.com" }), ), "https://schematax.com", ); }); test("a pinned origin replaces a forged host in both the page and the callback", () => { const forged = "https://evil.example/api/auth/callback/email?callbackUrl=%2Faccount&token=abc123&email=a%40b.co"; assert.equal( confirmLinkFor(forged, "https://schematax.com"), "https://schematax.com/signin/confirm?url=https%3A%2F%2Fschematax.com%2Fapi%2Fauth%2Fcallback%2Femail%3FcallbackUrl%3D%252Faccount%26token%3Dabc123%26email%3Da%2540b.co", ); });