Files
Speisekarte/app/api/auth/oidc/login/route.ts
T
florian.wildeandClaude Sonnet 5 32ef93ae8e feat: SSO-Login per OpenID Connect (Authentik u.a.)
Dritte Login-Methode neben Passwort und Passkey, konfiguriert über
OIDC_ISSUER/OIDC_CLIENT_ID/OIDC_CLIENT_SECRET (optional, Button bleibt
ohne diese Vars ausgeblendet). Authorization-Code-Flow mit PKCE,
ID-Token-Signaturprüfung gegen das JWKS des Providers via neuer
Abhängigkeit "jose" (RS256 fest gepinnt gegen Algorithm-Confusion),
State/Nonce kurzlebig und einmalig verwendbar in neuer Tabelle
oidc_states (Migration 0003, analog zu webauthn_challenges).

Funktioniert ausschließlich für bereits angelegte Benutzer (Abgleich
per E-Mail) – kein Auto-Provisioning, keine Rollenübernahme aus
Claims, beides bleibt exklusiv über die bestehende Benutzerverwaltung
steuerbar.

app/login/page.tsx zu Server-Component-Wrapper reduziert (prüft
isConfigured(), liest ?error= aus searchParams), bisheriger Inhalt
nach components/LoginForm.tsx verschoben.

Version 1.9.0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 00:29:57 +02:00

32 lines
1.3 KiB
TypeScript
Raw 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 { createHash, randomBytes } from "node:crypto";
import { NextResponse } from "next/server";
import { isConfigured } from "@/lib/auth/oidc-config";
import { storeState } from "@/lib/auth/oidc-state";
import { buildAuthorizeUrl, discover } from "@/lib/auth/oidc-client";
// Bewusst ein einfacher GET-Link statt eines CSRF-gesicherten POSTs:
// der Browser muss hier auf die fremde Login-Seite des Identity
// Providers navigieren (funktioniert nicht per fetch/XHR) – identisch
// zum Muster jeder "Login mit X"-Schaltfläche. Die Absicherung gegen
// Cross-Site-Missbrauch übernimmt der state-Parameter im Callback.
export async function GET(request: Request) {
if (!isConfigured()) {
return NextResponse.json({ error: "oidc_not_configured" }, { status: 404 });
}
const state = randomBytes(32).toString("base64url");
const nonce = randomBytes(32).toString("base64url");
const codeVerifier = randomBytes(32).toString("base64url");
const codeChallenge = createHash("sha256").update(codeVerifier).digest("base64url");
try {
const discovery = await discover();
await storeState(state, nonce, codeVerifier);
return NextResponse.redirect(
buildAuthorizeUrl(discovery, { state, nonce, codeChallenge })
);
} catch {
return NextResponse.redirect(new URL("/login?error=oidc_failed", request.url));
}
}