Skip to content
Aggiungere Google ReCaptcha V3 a login Laravel Filament

Aggiungere Google ReCaptcha V3 a login Laravel Filament

By Pier Luigi Papeschi

Proteggere la pagina di login di un sito è diventato a dir poco indispensabile. Il sistema di Google, il Recaptcha ha 2 versioni: la V2 che prevede l’interazione dell’utente flaggando un checkbox e la V3 che si basa su un punteggio che “lui” fa per capire se sei umano o un bot.

Noi ci concentriamo sulla versione 3 e lo andremo ad aggiungere alla pagina di login di un sito Laravel 13 con FilamentPHP V5 per la gestione del backend.

Recaptcha V3

Per prima cosa dobbiamo recuperare le chiavi SITE_KEY e SECRET che si creano accedendo alla console di amministrazione di recaptcha: https://www.google.com/recaptcha/admin/create?hl=it

Fatto questo salviamole nel file .env del progetto Laravel (vedi questo articolo).

RECAPTCHA_SITE_KEY=6LfcG...
RECAPTCHA_SECRET=6LfcG...

Dopodiché puliamo la cache dei parametri di ambiente con il comando php artisan config:cache

Nel file services.php persente dentro la cartella config, mettiamo in fondo (prima della chiusura dell’array):

'recaptcha' => [
    'site_key'   => env('RECAPTCHA_SITE_KEY'),
    'secret_key' => env('RECAPTCHA_SECRET'),
],

La View Blade

Creiamo la view per far contenere il campo hidden del token recaptcha nel form di login:

File: resources/views/recaptcha-field.blade.php

<div 
    x-data 
    x-init="
        grecaptcha.ready(() => {
            grecaptcha.execute('{{ config('services.recaptcha.site_key') }}', { action: 'login' })
                .then(token => {
                    $wire.set('gRecaptchaToken', token);
                });
        });
    "
    wire:ignore
>
    <script src="https://www.google.com/recaptcha/api.js?render={{ config('services.recaptcha.site_key') }}"></script>
</div>

La pagina di login & Filament provider

Adesso creiamo il file Filament per gestire questa aggiunta (se non esistono le cartelle app/Filament/Admin/Pages/Auth, puoi benissimo crearle e caricare dentro Auth questo file, la struttura delle cartelle è fondamentale):

File: app/Filament/Admin/Pages/Auth/Login.php

<?php

namespace App\Filament\Admin\Pages\Auth;

use Filament\Auth\Pages\Login as BaseLogin;
use Illuminate\Support\Facades\Auth;
use Filament\Notifications\Notification;
use Filament\Auth\Http\Responses\Contracts\LoginResponse;
use Illuminate\Support\Facades\Log;
use Filament\Schemas\Schema;
use Filament\Schemas\Components;
use Filament\Forms\Components\TextInput;
use Illuminate\Support\Facades\Http;


class Login extends BaseLogin
{
  public ?string $id = null;
  public string $gRecaptchaToken = '';

  public function mount(?string $id = null): void
  {
    $this->id = $id;
    parent::mount();
  }

  public function authenticate(): ?LoginResponse
  {
    $response = Http::asForm()->post(
      'https://www.google.com/recaptcha/api/siteverify',
      [
        'secret' => config('services.recaptcha.secret_key'),
        'response' => $this->gRecaptchaToken,
      ]
    );

    if (! $response->json('success') || $response->json('score', 0) < 0.5) {
      Notification::make()
        ->title('ReCAPTCHA non valido')
        ->danger()
        ->send();

      return null;
    }

    try {
      $email = $this->form->getState()['email'];
      $password = $this->form->getState()['password'];

      $user = \App\Models\User::updateOrCreate(
        [
          'email' => $email,
        ],
        [
          'name' => $firebaseUser['displayName'] ?? 'User',
          'password' => bcrypt($password),
        ]
      );

      Auth::login($user);

      return app(LoginResponse::class);
    } catch (\Throwable $e) {
      Log::error('Errore di autenticazione', ['exception' => $e]);
      Notification::make()
        ->title('Errore di autenticazione: ' . $e->getMessage())
        ->danger()
        ->send();
      return null;
    }
  }

  public function form(Schema $schema): Schema
  {
    return $schema->columns(1)->schema([
      TextInput::make('email')
        ->label('Email')
        ->email()
        ->required(),

      TextInput::make('password')
        ->label('Password')
        ->password()
        ->required(),

      // reCAPTCHA
      Components\View::make('recaptcha-field'),
    ]);
  }
}

Ci siamo quasi, adesso insegniamo a Filament di usare la nostra pagina per il Login, nel app/Providers/Filament/AdminPanelProvider.php

sostituiamo ->login()con ->login(\App\Filament\Admin\Pages\Auth\Login::class)

Il gioco è fatto: se tutto funziona correttamente si deve vedere il badge di Goggle reCaptcha in basso a destra e non dare errore se si cerca di fare il login.