syncova-policies/middleware.ts
Weapie 265ca8ac38
All checks were successful
Container-Image bauen und veröffentlichen / build-and-push (push) Successful in 5m15s
Improve accessibility and access controls; verify production and database recovery
2026-09-14 11:44:41 +02:00

91 lines
2.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
import { auth } from "@/lib/auth"
/** Cookies, unter denen Auth.js die Sitzung ablegt (http und https). */
const SESSION_COOKIES = [
"authjs.session-token",
"__Secure-authjs.session-token",
]
/**
* Ermittelt die vom Client tatsächlich verwendete Basis-URL.
*
* `req.nextUrl.origin` zeigt hinter einem Reverse-Proxy oder im Container auf
* den internen Port (3000) – Weiterleitungen landeten dadurch auf einer für den
* Benutzer nicht erreichbaren Adresse. Die Forwarded-Header haben Vorrang.
*/
function externalBaseUrl(req: NextRequest) {
const forwardedHost = req.headers.get("x-forwarded-host")?.split(",")[0]?.trim()
const host = forwardedHost || req.headers.get("host")?.trim()
if (!host) return req.nextUrl.origin
const forwardedProto = req.headers.get("x-forwarded-proto")?.split(",")[0]?.trim()
const protocol = forwardedProto || req.nextUrl.protocol.replace(":", "") || "http"
return `${protocol}://${host}`
}
/**
* Leitet zur Anmeldung um und entfernt dabei ein eventuell vorhandenes
* Sitzungs-Cookie. Ohne das bleibt ein mit einem anderen NEXTAUTH_SECRET
* verschlüsseltes Cookie im Browser liegen und erzeugt bei jedem Aufruf einen
* JWTSessionError ("no matching decryption secret"), bis es von Hand
* gelöscht wird.
*/
function redirectToSignIn(req: NextRequest) {
const target = new URL("/auth/signin", externalBaseUrl(req))
const response = NextResponse.redirect(target)
for (const name of SESSION_COOKIES) {
if (req.cookies.has(name)) {
response.cookies.set(name, "", { path: "/", maxAge: 0 })
}
}
return response
}
export default auth((req) => {
const { pathname } = req.nextUrl
// Öffentlich erreichbare Bereiche
if (
pathname === "/" ||
pathname.startsWith("/erklaerung/") ||
pathname === "/impressum" ||
pathname === "/datenschutz" ||
pathname === "/barrierefreiheit" ||
pathname.startsWith("/auth/") ||
pathname.startsWith("/api/auth/") ||
pathname.startsWith("/api/privacy-policies") ||
pathname.startsWith("/api/policy-defaults") ||
pathname.startsWith("/api/site-pages")
) {
return
}
if (pathname.startsWith("/admin") || pathname.startsWith("/api/users")) {
if (!req.auth) {
return redirectToSignIn(req)
}
const userRole = req.auth.user?.role
if (userRole !== "ADMIN" && userRole !== "SUPER_ADMIN") {
return redirectToSignIn(req)
}
}
})
export const config = {
runtime: "nodejs",
matcher: [
/*
* Alle Pfade außer statischen Next.js-Dateien und dem Favicon.
*/
"/((?!_next/static|_next/image|favicon.ico).*)",
],
}