|string> */ public function rules(): array { return [ 'email' => ['required', 'string'], 'password' => ['required', 'string'], ]; } /** * Attempt to authenticate the request's credentials. * * @throws \Illuminate\Validation\ValidationException */ public function authenticate(): void { $this->ensureIsNotRateLimited(); $identifier = strtolower(trim((string) $this->input('email'))); $password = (string) $this->input('password'); $user = User::query() ->whereRaw('LOWER(email) = ?', [$identifier]) ->first(); $authenticatedVia = 'email'; if (! $user) { $candidate = User::query() ->whereRaw('LOWER(username) = ?', [$identifier]) ->first(); if ($candidate?->supportsUsernameLogin()) { $user = $candidate; $authenticatedVia = 'username'; } } if (! $user || ! Hash::check($password, (string) $user->password)) { RateLimiter::hit($this->throttleKey()); app(AuthAuditLogger::class)->log( eventType: 'login', request: $this, status: 'failed', reason: 'invalid_credentials', identifier: $identifier, user: $user, metadata: ['via' => $authenticatedVia] ); throw ValidationException::withMessages([ 'email' => trans('auth.failed'), ]); } Auth::login($user, $this->boolean('remember')); $this->authenticatedUser = $user; $this->authenticatedVia = $authenticatedVia; RateLimiter::clear($this->throttleKey()); } public function authenticatedUser(): ?User { return $this->authenticatedUser; } public function authenticatedViaUsername(): bool { return $this->authenticatedVia === 'username'; } protected function failedValidation(Validator $validator): void { app(AuthAuditLogger::class)->log( eventType: 'login', request: $this, status: 'failed', reason: 'validation_failed', identifier: (string) $this->input('email'), metadata: ['fields' => array_keys($validator->errors()->toArray())] ); parent::failedValidation($validator); } /** * Ensure the login request is not rate limited. * * @throws \Illuminate\Validation\ValidationException */ public function ensureIsNotRateLimited(): void { if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { return; } event(new Lockout($this)); $seconds = RateLimiter::availableIn($this->throttleKey()); app(AuthAuditLogger::class)->log( eventType: 'login', request: $this, status: 'failed', reason: 'rate_limited', identifier: (string) $this->input('email'), metadata: ['seconds' => $seconds] ); throw ValidationException::withMessages([ 'email' => trans('auth.throttle', [ 'seconds' => $seconds, 'minutes' => ceil($seconds / 60), ]), ]); } /** * Get the rate limiting throttle key for the request. */ public function throttleKey(): string { return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip()); } }