RahulDey12/laravel-captcha

Issue with validating the captcha on login only using Fortify; register works fine

Closed this issue · 2 comments

Ok I've got a very weird problem here that I cannot figure out. The captcha is validated on the user registration form no problem, but the exact same snippet on the login form fails to validate. I'm sure it's something I'm doing wrong but I would really appreciate a second set of eyes.

FortifyServiceProvider:

    public function boot(): void
    {
        Fortify::createUsersUsing(CreateNewUser::class);

...

        Fortify::authenticateUsing(function (Request $request) {
            Validator::make($request->all(), [
                'email' => ['required', 'string', 'email', 'max:255'],
                Captcha::getResponseName() => ['required', 'captcha'],            // <--- Never validates
            ])->validate();

...

    }

CreateNewUser (this works fine):

    public function create(array $input): User
    {
        Validator::make($input, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => $this->passwordRules(),
            Captcha::getResponseName() => ['required', 'captcha'],            // <--- Always validates
            'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature() ? ['accepted', 'required'] : '',
        ])->validate();

Both import the Rahul900day\Captcha\Facades\Captcha; and both have the <x-captcha-container /> element inside their form tags. They both inherit the same layout injecting the JS, and if you dd() the $input / $request, the correct cf-turnstile-response shows up.

I feel like I'm missing something very stupid, but I can't see what.

@nexxai do you have an example of it? By the way extremely sorry for the late response I have been busy with other things.

Shoot, sorry about this.

I ended up solving it by extending the FortifyLoginClass class and implementing the validation there, and then bound my class in the FortifyServiceProvider instead of the package-provided one. For anyone who reads this in the future, this is the entire class I created.

app/Http/Requests/FortifyLoginRequest.php

<?php

namespace App\Http\Requests;

use Laravel\Fortify\Fortify;
use Laravel\Fortify\Http\Requests\LoginRequest;
use Rahul900day\Captcha\Facades\Captcha;

class FortifyLoginRequest extends LoginRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            Fortify::username() => ['required', 'string'],
            'password' => ['required', 'string'],
            Captcha::getResponseName() => ['required', 'captcha'],
        ];
    }
}

FortifyServiceProvider.php

<?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\Requests\FortifyLoginRequest;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider;
use Laravel\Fortify\Fortify;
use Laravel\Fortify\Http\Requests\LoginRequest;

class FortifyServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        $this->app->bind(LoginRequest::class, FortifyLoginRequest::class);    // <- I did this
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        <snip>

        Fortify::authenticateUsing(function (FortifyLoginRequest $request) {     // <- so I could do this
            $user = User::where('email', $request->email)->first();

            if ($user && Hash::check($request->password, $user->password)) {
                return $user;
            }
        });

        <snip>
    }
}