'Willkommen zurück', 'eyebrow' => 'Anmeldung im Publisher-Hub', 'topRightLabel' => 'Noch kein Konto?', 'topRightLinkText' => 'Konto erstellen', 'topRightLinkHref' => '/register'])] class extends Component { #[Validate('required|string|email')] public string $email = ''; #[Validate('required|string')] public string $password = ''; public bool $remember = false; /** * Handle an incoming authentication request. */ public function login(): void { $this->validate(); $this->ensureIsNotRateLimited(); if (! Auth::attempt(['email' => $this->email, 'password' => $this->password], $this->remember)) { RateLimiter::hit($this->throttleKey()); throw ValidationException::withMessages([ 'email' => __('auth.failed'), ]); } $authenticatedUser = Auth::user(); if ($authenticatedUser) { $authenticatedUser->update([ 'last_login_at' => now(), 'last_login_ip' => request()->ip(), ]); } RateLimiter::clear($this->throttleKey()); Session::regenerate(); // Rollen-basierter Default-Redirect: // Admin/Editor → /dashboard, Customer → /admin/me. // Ohne navigate:true, weil das Portal ein anderes Vite-Bundle nutzt // (build/portal mit FluxUI) als das Hub-Auth-Layout (build/web). // SPA-Navigation kann den Bundle-Wechsel nicht handhaben. $defaultRoute = $authenticatedUser?->canAccessAdmin() ? route('dashboard', absolute: false) : ($authenticatedUser?->canAccessCustomer() ? route('me.dashboard', absolute: false) : '/'); $this->redirectIntended(default: $defaultRoute); } public function sendMagicLink(): void { $this->validateOnly('email'); $user = User::query()->where('email', $this->email)->first(); if ($user && $user->is_active) { $generated = app(MagicLinkGenerator::class)->createLoginLink($user, request()->ip()); $loginUrl = route('magic-links.consume', ['token' => $generated['plain_token']]); Mail::to($user->email)->send( new MagicLoginLink( user: $user, loginUrl: $loginUrl, expiresAt: $generated['expires_at']->format('d.m.Y H:i') ) ); } session()->flash('status', __('If an active account exists for this email, we sent a magic login link.')); } /** * Ensure the authentication request is not rate limited. */ protected function ensureIsNotRateLimited(): void { if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { return; } event(new Lockout(request())); $seconds = RateLimiter::availableIn($this->throttleKey()); throw ValidationException::withMessages([ 'email' => __('auth.throttle', [ 'seconds' => $seconds, 'minutes' => ceil($seconds / 60), ]), ]); } /** * Get the authentication rate limiting throttle key. */ protected function throttleKey(): string { return Str::transliterate(Str::lower($this->email).'|'.request()->ip()); } }; ?>