All checks were successful
Container-Image bauen und veröffentlichen / build-and-push (push) Successful in 5m15s
91 lines
2.7 KiB
TypeScript
91 lines
2.7 KiB
TypeScript
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).*)",
|
||
],
|
||
}
|