From 1cf2619393d4a89de7c6d99dc4fbe2051f770933 Mon Sep 17 00:00:00 2001 From: sam Date: Fri, 13 Dec 2024 21:25:41 +0100 Subject: [PATCH] feat: add email to existing account, change password --- .../Authentication/EmailAuthController.cs | 7 +- .../Mailables/AccountCreationMailable.cs | 1 + .../Mailables/AddEmailMailable.cs | 1 + .../Views/Mail/AccountCreation.cshtml | 2 +- Foxnouns.Backend/Views/Mail/AddEmail.cshtml | 2 +- .../components/settings/AuthMethodRow.svelte | 2 +- .../components/settings/EmailSettings.svelte | 73 +++++++++++++++++++ .../components/settings/NewAuthMethod.svelte | 2 + .../src/lib/i18n/locales/en.json | 7 +- .../src/routes/settings/auth/+page.server.ts | 39 +++++++++- .../src/routes/settings/auth/+page.svelte | 18 ++--- .../settings/auth/add-email/+page.server.ts | 44 +++++++++++ .../settings/auth/add-email/+page.svelte | 49 +++++++++++++ 13 files changed, 227 insertions(+), 20 deletions(-) create mode 100644 Foxnouns.Frontend/src/lib/components/settings/EmailSettings.svelte create mode 100644 Foxnouns.Frontend/src/routes/settings/auth/add-email/+page.server.ts create mode 100644 Foxnouns.Frontend/src/routes/settings/auth/add-email/+page.svelte diff --git a/Foxnouns.Backend/Controllers/Authentication/EmailAuthController.cs b/Foxnouns.Backend/Controllers/Authentication/EmailAuthController.cs index 1587f87..bbf41f5 100644 --- a/Foxnouns.Backend/Controllers/Authentication/EmailAuthController.cs +++ b/Foxnouns.Backend/Controllers/Authentication/EmailAuthController.cs @@ -183,7 +183,7 @@ public class EmailAuthController( return NoContent(); } - [HttpPost("add-email")] + [HttpPost("add-account")] [Authorize("*")] public async Task AddEmailAddressAsync([FromBody] AddEmailAddressRequest req) { @@ -208,6 +208,9 @@ public class EmailAuthController( } else { + ValidationUtils.Validate( + [("password", ValidationUtils.ValidatePassword(req.Password))] + ); await authService.SetUserPasswordAsync(CurrentUser!, req.Password); await db.SaveChangesAsync(); } @@ -232,7 +235,7 @@ public class EmailAuthController( return NoContent(); } - [HttpPost("add-email/callback")] + [HttpPost("add-account/callback")] [Authorize("*")] public async Task AddEmailCallbackAsync([FromBody] EmailCallbackRequest req) { diff --git a/Foxnouns.Backend/Mailables/AccountCreationMailable.cs b/Foxnouns.Backend/Mailables/AccountCreationMailable.cs index 9c33213..41a6609 100644 --- a/Foxnouns.Backend/Mailables/AccountCreationMailable.cs +++ b/Foxnouns.Backend/Mailables/AccountCreationMailable.cs @@ -32,6 +32,7 @@ public class AccountCreationMailable(Config config, AccountCreationMailableView { To(view.To) .From(config.EmailAuth.From!) + .Subject("Create an account") .View("~/Views/Mail/AccountCreation.cshtml", view) .Text(PlainText()); } diff --git a/Foxnouns.Backend/Mailables/AddEmailMailable.cs b/Foxnouns.Backend/Mailables/AddEmailMailable.cs index 1d29d0f..1c381f2 100644 --- a/Foxnouns.Backend/Mailables/AddEmailMailable.cs +++ b/Foxnouns.Backend/Mailables/AddEmailMailable.cs @@ -32,6 +32,7 @@ public class AddEmailMailable(Config config, AddEmailMailableView view) { To(view.To) .From(config.EmailAuth.From!) + .Subject("Confirm adding this email address to an existing account") .View("~/Views/Mail/AddEmail.cshtml", view) .Text(PlainText()); } diff --git a/Foxnouns.Backend/Views/Mail/AccountCreation.cshtml b/Foxnouns.Backend/Views/Mail/AccountCreation.cshtml index cf8d1bc..fb85a65 100644 --- a/Foxnouns.Backend/Views/Mail/AccountCreation.cshtml +++ b/Foxnouns.Backend/Views/Mail/AccountCreation.cshtml @@ -3,7 +3,7 @@

Please continue creating a new pronouns.cc account by using the following link:
- Confirm your email address + @Model.BaseUrl/auth/callback/email/@Model.Code
Note that this link will expire in one hour.

diff --git a/Foxnouns.Backend/Views/Mail/AddEmail.cshtml b/Foxnouns.Backend/Views/Mail/AddEmail.cshtml index 2423434..fcdd2b2 100644 --- a/Foxnouns.Backend/Views/Mail/AddEmail.cshtml +++ b/Foxnouns.Backend/Views/Mail/AddEmail.cshtml @@ -3,7 +3,7 @@

Hello @@@Model.Username, please confirm adding this email address to your account by using the following link:
- Confirm your email address + @Model.BaseUrl/auth/callback/email/@Model.Code
Note that this link will expire in one hour.

diff --git a/Foxnouns.Frontend/src/lib/components/settings/AuthMethodRow.svelte b/Foxnouns.Frontend/src/lib/components/settings/AuthMethodRow.svelte index 62c5d6f..692146a 100644 --- a/Foxnouns.Frontend/src/lib/components/settings/AuthMethodRow.svelte +++ b/Foxnouns.Frontend/src/lib/components/settings/AuthMethodRow.svelte @@ -8,7 +8,7 @@ let name = $derived( method.type === "EMAIL" ? method.remote_id : (method.remote_username ?? method.remote_id), ); - let showId = $derived(method.type !== "FEDIVERSE"); + let showId = $derived(method.type !== "EMAIL");
diff --git a/Foxnouns.Frontend/src/lib/components/settings/EmailSettings.svelte b/Foxnouns.Frontend/src/lib/components/settings/EmailSettings.svelte new file mode 100644 index 0000000..29a1197 --- /dev/null +++ b/Foxnouns.Frontend/src/lib/components/settings/EmailSettings.svelte @@ -0,0 +1,73 @@ + + +

{$t("auth.email-password-title")}

+ +{#if emails.length > 0} +
+
+

Your email addresses

+
+ {#each emails as method (method.id)} + + {/each} + {#if emails.length < max} + + {$t("auth.add-email-address")} + + {/if} +
+
+
+ +

Change password

+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+{:else} +

{$t("auth.no-email-addresses")}

+

+ + + {$t("auth.add-email-address")} + +

+{/if} diff --git a/Foxnouns.Frontend/src/lib/components/settings/NewAuthMethod.svelte b/Foxnouns.Frontend/src/lib/components/settings/NewAuthMethod.svelte index 32018a6..73405bc 100644 --- a/Foxnouns.Frontend/src/lib/components/settings/NewAuthMethod.svelte +++ b/Foxnouns.Frontend/src/lib/components/settings/NewAuthMethod.svelte @@ -19,6 +19,8 @@ return $t("auth.successful-link-tumblr"); case "FEDIVERSE": return $t("auth.successful-link-fedi"); + case "EMAIL": + return $t("auth.successful-link-email"); default: return ""; } diff --git a/Foxnouns.Frontend/src/lib/i18n/locales/en.json b/Foxnouns.Frontend/src/lib/i18n/locales/en.json index 659007e..cd45d49 100644 --- a/Foxnouns.Frontend/src/lib/i18n/locales/en.json +++ b/Foxnouns.Frontend/src/lib/i18n/locales/en.json @@ -56,7 +56,12 @@ "register-with-google": "Register with a Google account", "remote-google-account-label": "Your Google account", "register-with-tumblr": "Register with a Tumblr account", - "remote-tumblr-account-label": "Your Tumblr account" + "remote-tumblr-account-label": "Your Tumblr account", + "email-password-title": "Email and password", + "add-email-address": "Add email address", + "no-email-addresses": "You haven't linked any email addresses yet.", + "check-inbox-for-link-hint": "Check your inbox for a link!", + "successful-link-email": "Your account has successfully been linked to the following email address:" }, "error": { "bad-request-header": "Something was wrong with your input", diff --git a/Foxnouns.Frontend/src/routes/settings/auth/+page.server.ts b/Foxnouns.Frontend/src/routes/settings/auth/+page.server.ts index 4fe52f7..65be131 100644 --- a/Foxnouns.Frontend/src/routes/settings/auth/+page.server.ts +++ b/Foxnouns.Frontend/src/routes/settings/auth/+page.server.ts @@ -1,7 +1,44 @@ -import { apiRequest } from "$api"; +import { apiRequest, fastRequest } from "$api"; +import ApiError, { ErrorCode, type RawApiError } from "$api/error.js"; import type { AuthUrls } from "$api/models/auth"; +import log from "$lib/log"; export const load = async ({ fetch }) => { const urls = await apiRequest("POST", "/auth/urls", { fetch, isInternal: true }); return { urls }; }; + +export const actions = { + password: async ({ request, fetch, cookies }) => { + const body = await request.formData(); + const current = body.get("current") as string | null; + const password = body.get("password") as string | null; + const password2 = body.get("confirm-password") as string | null; + + if (password !== password2) { + return { + ok: false, + error: { + status: 400, + code: ErrorCode.BadRequest, + message: "Passwords do not match", + } as RawApiError, + }; + } + + try { + await fastRequest("POST", "/auth/email/change-password", { + body: { current, new: password }, + isInternal: true, + fetch, + cookies, + }); + + return { ok: true, error: null }; + } catch (e) { + if (e instanceof ApiError) return { ok: false, error: e.obj }; + log.error("error changing password:", e); + throw e; + } + }, +}; diff --git a/Foxnouns.Frontend/src/routes/settings/auth/+page.svelte b/Foxnouns.Frontend/src/routes/settings/auth/+page.svelte index ca0cf27..c0d6056 100644 --- a/Foxnouns.Frontend/src/routes/settings/auth/+page.svelte +++ b/Foxnouns.Frontend/src/routes/settings/auth/+page.svelte @@ -1,14 +1,13 @@ {#if data.urls.email_enabled} -

Email addresses

- + {/if} {#if data.urls.discord}

Discord accounts

diff --git a/Foxnouns.Frontend/src/routes/settings/auth/add-email/+page.server.ts b/Foxnouns.Frontend/src/routes/settings/auth/add-email/+page.server.ts new file mode 100644 index 0000000..4ad86f6 --- /dev/null +++ b/Foxnouns.Frontend/src/routes/settings/auth/add-email/+page.server.ts @@ -0,0 +1,44 @@ +import { fastRequest } from "$api"; +import ApiError, { ErrorCode, type RawApiError } from "$api/error.js"; +import log from "$lib/log.js"; +import { redirect } from "@sveltejs/kit"; + +export const load = async ({ parent }) => { + const { user } = await parent(); + return { firstEmail: user.auth_methods.filter((a) => a.type === "EMAIL").length === 0 }; +}; + +export const actions = { + add: async ({ request, fetch, cookies }) => { + const body = await request.formData(); + const email = body.get("email") as string; + const password = body.get("password") as string | null; + const password2 = body.get("confirm-password") as string | null; + + if (password2 && password !== password2) { + return { + ok: false, + error: { + status: 400, + code: ErrorCode.BadRequest, + message: "Passwords do not match", + } as RawApiError, + }; + } + + try { + await fastRequest("POST", "/auth/email/add-account", { + body: { email, password }, + isInternal: true, + fetch, + cookies, + }); + + return { ok: true, error: null }; + } catch (e) { + if (e instanceof ApiError) return { ok: false, error: e.obj }; + log.error("error adding email address to account:", e); + throw e; + } + }, +}; diff --git a/Foxnouns.Frontend/src/routes/settings/auth/add-email/+page.svelte b/Foxnouns.Frontend/src/routes/settings/auth/add-email/+page.svelte new file mode 100644 index 0000000..fb97c1d --- /dev/null +++ b/Foxnouns.Frontend/src/routes/settings/auth/add-email/+page.svelte @@ -0,0 +1,49 @@ + + +
+

Link a new email address

+ + + +
+
+ + +
+
+ + +
+ {#if data.firstEmail} +
+ + +
+ {/if} + + +
+