feat(opaque): handle the force password change

This commit is contained in:
Edouard Vanbelle
2026-08-02 03:01:26 +02:00
parent e8de07768e
commit 903d769e03
5 changed files with 148 additions and 1 deletions
+15
View File
@@ -246,6 +246,21 @@ export interface AuthResponse {
refresh_token: string;
token_type: string;
expires_in: number;
/**
* Mirrors `auth.users.force_password_change_at_next_login` — set
* TRUE by the admin password-reset flow (see backend
* `OpaquePgRepository::clear_registration`) so admin-picked
* passwords stay temporary until the user changes them. When true,
* the SPA's post-login handler must route to `/settings/security`
* (or the equivalent change-password surface) instead of the
* user's home. Cleared server-side by a successful
* `POST /api/auth/change-password`.
*
* Optional on the wire because the backend `#[serde(default)]`s
* to `false` — older clients / non-login endpoints hitting this
* type won't nil-deref.
*/
force_password_change?: boolean;
}
/**
+15 -1
View File
@@ -163,7 +163,21 @@
}
session.setUser(data.user);
postRegisterNotice = null;
await goto(resolve(redirectTarget), { replaceState: true });
// When the backend flags `force_password_change` the user's
// current credential is an admin-set temporary password;
// route them to the profile page (`?forcePasswordChange=1`
// lights up an in-page banner) instead of the requested
// destination. The redirect target is preserved as `next`
// so the profile flow can bounce back after they pick a
// real password.
if (data.force_password_change) {
const next = encodeURIComponent(redirectTarget);
await goto(resolve(`/profile?forcePasswordChange=1&next=${next}`), {
replaceState: true
});
} else {
await goto(resolve(redirectTarget), { replaceState: true });
}
} catch (err) {
if (err instanceof ApiError && err.errorType === 'EmailNotVerified') {
// Server auto-sent a verification magic-link on the