presseportale/app/Http/Responses/RoleAwareLoginResponse.php
Kevin Adametz 1ccb4a8479 auth/security: API-Firmenzuordnung nur für Mitglieder oder Allowlist, Google-Vorabübernahme geschlossen, gemeinsamer Login-Abschluss (Umsetzungsaudit 29.09.: R-01, K-04, K-07, K-08, B-10)
- R-01: ApiNewsroomAssignment hängt an eine bestehende Firma nur noch Owner,
  Mitglieder oder Nutzer aus NEWSROOM_API_ASSIGNMENT_USER_IDS; Pivot mit
  granted_via = api; Befehl company-memberships:api-assignments (Liste,
  --remove mit Vorschau)
- K-04: OAuth-Verifikation eines offenen Kontos verwirft Passwort,
  remember_token und Sanctum-Tokens; SessionRevocation + Middleware
  EnsureSessionIsNotRevoked widerruft alte Sessions (Redis-tauglich)
- K-08: LoginCompletion für Passwort, Magic Link und Google — last_login_at
  und Anmeldemethode auch nach 2FA, Landeziel über die Challenge, Google
  ohne erzwungenes remember, magic-login ohne guest für eigene Links
- B-10: eigener Magic-Link-Zweck image_confirmation (Migration)
- K-07: legalAcceptanceVersions #[Locked], Versionswechsel beim Absenden
  wird abgelehnt, Gate-Abfragen je Request zwischengespeichert

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:09:00 +00:00

68 lines
2.7 KiB
PHP
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.

<?php
namespace App\Http\Responses;
use App\Services\Auth\LoginCompletion;
use App\Support\LoginRedirect;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Laravel\Fortify\Contracts\LoginResponse as LoginResponseContract;
use Laravel\Fortify\Contracts\TwoFactorLoginResponse as TwoFactorLoginResponseContract;
/**
* Einheitliche Antwort für den Fortify-POST-Login (HTML und JSON/XHR) UND den
* Abschluss der 2FA-Challenge. Spiegelt dieselbe Policy wie der Volt-Login:
* - verifiziert, aber inaktiv → Session beenden, KEIN Login (auch für JSON)
* - unverifiziert → Verifizierungs-Notice (JSON: 204, verified-Middleware schützt)
* - sonst rollengerechter, 403-sicherer Redirect (intended nur wenn erreichbar)
*
* Wichtig: Die Sicherheitsprüfungen laufen VOR dem JSON-Kurzschluss, damit ein
* XHR-Login keine Session für einen deaktivierten Account erhält.
*/
class RoleAwareLoginResponse implements LoginResponseContract, TwoFactorLoginResponseContract
{
public function toResponse($request): RedirectResponse|JsonResponse
{
$user = $request->user();
$wantsJson = $request instanceof Request && $request->wantsJson();
// Sicherheits-Boundary zuerst: ein verifizierter, aber deaktivierter
// Account darf keine Session behalten – egal ob HTML oder JSON.
if ($user && $user->hasVerifiedEmail() && ! $user->is_active) {
Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
$message = __('Ihr Konto ist nicht aktiv. Bitte wenden Sie sich an den Support.');
return $wantsJson
? new JsonResponse(['message' => $message], 403)
: redirect()->route('login')->withErrors(['email' => $message]);
}
// Unverifizierte Selbst-Registrierer sind authentifiziert, aber die
// verified-Middleware sperrt geschützte Routen. HTML → Notice, JSON → 204.
if ($user && ! $user->hasVerifiedEmail()) {
return $wantsJson
? new JsonResponse('', 204)
: redirect()->route('verification.notice');
}
// Login-Abschluss auch nach der 2FA-Challenge (Audit K-08) — die
// direkten Wege setzen ihn in LoginCompletion::login().
if ($user) {
app(LoginCompletion::class)->recordLogin($user, $request);
}
if ($wantsJson) {
return new JsonResponse('', 204);
}
$default = LoginRedirect::homeFor($user);
$intended = $request->session()->pull('url.intended');
return redirect(LoginRedirect::safeTarget($user, $intended, $default));
}
}