mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-24 07:38:04 +02:00
Suite de l'audit : les cinq points laissés en suspens sont traités. Mot de passe - `changeOwnPassword` exige désormais le mot de passe actuel. Un poste laissé ouvert ne suffit plus à s'approprier un compte. Les erreurs reviennent sur l'écran avec un message au lieu d'une page d'erreur brute. Sessions révocables - Colonne `users.session_version`, portée dans le jeton et comparée à la base. - `getSession()` (src/lib/session.ts) remplace `auth()` sur les 20 pages et actions : rôle, rattachement adhérent et existence du compte sont relus à chaque requête. Supprimer un compte ou réinitialiser un mot de passe coupe immédiatement les sessions ouvertes, sans attendre l'expiration du jeton. Dépendances — de 8 vulnérabilités (2 critiques) à zéro - next 15.5.19 → 15.5.21, next-auth beta.25 → beta.32, drizzle-orm 0.38 → 0.45. - postcss et sharp forcés par `overrides` sur leurs versions corrigées, Next ne les ayant pas encore reprises ; drizzle-kit et esbuild montés côté outillage. - `npm audit fix --force` a été écarté : il proposait de RÉTROGRADER Next en 9.3.3 et eslint-config-next en 12, ce qui aurait cassé l'application. - `eslint-config-next` traînait dans node_modules sans être déclaré : retiré. CSP complète - Politique à nonce posée par le middleware, nonce régénéré à chaque requête, `script-src` sans 'unsafe-inline'. `style-src` garde 'unsafe-inline' : tout le design repose sur des attributs style, et une injection de style n'a pas la portée d'une injection de script. - Conséquence assumée : rendu dynamique pour toutes les pages, un HTML pré-généré ne pouvant pas porter de nonce. Tests - `npm test` (runner natif node:test via tsx), 18 tests sur le filtre XSS, la limitation des tentatives de connexion et le chiffrement des jetons. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
68 lines
2.6 KiB
TypeScript
68 lines
2.6 KiB
TypeScript
import { redirect } from "next/navigation";
|
|
import { getSession } from "@/lib/session";
|
|
import { changeOwnPassword } from "../actions";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
export default async function ChangePasswordPage({
|
|
searchParams,
|
|
}: {
|
|
searchParams: Promise<{ error?: string }>;
|
|
}) {
|
|
const session = await getSession();
|
|
if (!session?.user) redirect("/login");
|
|
|
|
const { error } = await searchParams;
|
|
const forced = session.user.mustChangePassword;
|
|
|
|
return (
|
|
<div style={{ maxWidth: 460 }}>
|
|
<div style={{ background: "#fff", border: "1px solid #e6dcc6", borderRadius: 16, padding: 26 }}>
|
|
<h3 className="font-display" style={{ fontWeight: 800, fontSize: 20, margin: "0 0 8px", color: "#26201a" }}>
|
|
Changer mon mot de passe
|
|
</h3>
|
|
{forced && (
|
|
<div style={{ background: "#fbeede", border: "1px solid #ecd8b8", color: "#9a6638", borderRadius: 10, padding: "11px 14px", fontSize: 13.5, marginBottom: 16 }}>
|
|
Première connexion : pour des raisons de sécurité, choisissez un nouveau mot de passe
|
|
avant de continuer.
|
|
</div>
|
|
)}
|
|
|
|
{error && (
|
|
<div style={{ background: "#fbe9e6", border: "1px solid #f2d5cf", color: "#a8503c", borderRadius: 10, padding: "11px 14px", fontSize: 13.5, marginBottom: 16 }}>
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
<form action={changeOwnPassword}>
|
|
<label className="field-label">
|
|
{forced ? "Mot de passe temporaire reçu" : "Mot de passe actuel"}
|
|
</label>
|
|
<input
|
|
name="currentPassword"
|
|
type="password"
|
|
required
|
|
className="field"
|
|
style={{ marginBottom: 14 }}
|
|
autoComplete="current-password"
|
|
/>
|
|
|
|
<label className="field-label">Nouveau mot de passe (8 caractères min.)</label>
|
|
<input name="password" type="password" required minLength={8} className="field" style={{ marginBottom: 14 }} autoComplete="new-password" />
|
|
|
|
<label className="field-label">Confirmer le mot de passe</label>
|
|
<input name="confirm" type="password" required minLength={8} className="field" style={{ marginBottom: 18 }} autoComplete="new-password" />
|
|
|
|
<button
|
|
type="submit"
|
|
className="font-display"
|
|
style={{ width: "100%", border: "none", background: "#13324F", color: "#fff", fontWeight: 700, fontSize: 14.5, padding: 13, borderRadius: 11, cursor: "pointer" }}
|
|
>
|
|
Enregistrer le nouveau mot de passe
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|