---
title: Protect OAuth Code Flow Vs CSRF
impact: HIGH
impactDescription: prevents attackers from tricking users into linking malicious accounts via OAuth CSRF
tags: oauth, csrf, state, authorization, security, php
---

## Protect OAuth Code Flow Vs CSRF

During the OAuth 2.0 authorization code flow, an attacker could initiate an auth request and trick a logged-in user into clicking the callback URL. Without a `state` parameter, your application might link the attacker's account (e.g., GitHub/Google) to the victim's local session.

**Incorrect (no state parameter):**

```php
// Initiating OAuth without state
public function redirectToProvider() {
    $url = "https://provider.com/oauth/authorize?" . http_build_query([
        'client_id' => 'CLIENT_ID',
        'redirect_uri' => 'CALLBACK_URL',
        'response_type' => 'code',
        'scope' => 'user:email',
    ]);
    return redirect($url);
}

// Callback without state validation
public function handleCallback(Request $request) {
    $code = $request->input('code');
    // Dangerous: missing state validation!
}
```

**Correct (state parameter validation):**

```php
// 1. Manually initiating with state
public function redirectToProvider() {
    $state = bin2hex(random_bytes(16));
    session(['oauth_state' => $state]); // Store in session

    $url = "https://provider.com/oauth/authorize?" . http_build_query([
        'client_id' => 'CLIENT_ID',
        'redirect_uri' => 'CALLBACK_URL',
        'response_type' => 'code',
        'state' => $state,
    ]);
    return redirect($url);
}

// 2. Validating in callback
public function handleCallback(Request $request) {
    $state = $request->input('state');
    $storedState = session()->pull('oauth_state');

    if (empty($state) || $state !== $storedState) {
        abort(403, 'Invalid OAuth state.');
    }

    // Proceed to exchange code for token
}

/**
 * 3. Using Laravel Socialite (Recommended)
 * Socialite handles the state parameter automatically.
 */
return Socialite::driver('github')->redirect();
```

**Why it matters?**
The `state` parameter acts as a secret token that links the initial request from your site to the final callback from the provider. If the `state` doesn't match, it means the auth flow was not initiated by the user on your application, signaling a potential CSRF attack.

**Tools:** Laravel Socialite, League OAuth2 Client, PHP `random_bytes()`, Manual Security Review
