Skip to main content

Overview

Cashify uses Laravel Breeze as its authentication foundation, providing a simple and clean implementation of login, registration, password reset, and email verification. Additionally, it supports social authentication through GitHub OAuth using Laravel Socialite.

Laravel Breeze Setup

Cashify comes with Laravel Breeze pre-configured, providing all the essential authentication features out of the box.

Installation

Breeze is already included in Cashify’s dependencies:
composer.json
{
  "require": {
    "laravel/breeze": "^2.0",
    "laravel/socialite": "^5.15"
  }
}

Authentication Routes

All authentication routes are defined in routes/auth.php:
routes/auth.php
Route::middleware('guest')->group(function () {
    // Registration
    Route::get('register', [RegisteredUserController::class, 'create'])
        ->name('register');
    Route::post('register', [RegisteredUserController::class, 'store']);

    // Login
    Route::get('login', [AuthenticatedSessionController::class, 'create'])
        ->name('login');
    Route::post('login', [AuthenticatedSessionController::class, 'store']);

    // Password Reset
    Route::get('forgot-password', [PasswordResetLinkController::class, 'create'])
        ->name('password.request');
    Route::post('forgot-password', [PasswordResetLinkController::class, 'store'])
        ->name('password.email');

    Route::get('reset-password/{token}', [NewPasswordController::class, 'create'])
        ->name('password.reset');
    Route::post('reset-password', [NewPasswordController::class, 'store'])
        ->name('password.store');

    // OAuth Social Login
    Route::get('auth/{driver}/redirect', [SocialiteSessionController::class, 'redirectToProvider'])
        ->where('driver', '(github|google)')
        ->name('socialite.redirect');
    Route::get('auth/{driver}/callback', [SocialiteSessionController::class, 'handleProviderCallback'])
        ->name('socialite.callback');
});

Route::middleware('auth')->group(function () {
    // Email Verification
    Route::get('verify-email', EmailVerificationPromptController::class)
        ->name('verification.notice');
    Route::get('verify-email/{id}/{hash}', VerifyEmailController::class)
        ->middleware(['signed', 'throttle:6,1'])
        ->name('verification.verify');

    // Logout
    Route::post('logout', [AuthenticatedSessionController::class, 'destroy'])
        ->name('logout');
});

Authentication Configuration

The authentication configuration is managed in config/auth.php:
config/auth.php
return [
    'defaults' => [
        'guard' => env('AUTH_GUARD', 'web'),
        'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => env('AUTH_MODEL', App\Models\User::class),
        ],
    ],

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

    'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
];

User Model

The User model implements email verification:
app/Models/User.php
class User extends Authenticatable implements MustVerifyEmail
{
    use HasFactory, Notifiable;

    protected $fillable = [
        'name',
        'email',
        'password',
        'provider',      // OAuth provider (github, google)
        'provider_id',   // OAuth provider user ID
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }

    // Check if user registered via OAuth
    public function isSocialiteUser()
    {
        return is_null($this->password);
    }
}

Standard Registration

Cashify includes Cloudflare Turnstile CAPTCHA protection on registration to prevent spam:
app/Http/Controllers/Auth/RegisteredUserController.php
public function store(Request $request): RedirectResponse
{
    $request->validate([
        'name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
        'password' => ['required', 'confirmed', Rules\Password::defaults()],
        'cf-turnstile-response' => ['required', 'string'],
    ]);

    // Validate Turnstile CAPTCHA
    $response = Http::asForm()->post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
        'secret' => config('services.turnstile.secret_key'),
        'response' => $request->input('cf-turnstile-response'),
        'remoteip' => $request->ip(),
    ]);

    if (!$response->json('success')) {
        throw ValidationException::withMessages([
            'cf-turnstile-response' => 'The Turnstile challenge failed. Please try again.',
        ]);
    }

    $user = User::create([
        'name' => $request->name,
        'email' => $request->email,
        'password' => Hash::make($request->password),
    ]);

    event(new Registered($user));
    Auth::login($user);

    // Initialize user's net worth
    Auth::user()->netWorth()->create([
        'net_worth' => 0,
    ]);

    return redirect(route('dashboard', absolute: false));
}
1

Configure Turnstile

Set up your Cloudflare Turnstile keys in .env:
TURNSTILE_SITE_KEY=your_site_key
TURNSTILE_SECRET_KEY=your_secret_key
2

Add to Services Config

The Turnstile configuration is automatically loaded from config/services.php:
'turnstile' => [
    'site_key' => env('TURNSTILE_SITE_KEY'),
    'secret_key' => env('TURNSTILE_SECRET_KEY'),
],

GitHub OAuth Integration

Cashify provides seamless GitHub OAuth authentication using Laravel Socialite.

Setup GitHub OAuth

1

Create GitHub OAuth App

  1. Go to GitHub Developer Settings
  2. Click “New OAuth App”
  3. Fill in the application details:
    • Application name: Cashify
    • Homepage URL: https://yourdomain.com
    • Authorization callback URL: https://yourdomain.com/auth/github/callback
  4. Click “Register application”
  5. Copy your Client ID and generate a new Client Secret
2

Configure Environment Variables

Add your GitHub OAuth credentials to .env:
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
GITHUB_REDIRECT=https://yourdomain.com/auth/github/callback
3

