feat(auth): R4 S1 — fix auth dead ends

- password reset also sets email_verified (the mail proves the mailbox),
  rescuing an account that never clicked the verify link; is_active (admin
  approval) is deliberately left untouched
- add POST /auth/verify/resend (rate-limited, off-response-path, anti-enum)
  + a "resend" mode/CTA on the verifyInvalid banner, so an expired/lost
  verification link is no longer a dead end (re-register was a silent no-op)
- OAuth cancel/deny now redirects to /?login=oauth_cancelled with a friendly
  banner instead of a raw 400 JSON page; pop the oauth_link markers BEFORE the
  token exchange so an aborted link flow can't poison the next clean sign-in
This commit is contained in:
2026-07-23 00:15:26 +02:00
parent e663247791
commit a851fb213b
5 changed files with 112 additions and 16 deletions
+8
View File
@@ -1557,6 +1557,14 @@ export const api = {
// Confirm an email-verification token the SPA read from the URL fragment (SB3).
verifyEmail: (token: string): Promise<{ ok: boolean }> =>
req("/auth/verify", { method: "POST", body: JSON.stringify({ token }) }, { quiet: true }),
// Re-issue a verification link (the first one expired, or was lost). Uniform response — never
// reveals whether the email has a pending account.
resendVerification: (email: string): Promise<{ status: string }> =>
req(
"/auth/verify/resend",
{ method: "POST", body: JSON.stringify({ email }) },
{ quiet: true },
),
// SA4: sign every OTHER session for this account out (keeps the current one).
logoutOthers: (): Promise<{ ok: boolean }> => req("/auth/logout-others", { method: "POST" }),
// Set or change the signed-in account's password (errors shown inline via quiet).