*/ protected static array $loginRules = []; /** * Authorization rules applied before a password reset is completed. Rules * receive the user being reset + submitted input and return whether the * local reset flow may set a new password for that account. * * @var array): bool> */ protected static array $passwordResetRules = []; /** * Register an additional rule that gates password login (see $loginRules). * * @param \Closure(User, Request): bool $rule */ public static function authenticateUsingRule(\Closure $rule): void { static::$loginRules[] = $rule; } /** * Register an additional rule that gates password reset completion. * * @param \Closure(User, array): bool $rule */ public static function resetPasswordUsingRule(\Closure $rule): void { static::$passwordResetRules[] = $rule; } /** * Check whether the given user may complete the local password reset flow. * * @param array $input */ public static function canResetPassword(User $user, array $input = []): bool { foreach (static::$passwordResetRules as $rule) { if (! $rule($user, $input)) { return false; } } return true; } /** * Register any application services. */ public function register(): void { // } /** * Bootstrap any application services. */ public function boot(): void { Fortify::createUsersUsing(CreateNewUser::class); Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class); Fortify::updateUserPasswordsUsing(UpdateUserPassword::class); Fortify::resetUserPasswordsUsing(ResetUserPassword::class); Fortify::registerView(function () { return Inertia::render('Auth/Register', [ 'terms_url' => config('auth.terms_url'), 'privacy_policy_url' => config('auth.privacy_policy_url'), 'newsletter_consent' => config('auth.newsletter_consent'), ]); }); Fortify::loginView(function () { return Inertia::render('Auth/Login', [ 'canResetPassword' => Route::has('password.request'), 'status' => session('status'), ]); }); Fortify::requestPasswordResetLinkView(function () { return Inertia::render('Auth/ForgotPassword', [ 'status' => session('status'), ]); }); Fortify::resetPasswordView(function (Request $request) { return Inertia::render('Auth/ResetPassword', [ 'email' => $request->input('email'), 'token' => $request->route('token'), ]); }); Fortify::verifyEmailView(function () { return Inertia::render('Auth/VerifyEmail', [ 'status' => session('status'), ]); }); Fortify::twoFactorChallengeView(function () { return Inertia::render('Auth/TwoFactorChallenge'); }); Fortify::confirmPasswordView(function () { return Inertia::render('Auth/ConfirmPassword'); }); Fortify::authenticateUsing(function (Request $request): ?User { /** @var User|null $user */ $user = User::query() ->where('email', $request->email) ->where('is_placeholder', '=', false) ->first(); // Always run the hash check — against the real hash, or a dummy when // there is no user — so login timing is identical either way (see // ABSENT_USER_PASSWORD_HASH). Passwordless accounts (SSO-only users // have password = null) fail here, so they cannot password-login. $existingPasswordHash = $user->password ?? self::ABSENT_USER_PASSWORD_HASH; $passwordIsValid = Hash::check((string) $request->password, $existingPasswordHash); if ($user !== null && $passwordIsValid) { // Credentials are valid; now apply any registered authorization // rules (e.g. SSO enforcement may still block password login). foreach (static::$loginRules as $rule) { if (! $rule($user, $request)) { return null; } } return $user; } return null; }); RateLimiter::for('login', function (Request $request) { $throttleKey = Str::transliterate(Str::lower($request->input(Fortify::username())).'|'.$request->ip()); return Limit::perMinute(5)->by($throttleKey); }); RateLimiter::for('two-factor', function (Request $request) { return Limit::perMinute(5)->by($request->session()->get('login.id')); }); $this->app->instance(LoginResponseContract::class, new CustomLoginResponse); $this->app->instance(TwoFactorLoginResponse::class, new CustomTwoFactorLoginResponse); } }