Update Services Configuration

The GitHub configuration is automatically loaded from config/services.php:
'github' => [
    'client_id' => env('GITHUB_CLIENT_ID'),
    'client_secret' => env('GITHUB_CLIENT_SECRET'),
    'redirect' => env('GITHUB_REDIRECT'),
],

Socialite Controller Implementation

The SocialiteSessionController handles OAuth authentication:
app/Http/Controllers/Auth/SocialiteSessionController.php
class SocialiteSessionController extends Controller
{
    public function redirectToProvider($driver)
    {
        return Socialite::driver($driver)->redirect();
    }

    public function handleProviderCallback($driver)
    {
        try {
            if (!$this->requestHasCode()) {
                return $this->handleFailedAuthentication(
                    'Authentication cancelled or failed. Please try again.'
                );
            }

            $socialiteUser = Socialite::driver($driver)->user();
            $user = $this->findOrCreateUser($driver, $socialiteUser);

            Auth::login($user);

            return redirect()->intended(route('dashboard', absolute: false));
        } catch (Exception $e) {
            return $this->handleFailedAuthentication(
                'Authentication failed. Please try again.'
            );
        }
    }

    protected function findOrCreateUser($driver, $socialiteUser)
    {
        $user = User::query()
            ->where('provider', $driver)
            ->where('provider_id', $socialiteUser->id)
            ->first();

        if (!$user) {
            $user = $this->createUser($driver, $socialiteUser);
        }

        return $user;
    }

    protected function createUser($driver, $socialiteUser)
    {
        $user = User::create([
            'provider' => $driver,
            'provider_id' => $socialiteUser->id,
            'name' => $socialiteUser->name,
            'email' => $socialiteUser->email,
        ]);

        event(new Registered($user));
        $user->markEmailAsVerified();

        // Initialize user's net worth
        $user->netWorth()->create([
            'net_worth' => 0,
        ]);

        return $user;
    }
}
Users who register via OAuth have their email automatically verified and no password set (password field is null).

Frontend Integration

The login button for GitHub OAuth:
resources/views/components/buttons/socialite-buttons.blade.php
<a href="{{ route('socialite.redirect', 'github') }}" hx-boost="false"
   class="flex items-center justify-center px-4 py-2.5 border border-black rounded-lg text-sm text-white dark:text-black bg-black dark:bg-white hover:bg-neutral-900 dark:hover:bg-neutral-200 transition-colors duration-200">
    <svg class="w-6 h-6 mr-2" fill="currentColor" viewBox="0 0 24 24">
        <path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd"/>
    </svg>
    Continue with GitHub
</a>

Email Verification

Cashify implements Laravel’s email verification feature:
class User extends Authenticatable implements MustVerifyEmail
{
    // Implementation
}
1

Configure Mail Service

Set up your mail service in .env (see Environment Variables):
MAIL_MAILER=mailtrap
MAILTRAP_API_KEY=your_api_key
MAIL_FROM_ADDRESS=[email protected]
2

Apply Verification Middleware

Protect routes that require verified emails:
Route::middleware(['auth', 'verified'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index'])
        ->name('dashboard');
});

Password Reset

Password reset functionality is built-in with Laravel Breeze:
1

User Requests Reset

User visits /forgot-password and enters their email address.
2

Email Sent

A password reset link is sent to the user’s email address.
3

Reset Password

User clicks the link and sets a new password at /reset-password/{token}.
Password reset tokens expire after 60 minutes (configurable in config/auth.php).

Session Management

Cashify uses database-backed sessions for security and scalability:
.env
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
To create the sessions table:
php artisan session:table
php artisan migrate

Security Features

CSRF Protection

All forms include CSRF tokens:
<form method="POST" action="{{ route('login') }}">
    @csrf
    <!-- Form fields -->
</form>

Rate Limiting

Authentication endpoints are rate-limited:
Route::middleware('throttle:6,1')->group(function () {
    // Email verification
    Route::get('verify-email/{id}/{hash}', VerifyEmailController::class)
        ->name('verification.verify');
});

Password Hashing

Passwords are automatically hashed using bcrypt:
protected function casts(): array
{
    return [
        'password' => 'hashed',
    ];
}
Configure bcrypt rounds in .env:
BCRYPT_ROUNDS=12

Adding Google OAuth (Optional)

Cashify includes support for Google OAuth, though it’s commented out by default:
1

Enable Google in Routes

Update the OAuth route constraint in routes/auth.php:
Route::get('auth/{driver}/redirect', [SocialiteSessionController::class, 'redirectToProvider'])
    ->where('driver', '(github|google)')  // Already configured
    ->name('socialite.redirect');
2

Configure Google Credentials

Add your Google OAuth credentials to .env:
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
GOOGLE_REDIRECT=https://yourdomain.com/auth/google/callback
3

Uncomment Frontend Button

Enable the Google login button in resources/views/components/buttons/socialite-buttons.blade.php.

Testing Authentication

# Test registration
php artisan test --filter=RegistrationTest

# Test login
php artisan test --filter=AuthenticationTest

# Test email verification
php artisan test --filter=EmailVerificationTest

Next Steps

Environment Variables

Configure all authentication-related environment variables

Localization

Set up multi-language authentication messages

Build docs developers (and LLMs) love