mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 17:25:19 +03:00
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.
62 lines
2.0 KiB
TypeScript
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()}`);
|
|
}
|
|
}
|