Files
netris-nestri/apps/auth/src/email.ts
Wanjohi 96b0cf8111 feat(auth): sign in with an email address
Wires the pin-code provider, which existed and was never reachable, and makes
it the only branch that can create an account. Steam now resolves an existing
connection instead of minting a user from a persona, and refuses when there is
no account behind it — which is an answer the interface renders rather than an
implicit signup.

Delivery is a small provider-neutral POST rather than a vendor SDK: configure
an endpoint, a key and a from address. With none of them set it logs the code
outside production so a local sign-in works, and throws in production, because
a screen that says "check your email" when nothing was sent leaves someone
waiting instead of telling anybody.

A person who has only ever signed in by email has no connected account, and
the token says so with an empty value — the same one a server-to-server caller
has always carried.
2026-09-05 00:02:16 +03:00

62 lines
2.0 KiB
TypeScript

/**
* Getting a pin code to a mailbox.
*
* Deliberately not tied to one mail vendor: it posts a small JSON body to
* whatever endpoint is configured, so swapping providers is configuration and
* not a code change. Three settings, all optional except in production —
* `EMAIL_SEND_URL`, `EMAIL_API_KEY`, `EMAIL_FROM`.
*/
export interface MailerConfig {
EMAIL_SEND_URL?: string;
EMAIL_API_KEY?: string;
EMAIL_FROM?: string;
NODE_ENV?: string;
}
/**
* Send the code, or fail loudly.
*
* With no mailer configured this logs the code and carries on, which is what
* makes a local sign-in possible without a mail account. In production the
* same situation throws instead: a signup screen that says "check your email"
* when nothing was sent is worse than one that says it is broken, because the
* person waits instead of telling anybody.
*/
export async function sendVerificationCode(
config: MailerConfig,
email: string,
code: string
): Promise<void> {
const configured = config.EMAIL_SEND_URL && config.EMAIL_API_KEY && config.EMAIL_FROM;
if (!configured) {
if (config.NODE_ENV === 'production') {
throw new Error('Email delivery is not configured, so no sign-in code can be sent');
}
console.log(`[auth] sign-in code for ${email}: ${code}`);
return;
}
const response = await fetch(config.EMAIL_SEND_URL!, {
method: 'POST',
headers: {
authorization: `Bearer ${config.EMAIL_API_KEY}`,
'content-type': 'application/json'
},
body: JSON.stringify({
from: config.EMAIL_FROM,
to: [email],
subject: `${code} is your Nestri sign-in code`,
text:
`Your Nestri sign-in code is ${code}.\n\n` +
`It expires shortly. If you did not ask to sign in, you can ignore this.`
})
});
if (!response.ok) {
// The body is included because the useful part of a delivery failure is
// always the provider's own message, and it is otherwise lost.
throw new Error(`Sending the sign-in code failed: ${response.status} ${await response.text()}`);
}
}