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>
This commit is contained in:
Kevin Adametz 2026-10-02 13:09:00 +00:00
parent 3dbc3fa673
commit 1ccb4a8479
29 changed files with 1287 additions and 73 deletions

View file

@ -3,12 +3,11 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Listeners\RecordAuthActivity;
use App\Services\Auth\LoginCompletion;
use App\Services\Auth\SocialAuthService;
use App\Support\LoginRedirect;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;
/**
@ -23,7 +22,7 @@ class GoogleController extends Controller
return Socialite::driver('google')->redirect();
}
public function callback(Request $request, SocialAuthService $social): RedirectResponse
public function callback(Request $request, SocialAuthService $social, LoginCompletion $completion): RedirectResponse
{
try {
$googleUser = Socialite::driver('google')->user();
@ -49,25 +48,16 @@ class GoogleController extends Controller
// 2FA aktiv: wie Passwort- und Magic-Link-Login nicht direkt einloggen,
// sondern in Fortifys Challenge übergeben (Audit F02). Google belegt
// den Besitz des Postfachs, nicht den zweiten Faktor.
//
// Kein erzwungenes „Angemeldet bleiben" mehr (Audit K-08): wie beim
// Magic Link nur die Sitzung, siehe LoginCompletion.
if ($user->hasEnabledTwoFactorAuthentication()) {
$request->session()->put([
'login.id' => $user->getKey(),
'login.remember' => true,
]);
$request->session()->regenerate();
$completion->handOffToTwoFactor($request, $user, 'google');
return redirect()->route('two-factor.challenge');
}
$request->attributes->set(RecordAuthActivity::AUTH_METHOD_ATTRIBUTE, 'google');
Auth::login($user, remember: true);
$request->session()->regenerate();
$user->forceFill([
'last_login_at' => now(),
'last_login_ip' => $request->ip(),
])->save();
$completion->login($request, $user, 'google');
return redirect(LoginRedirect::safeTarget(
$user,