# Adding Google reCAPTCHA v2 to Your Laravel Fortify Registration Form (No Packages)

> Author: [Oussama Ghaieb](https://oussama.ghaieb.com/about-me)
> Canonical: https://oussama.ghaieb.com/posts/adding-google-recaptcha-v2-to-your-laravel-fortify-registration-form-no-packages

# <span class="hover:bg-yellow-100">Adding Google reCAPTCHA v2 to Your Laravel Fortify Registration Form (No Packages)</span>

If you’re using Laravel Fortify to handle authentication in your app and want to secure your registration form against bots, Google reCAPTCHA v2 is a fantastic tool. In this guide, I’ll walk you through adding the reCAPTCHA v2 Checkbox ("I’m not a robot") to your Fortify-powered registration form without using any external packages. We’ll leverage Laravel’s built-in features and a custom validation rule. Let’s get started!

## Why reCAPTCHA with Fortify?
Fortify provides a clean, modern authentication system for Laravel, and adding reCAPTCHA ensures your registration process stays spam-free. Doing it without packages gives you flexibility and keeps your project lean.

## Prerequisites
- A Laravel project with Fortify installed (`php artisan fortify:install` and `npm run dev` already run).
- A Google reCAPTCHA account for API keys.
- Basic familiarity with Laravel Blade and Fortify’s structure.

---

### Step 1: Get Your reCAPTCHA Keys
First, you need API keys from Google.

1. Head to the [Google reCAPTCHA admin console](https://www.google.com/recaptcha/admin).
2. Click “+” to register a new site.
3. Configure it:
   - **Label**: E.g., “My Laravel Fortify App.”
   - **reCAPTCHA type**: Choose “reCAPTCHA v2” > “Checkbox” ("I’m not a robot").
   - **Domains**: Add `localhost` for testing or your production domain.
4. Submit, and you’ll receive:
   - **Site Key**: For the frontend.
   - **Secret Key**: For backend validation.

Keep these safe—we’ll use them next.

---

### Step 2: Store Keys in Your Environment
Add your keys to your `.env` file for security:
```
RECAPTCHA_SITE_KEY=YOUR_SITE_KEY_HERE
RECAPTCHA_SECRET_KEY=YOUR_SECRET_KEY_HERE
```

Then, update `config/services.php` to make them accessible:
```php
return [
    // Existing config...
    'recaptcha' => [
        'site_key' => env('RECAPTCHA_SITE_KEY'),
        'secret_key' => env('RECAPTCHA_SECRET_KEY'),
    ],
];
```

This keeps your credentials secure and configurable.

---

### Step 3: Add reCAPTCHA to the Registration Form
Fortify’s registration view is typically at `resources/views/auth/register.blade.php`. Let’s add the reCAPTCHA widget.

1. Include the reCAPTCHA script in your guest layout (`resources/views/layouts/guest.blade.php`). Add this just before the closing `</body>` tag:
```html
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
```

2. Update `resources/views/auth/register.blade.php` to include the reCAPTCHA field. Insert this before the submit button section:
```html
<!-- Google Recaptcha -->
<div class="g-recaptcha mt-4" data-sitekey="{{ config('services.recaptcha.site_key') }}"></div>
```

Your full form might look something like this (partial excerpt):
```html
<form method="POST" action="{{ route('register') }}">
    @csrf

    <!-- Name, Email, Password fields... -->

    @if(Jetstream::hasTermsAndPrivacyPolicyFeature())
        <div class="mt-4">
            <!-- Terms checkbox -->
        </div>
    @endif

    <!-- Google Recaptcha -->
    <div class="g-recaptcha mt-4" data-sitekey="{{ config('services.recaptcha.site_key') }}"></div>

    <div class="flex items-center justify-end mt-4">
        <a class="underline text-sm text-gray-600" href="{{ route('login') }}">
            {{ __('Already registered?') }}
        </a>
        <button type="submit" class="ml-4 bg-indigo-600 text-white">
            {{ __('Register') }}
        </button>
    </div>
</form>
```

This adds the “I’m not a robot” checkbox to your form.

---

### Step 4: Create a Custom reCAPTCHA Validation Rule
Since we’re not using a package, we’ll create a custom rule to validate the reCAPTCHA response. Run this Artisan command to generate it:
```bash
php artisan make:rule Recaptcha
```

Update `app/Rules/Recaptcha.php` with the following:
```php
namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Http;

class Recaptcha implements Rule
{
    public function passes($attribute, $value)
    {
        $response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
            'secret' => config('services.recaptcha.secret_key'),
            'response' => $value,
            'remoteip' => request()->ip(), // Optional
        ]);

        return $response->json()['success'] ?? false;
    }

    public function message()
    {
        return 'reCAPTCHA verification failed. Please try again.';
    }
}
```

This rule:
- Sends the user’s reCAPTCHA response to Google’s API.
- Checks if `success` is `true`.
- Returns a custom error message if it fails.

---

### Step 5: Validate reCAPTCHA in Fortify
Fortify handles user creation in `app/Actions/Fortify/CreateNewUser.php`. Update the `create` method to include reCAPTCHA validation:
```php
namespace App\Actions\Fortify;

use App\Models\Team;
use App\Models\User;
use App\Rules\Recaptcha;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\CreatesNewUsers;
use Laravel\Jetstream\Jetstream;

class CreateNewUser implements CreatesNewUsers
{
    use PasswordValidationRules;

    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(),
            'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature() ? ['accepted', 'required'] : '',
            'g-recaptcha-response' => ['required', new Recaptcha()],
        ])->validate();

        return DB::transaction(function () use ($input) {
            return tap(User::create([
                'name' => $input['name'],
                'email' => $input['email'],
                'password' => Hash::make($input['password']),
            ]), function (User $user) {
                $this->createTeam($user);
            });
        });
    }

    protected function createTeam(User $user)
    {
        // Default team creation logic...
    }
}
```

Key changes:
- Added `'g-recaptcha-response' => ['required', new Recaptcha()]` to the validation rules.
- The `Recaptcha` rule ensures the response is valid before creating the user.

---

### Step 6: Test It Out
- Run your app (`php artisan serve`).
- Visit `/register`.
- Verify the reCAPTCHA checkbox appears.
- Test submitting the form:
  - Without checking: Should fail with “The g-recaptcha-response field is required.”
  - With a valid check: Should register the user if all other fields are valid.

---

### Troubleshooting
- **reCAPTCHA not showing?** Check your Site Key and ensure the script loads.
- **Validation fails?** Confirm your Secret Key and test with `localhost` in your Google reCAPTCHA domains.
- **Errors?** Use `dd($response->json())` in the `Recaptcha` rule to debug Google’s response.

---

### Wrapping Up
You’ve successfully added reCAPTCHA v2 to your Laravel Fortify registration form without packages! By using a custom validation rule and Fortify’s extensible actions, you’ve kept your app secure and dependency-free. Bots will think twice before spamming your registration page now.

<!-- Questions or need a tweak? Let me know in the comments—I’m here to help. Happy coding! -->
