diff --git a/.changeset/email-message-headers.md b/.changeset/email-message-headers.md new file mode 100644 index 0000000..b99ccce --- /dev/null +++ b/.changeset/email-message-headers.md @@ -0,0 +1,10 @@ +--- +"@seamless-auth/core": minor +"@seamless-auth/express": minor +"@seamless-auth/fastify": minor +"@seamless-auth/nextjs": minor +--- + +`EmailMessage` carries an optional `headers` record, taken from `@seamless-auth/types` 0.34.0, so an override or a custom transport can send headers such as `List-Unsubscribe` and `List-Unsubscribe-Post`. Core passes an override's headers to the email transport untouched; `@seamless-auth/messaging-aws` sends them through SES v2 and validates the names and values. + +`SendEnrollmentInviteEmailInput` now comes from `@seamless-auth/types` rather than being declared in core, and the Express, Fastify and Next.js adapters export it alongside the other messaging types. diff --git a/packages/core/src/authMessaging.ts b/packages/core/src/authMessaging.ts index a3a101f..4b36606 100644 --- a/packages/core/src/authMessaging.ts +++ b/packages/core/src/authMessaging.ts @@ -18,6 +18,7 @@ export type { DeliveryResult, EmailMessage, MessagingChannel, + SendEnrollmentInviteEmailInput, SendMagicLinkEmailInput, SendOtpEmailInput, SendOtpSmsInput, @@ -30,19 +31,13 @@ import type { DeliveryPurpose, DeliveryResult, EmailMessage, + SendEnrollmentInviteEmailInput, SendMagicLinkEmailInput, SendOtpEmailInput, SendOtpSmsInput, SmsMessage, } from "@seamless-auth/types"; -export interface SendEnrollmentInviteEmailInput { - to: string; - signInUrl: string; - from?: string; - subject?: string; -} - /** * An owner notice the auth API asked this server to send, such as "account recovery * was requested". `text` is ready to send. `cancelUrl` carries a credential: send it diff --git a/packages/core/tests/deliverAuthMessage.test.js b/packages/core/tests/deliverAuthMessage.test.js index 810a87d..7290dac 100644 --- a/packages/core/tests/deliverAuthMessage.test.js +++ b/packages/core/tests/deliverAuthMessage.test.js @@ -84,6 +84,30 @@ describe("deliverAuthMessage", () => { expect(email.send.mock.calls[0][0].subject).toBe("Code for user@acme.test"); }); + it("hands an override's headers to the transport untouched", async () => { + const email = emailTransport(); + const headers = { + "List-Unsubscribe": "", + "List-Unsubscribe-Post": "List-Unsubscribe=One-Click", + }; + + await deliverAuthMessage( + { + email, + overrides: { + magicLinkEmail: (_input, defaults) => ({ ...defaults, headers }), + }, + }, + { + kind: "magic_link_email", + to: "user@acme.test", + magicLinkUrl: "https://acme.test/link", + }, + ); + + expect(email.send.mock.calls[0][0].headers).toEqual(headers); + }); + it("routes an SMS instruction to the SMS transport", async () => { const sms = smsTransport(); diff --git a/packages/express/src/index.ts b/packages/express/src/index.ts index 078cd5a..a125025 100644 --- a/packages/express/src/index.ts +++ b/packages/express/src/index.ts @@ -26,6 +26,7 @@ export type { ResourceServerOptions, ResourceServerUser, SeamlessAuthMessagingOptions, + SendEnrollmentInviteEmailInput, StepUpRequirement, SeamlessUser, SmsMessage, diff --git a/packages/express/tests/types/consumer.ts b/packages/express/tests/types/consumer.ts index c3dd967..3458337 100644 --- a/packages/express/tests/types/consumer.ts +++ b/packages/express/tests/types/consumer.ts @@ -6,6 +6,7 @@ import { getSeamlessUser, requireAuth, requireRole, + type EmailMessage, } from "@seamless-auth/express"; const app = express(); @@ -33,3 +34,13 @@ requireRole(42); createSeamlessAuthServer(42); // @ts-expect-error getSeamlessUser takes a request and options getSeamlessUser(42); + +const unsubscribable: EmailMessage = { + to: "user@example.com", + subject: "News", + text: "Hello", + headers: { "List-Unsubscribe": "" }, +}; +// @ts-expect-error header values are strings +const badHeader: EmailMessage = { ...unsubscribable, headers: { "X-Count": 1 } }; +void badHeader; diff --git a/packages/fastify/src/index.ts b/packages/fastify/src/index.ts index 4c897ec..d98f0d6 100644 --- a/packages/fastify/src/index.ts +++ b/packages/fastify/src/index.ts @@ -24,6 +24,7 @@ export type { ResourceServerOptions, ResourceServerUser, SeamlessAuthMessagingOptions, + SendEnrollmentInviteEmailInput, SeamlessAuthUser, SeamlessUser, SmsMessage, diff --git a/packages/nextjs/src/index.ts b/packages/nextjs/src/index.ts index 585abb1..24aef51 100644 --- a/packages/nextjs/src/index.ts +++ b/packages/nextjs/src/index.ts @@ -37,6 +37,7 @@ export type { ResourceServerOptions, ResourceServerUser, SeamlessAuthMessagingOptions, + SendEnrollmentInviteEmailInput, SeamlessAuthUser, SeamlessUser, SmsMessage,