-
app/Rules/Recaptcha.php
Open in GitHubuse Closure; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Http; use Symfony\Component\HttpFoundation\IpUtils; final readonly class Recaptcha implements ValidationRule { private const string URL = 'https://www.google.com/recaptcha/api/siteverify'; public function __construct(private ?string $ip) { // } public function validate(string $attribute, mixed $value, Closure $fail): void { $value = type($value)->asString(); assert(is_string($this->ip)); if (! $this->verify($this->ip, $value)) { $fail(__('The recaptcha response was invalid.')); } } public function verify(string $ipAddress, string $response): bool { $payload = [ 'secret' => config()->string('services.recaptcha.secret'), 'response' => $response, 'remoteip' => IpUtils::anonymize($ipAddress), ]; $response = Http::asForm()->post(self::URL, $payload); /** @var array{success: bool}|null $result */ $result = $response->json(); return $response->successful() && ! is_null($result) && $result['success'] === true; } }
-
app/Http/Controllers/Auth/RegisteredUserController.php
Open in GitHubuse App\Models\User; use App\Rules\Recaptcha; use App\Rules\UnauthorizedEmailProviders; use App\Rules\Username; use Illuminate\Http\Request; use Illuminate\Validation\Rules; final readonly class RegisteredUserController { // ... public function store(Request $request): RedirectResponse { $request->validate([ 'name' => ['required', 'string', 'max:255'], 'username' => ['required', 'string', 'min:4', 'max:50', 'unique:'.User::class, new Username], 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class, new UnauthorizedEmailProviders()], 'password' => ['required', 'confirmed', Rules\Password::defaults()], 'terms' => ['required', 'accepted'], 'g-recaptcha-response' => app()->environment('production') ? ['required', new Recaptcha($request->ip())] : [], ]); // ... } }