Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

nexxai's avatar
Level 37

Issue with validating Turnstile captcha on login only using Fortify and Laravel-Captcha; register works fine

Ok I've got a very weird problem here that I cannot figure out. I'm using Rahul Dey's Laravel-Captcha package on my site. The captcha is validated correctly 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.

0 likes
4 replies
nexxai's avatar
Level 37

To add more information, I went into the actual package files and DD'd out the response that's being returned from Cloudflare, and the response is success => true, so this is definitely an issue with my code or how Fortify is validating the $request parameters.

nexxai's avatar
Level 37

Even more information...

So I tried creating a Form Request but extending Fortify's LoginRequest class, but when I try and pass that in to the authenticateUsing() in the FortifyServiceProvider, it's saying that it must be a literal Fortify LoginRequest, not one that simply extends their class.

Error:

App\Providers\FortifyServiceProvider::App\Providers\{closure}(): Argument #1 ($request) must be of type App\Http\Requests\FortifyLoginRequest, Laravel\Fortify\Http\Requests\LoginRequest given

App\Http\Requests\FortifyLoginRequest:

<?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:

use App\Http\Requests\FortifyLoginRequest;

...

 		Fortify::authenticateUsing(function (FortifyLoginRequest $request) {
            $user = User::where('email', $request->email)->first();

            if ($user && Hash::check($request->password, $user->password)) {
                return $user;
            }
        });
nexxai's avatar
Level 37

Ok, now I am extremely confused.

As a test, I added the Captcha::getResponseName() => ['required', 'captcha'] validator to the rules() array of the Fortify package's own LoginRequest class, and it worked.

#1: Why?

#2: This is obviously not good since those changes won't make it to my site since they're being made in the /vendor folder, so how do I make a validator that works with Fortify and that can correctly validate those captchas?

nexxai's avatar
nexxai
OP
Best Answer
Level 37

Ok, so thanks to the Laravel Discord, someone suggest that I bind my request class to the container as part of the FortifyServiceProvider. Once I did that, I was able to correctly use my own FortifyLoginRequest class that extended Fortify's LoginRequest. Now I can use the validator provided in the laravel-captcha package to validate the Cloudflare Turnstile captchas when someone tries to login.

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>
    }
}

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;
    }

    /** @phpstan-ignore-next-line */
    public function rules(): array
    {
        return [
            Fortify::username() => ['required', 'string'],
            'password' => ['required', 'string'],
            Captcha::getResponseName() => ['required', 'captcha'],
        ];
    }
}

Please or to participate in this conversation.