RahulDey12 / laravel-captcha

Cloudflare Turnstile, Hcaptcha & Google ReCaptcha Solution for Laravel Framework.
https://laravel-captcha.rahuldey.dev
MIT License
102 stars 2 forks source link

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

Closed nexxai closed 8 months ago

nexxai commented 9 months ago

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.

RahulDey12 commented 8 months ago

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

nexxai commented 8 months ago

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