mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-12 01:38:04 +02:00
Cliquer sur « Connecter » sans avoir enregistré l'URL publique du site renvoyait une erreur, et la redirection retombait sur https://0.0.0.0 : la directive upgrade-insecure-requests était posée même sur une page servie en HTTP, le navigateur basculait donc la navigation vers un https:// inexistant. - `publicBaseUrl()` : réglage `site_public_url`, variables d'environnement, puis origine de la requête (X-Forwarded-Host / X-Forwarded-Proto). L'adresse de retour OAuth et le lien des publications l'utilisent ; `seo-server` s'appuie dessus au lieu de dupliquer la déduction. - `src/lib/site-url.ts` : déduction pure et testée de l'origine, HTTP par défaut pour localhost et les IP nues, HTTPS pour un nom de domaine. - Middleware : `upgrade-insecure-requests` uniquement quand la page est servie en HTTPS. - Page Réseaux : l'adresse détectée est annoncée comme valeur par défaut, avec le rappel que Facebook et LinkedIn exigent du HTTPS sur un domaine. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
70 lines
3.3 KiB
TypeScript
70 lines
3.3 KiB
TypeScript
import { NextResponse, type NextRequest } from "next/server";
|
|
import NextAuth from "next-auth";
|
|
import { authConfig } from "@/auth.config";
|
|
|
|
// Edge-safe auth instance (no providers / db) used only to gate routes.
|
|
// The redirect target is derived from AUTH_URL (set it to your public URL when
|
|
// running behind a reverse proxy, e.g. AUTH_URL=https://pleinr.ffnancy.fr).
|
|
const { auth } = NextAuth(authConfig);
|
|
|
|
/**
|
|
* Politique de sécurité du contenu, avec un nonce par requête.
|
|
*
|
|
* Next signe ses propres scripts d'hydratation avec le nonce lu dans l'en-tête
|
|
* CSP de la requête : c'est ce qui permet d'interdire les scripts en ligne sans
|
|
* casser l'application.
|
|
*
|
|
* `style-src` garde 'unsafe-inline' : tout le design repose sur des attributs
|
|
* `style` et sur la feuille Google Fonts. C'est un compromis assumé — une
|
|
* injection de style est sans commune mesure avec une injection de script.
|
|
*/
|
|
function contentSecurityPolicy(nonce: string, isDev: boolean, isHttps: boolean): string {
|
|
return [
|
|
"default-src 'self'",
|
|
"base-uri 'self'",
|
|
"object-src 'none'",
|
|
"form-action 'self'",
|
|
"frame-ancestors 'self'",
|
|
// 'strict-dynamic' laisse les scripts chargés par un script de confiance
|
|
// s'exécuter ; en développement Next a besoin d'eval pour le rafraîchissement.
|
|
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic' ${isDev ? "'unsafe-eval'" : ""}`.trim(),
|
|
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
|
|
"font-src 'self' https://fonts.gstatic.com",
|
|
// data: pour les images stockées en data-URI, https: pour les vignettes distantes.
|
|
"img-src 'self' data: https:",
|
|
"connect-src 'self'",
|
|
// Carte « Nous situer » de la fiche adhérent.
|
|
"frame-src https://www.google.com https://maps.google.com",
|
|
// Uniquement quand la page est servie en HTTPS : sur une page HTTP (test
|
|
// par IP avant la pose du DNS, accès direct au port), le navigateur
|
|
// basculerait toutes les navigations vers un https:// inexistant.
|
|
isHttps ? "upgrade-insecure-requests" : "",
|
|
]
|
|
.filter(Boolean)
|
|
.join("; ");
|
|
}
|
|
|
|
export default auth((request: NextRequest) => {
|
|
// `btoa` et non `Buffer` : le middleware tourne sur le runtime Edge.
|
|
const nonce = btoa(crypto.randomUUID());
|
|
const forwardedProto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim();
|
|
const isHttps = forwardedProto ? forwardedProto === "https" : request.nextUrl.protocol === "https:";
|
|
const csp = contentSecurityPolicy(nonce, process.env.NODE_ENV !== "production", isHttps);
|
|
|
|
// L'en-tête est posé sur la REQUÊTE : Next y lit le nonce pour l'appliquer à
|
|
// ses balises <script>. Il est ensuite renvoyé sur la réponse au navigateur.
|
|
const requestHeaders = new Headers(request.headers);
|
|
requestHeaders.set("x-nonce", nonce);
|
|
requestHeaders.set("content-security-policy", csp);
|
|
|
|
const response = NextResponse.next({ request: { headers: requestHeaders } });
|
|
response.headers.set("content-security-policy", csp);
|
|
return response;
|
|
}) as unknown as (request: NextRequest) => Response | Promise<Response>;
|
|
|
|
export const config = {
|
|
// La CSP doit couvrir toutes les pages, pas seulement /backend. On exclut les
|
|
// ressources statiques, qui n'ont pas besoin d'être traitées.
|
|
matcher: ["/((?!_next/static|_next/image|assets|favicon.ico).*)"],
|
|
};
|