Ctrl K
ComunidadGuías…

app/Providers/FortifyServiceProvider.php

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
<?php

namespace App\Providers;

use App\Actions\Fortify\CreateNewUser;
use App\Actions\Fortify\ResetUserPassword;
use App\Actions\Fortify\UpdateUserPassword;
use App\Actions\Fortify\UpdateUserProfileInformation;
use App\Http\Responses\NeutralPasswordResetLinkRequestResponse;
use Artwork\Modules\ExternalUserManagement\Repository\ExternalUserSourceRepository;
use Artwork\Modules\ExternalUserManagement\Service\CredentialLoginService;
use Artwork\Modules\GeneralSettings\Models\GeneralSettings;
use Artwork\Modules\User\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Config\Repository;
use Illuminate\Http\Request;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Inertia\Inertia;
use Laravel\Fortify\Fortify;
use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse;
use Laravel\Fortify\Contracts\LoginResponse;

class FortifyServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        //
    }

    public function boot(): void
    {
        // @phpstan-ignore-next-line
        Fortify::createUsersUsing(CreateNewUser::class);
        Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
        Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
        Fortify::resetUserPasswordsUsing(ResetUserPassword::class);

        // Einziger Credential-Pfad des Passwortformulars: lokaler Hash für lokale
        // Accounts, Bind gegen das Directory für LDAP, Sperre für OIDC (nur SSO).
        Fortify::authenticateUsing(function (Request $request) {
            return app(CredentialLoginService::class)->attempt(
                (string) $request->input(Fortify::username()),
                (string) $request->input('password')
            );
        });

        // Login-View mit aktiven Identity-Provider-Quellen für die OIDC/SSO-Buttons anreichern.
        Fortify::loginView(function () {
            return Inertia::render('Auth/Login', [
                'canResetPassword' => Route::has('password.request'),
                'status' => session('status'),
                'identityProviderSources' => rescue(
                    fn () => app(ExternalUserSourceRepository::class)->getActiveIdentityProviderSourcesForLogin(),
                    collect(),
                    false
                ),
            ]);
        });

        RateLimiter::for('login', function (Request $request) {
            // Läuft vor der Request-Validierung: Bots posten "email" auch als Array
            $email = $request->input(Fortify::username());
            $email = is_string($email) ? $email : '';

            return [
                Limit::perMinute(5)->by($email . $request->ip()),
                // Pro Konto unabhängig von der IP: bremst Password-Spraying über viele Adressen.
                // Höchstens 15 Minuten Wartezeit, damit ein Angreifer ein Konto nicht lange sperren kann.
                Limit::perMinutes(15, 10)->by('account|' . Str::lower(trim($email))),
            ];
        });

        RateLimiter::for('two-factor', function (Request $request) {
            return Limit::perMinute(5)->by($request->session()->get('login.id'));
        });

        RateLimiter::for('password-reset', function (Request $request) {
            $email = $request->input(Fortify::email());
            $email = is_string($email) ? Str::lower(trim($email)) : '';

            return Limit::perMinute(5)->by($email . '|' . $request->ip());
        });

        // Fortify registriert password.email ohne Throttle; die Route existiert erst nach dem Booten.
        $this->app->booted(function (): void {
            Route::getRoutes()->getByName('password.email')?->middleware('throttle:password-reset');
        });

        // Unbekannte E-Mail antwortet wie der Erfolgsfall (keine Nutzer-Enumeration).
        $this->app->singleton(
            FailedPasswordResetLinkRequestResponse::class,
            NeutralPasswordResetLinkRequestResponse::class
        );

        ResetPassword::toMailUsing(
            function (User $notifiable, string $token) {
                /** @var GeneralSettings $settings */
                $settings = $this->app->make(GeneralSettings::class);
                $config = $this->app->make(Repository::class);
                $fallbackSenderMail = $config->get('mail.system_mail');
                $pageTitle = $settings->page_title !== '' ?
                    $settings->page_title :
                    $config->get('mail.fallback_page_title');

                return (new MailMessage())
                    ->from(
                        $settings->business_email !== '' ? $settings->business_email : $fallbackSenderMail,
                        $pageTitle
                    )
                    ->subject(__('Reset password'))
                    ->markdown(
                        'emails.password_reset',
                        [
                            'name' => $notifiable->first_name . ' ' . $notifiable->last_name,
                            'page_title' => $settings->page_title !== '' ? $settings->page_title : 'Artwork',
                            'url' => sprintf(
                                '%s/reset-password/%s?email=%s',
                                $config->get('app.url'),
                                $token,
                                urlencode($notifiable->email)
                            )
                        ]
                    );
            }
        );

        // Custom Login Response für Inertia.js - erzwingt Full-Page-Reload nach Login
        $this->app->singleton(LoginResponse::class, function () {
            return new class implements LoginResponse {
                public function toResponse($request)
                {
                    $intended = redirect()->intended(RouteServiceProvider::HOME)->getTargetUrl();

                    // Für Inertia-Requests: Force Full-Page-Reload
                    if ($request->header('X-Inertia')) {
                        return response('', 409)
                            ->header('X-Inertia-Location', $intended);
                    }

                    return redirect()->intended(RouteServiceProvider::HOME);
                }
            };
        });
    }
}