v0.16.1 — Fix forced-password-change re-firing every visit

The dashboard gate trusted session.user.mustChangePassword from the JWT, which is
only refreshed on re-login/update() — so after a user set a new password the stale
cookie kept bouncing them back to /change-password. Read the flag from the DB (the
source of truth) so it clears immediately and never re-fires. Cheap for a 2-user
app; also re-checks the account is still active.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-16 01:26:00 -05:00
parent 5f4c482636
commit 80324afea2
3 changed files with 20 additions and 4 deletions

View File

@@ -1,5 +1,6 @@
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { getSession } from "@/lib/auth"; import { getSession } from "@/lib/auth";
import { db } from "@/lib/db";
import { Sidebar } from "@/components/layout/sidebar"; import { Sidebar } from "@/components/layout/sidebar";
import { TopNav } from "@/components/layout/topnav"; import { TopNav } from "@/components/layout/topnav";
import { Footer } from "@/components/layout/footer"; import { Footer } from "@/components/layout/footer";
@@ -7,10 +8,18 @@ import { Footer } from "@/components/layout/footer";
export default async function DashboardLayout({ children }: { children: React.ReactNode }) { export default async function DashboardLayout({ children }: { children: React.ReactNode }) {
const session = await getSession(); const session = await getSession();
if (!session) redirect("/login"); if (!session) redirect("/login");
// Force a password change before anything else (provisioned accounts ship with // Force a password change before anything else (provisioned accounts ship with
// a known default + mustChangePassword=true). The target page is outside this // a known default + mustChangePassword=true). Read the flag from the DB, not the
// layout group, so there's no redirect loop. // JWT: the cookie is only refreshed on re-login/update(), so trusting it would
if (session.user.mustChangePassword) redirect("/change-password"); // keep bouncing the user back here even after they've set a new password. The
// target page is outside this layout group, so there's no redirect loop.
const account = await db.user.findUnique({
where: { id: session.user.id },
select: { active: true, mustChangePassword: true },
});
if (!account || !account.active) redirect("/login");
if (account.mustChangePassword) redirect("/change-password");
return ( return (
<div className="flex h-screen flex-col"> <div className="flex h-screen flex-col">

View File

@@ -8,6 +8,13 @@ export type ChangelogEntry = {
}; };
export const CHANGELOG: ChangelogEntry[] = [ export const CHANGELOG: ChangelogEntry[] = [
{
version: "0.16.1",
date: "2026-06-16",
changes: [
"Fixed: after you set a new password, the app could keep asking you to change it again on every visit. It now clears the moment you save your new password.",
],
},
{ {
version: "0.16.0", version: "0.16.0",
date: "2026-06-16", date: "2026-06-16",

View File

@@ -1,2 +1,2 @@
// Bump on every user-visible release. Changelog entries live in `src/lib/changelog.ts`. // Bump on every user-visible release. Changelog entries live in `src/lib/changelog.ts`.
export const APP_VERSION = "0.16.0"; export const APP_VERSION = "0.16.1";