From bfe2ed783ffd85e58551c4f9805ccca8261666d2 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 8 Oct 2026 14:31:22 -0400 Subject: [PATCH] The web's password forms read the server's minimum length The server's MIN_PASSWORD_LEN was 8, and the web wrote 8 out five times: two checks and three placeholders. The constant moves beside the other policy numbers in settings.py (auth.py imports it), /api/config serves it as min_password_length, and the config store hands it to Register, Reset and Account. 8 stays only as the fallback until the config answers. DRY pass #2, batch 3 (#5372). Co-Authored-By: Claude Opus 5.5 --- frontend/src/stores/config.ts | 7 +++++++ frontend/src/views/AccountView.vue | 2 +- frontend/src/views/RegisterView.vue | 6 +++--- frontend/src/views/ResetPasswordView.vue | 6 +++--- src/inkwell/auth.py | 3 +-- src/inkwell/settings.py | 5 +++++ 6 files changed, 20 insertions(+), 9 deletions(-) diff --git a/frontend/src/stores/config.ts b/frontend/src/stores/config.ts index 4435978..13132d2 100644 --- a/frontend/src/stores/config.ts +++ b/frontend/src/stores/config.ts @@ -46,6 +46,9 @@ export interface PublicConfig { // Whether this server can email a reset link, so sign-in offers "Forgot password?" // (#5266). Absent on an older server and on the offline desktop: no. password_reset_by_email?: boolean; + // The shortest password an account may have (the server's MIN_PASSWORD_LEN). + // Absent on an older server and on the offline desktop, which has no accounts. + min_password_length?: number; // Every client this server holds, keyed by platform id. Absent on a server that // holds none, and absent on the desktop's own offline config — the Tauri build // answers `config_get` locally and has no clients to hand out. @@ -66,6 +69,8 @@ export const useConfigStore = defineStore("config", () => { // that never had the field, both correctly offer no downloads. const clients = ref>({}); const passwordResetByEmail = ref(false); + // The server's own number; 8 only until /api/config answers, or if it never does. + const minPasswordLength = ref(8); const loaded = ref(false); async function load(): Promise { @@ -79,6 +84,7 @@ export const useConfigStore = defineStore("config", () => { trashRetentionDays.value = cfg.trash_retention_days ?? 30; clients.value = cfg.clients ?? {}; passwordResetByEmail.value = cfg.password_reset_by_email ?? false; + minPasswordLength.value = cfg.min_password_length ?? 8; } catch { // Keep defaults if the config endpoint is unreachable. } finally { @@ -99,6 +105,7 @@ export const useConfigStore = defineStore("config", () => { trashRetentionDays, clients, passwordResetByEmail, + minPasswordLength, loaded, load, reload, diff --git a/frontend/src/views/AccountView.vue b/frontend/src/views/AccountView.vue index 3fb4160..dd6fbdf 100644 --- a/frontend/src/views/AccountView.vue +++ b/frontend/src/views/AccountView.vue @@ -264,7 +264,7 @@ onMounted(() => { type="password" label="New password" autocomplete="new-password" - placeholder="At least 8 characters" + :placeholder="`At least ${config.minPasswordLength} characters`" :error="passwordError" required /> diff --git a/frontend/src/views/RegisterView.vue b/frontend/src/views/RegisterView.vue index d583624..190aba9 100644 --- a/frontend/src/views/RegisterView.vue +++ b/frontend/src/views/RegisterView.vue @@ -24,8 +24,8 @@ const loading = ref(false); async function submit() { error.value = ""; - if (password.value.length < 8) { - error.value = "Password must be at least 8 characters."; + if (password.value.length < config.minPasswordLength) { + error.value = `Password must be at least ${config.minPasswordLength} characters.`; return; } loading.value = true; @@ -80,7 +80,7 @@ async function submit() { label="Password" type="password" autocomplete="new-password" - placeholder="At least 8 characters" + :placeholder="`At least ${config.minPasswordLength} characters`" required />

dict: "trash_retention_days": await get_setting(db, "trash_retention_days"), # Whether the sign-in screen offers "Forgot password?" (#5266). "password_reset_by_email": await mail_configured(db), + "min_password_length": MIN_PASSWORD_LEN, }