---
title: Protect OAuth Code Flow Vs CSRF
impact: HIGH
impactDescription: prevents attackers from stealing authorization codes or linking victim accounts to attacker credentials
tags: oauth, csrf, state, authorization, security, kotlin
---

## Protect OAuth Code Flow Vs CSRF

During an OAuth 2.0 Authorization Code flow, an attacker can perform a CSRF attack to force a user to link their session with the attacker's account, or to intercept the victim's authorization code. Using a `state` parameter is the standard defense.

**Incorrect (no state parameter):**

```kotlin
// VULNERABLE: Initiating OAuth without a unique state
get("/auth/google") {
    val googleAuthUrl = "https://accounts.google.com/oauth/authorize?" +
        "client_id=$clientId&" +
        "redirect_uri=$redirectUri&" +
        "response_type=code&" +
        "scope=email"
    call.respondRedirect(googleAuthUrl)
}
```

**Correct (state parameter validation):**

```kotlin
import java.security.SecureRandom
import java.util.Base64

@GetMapping("/auth/google")
fun initiateGoogleAuth(session: HttpSession, response: HttpServletResponse) {
    // 1. Generate a random, unpredictable state
    val state = ByteArray(32).let {
        SecureRandom().nextBytes(it)
        Base64.getUrlEncoder().withoutPadding().encodeToString(it)
    }

    // 2. Store it in the user's session
    session.setAttribute("oauth_state", state)

    // 3. Include it in the authorization request
    val authUrl = "https://accounts.google.com/oauth/authorize?" +
        "client_id=$clientId&" +
        "redirect_uri=$redirectUri&" +
        "response_type=code&" +
        "state=$state"
    
    response.sendRedirect(authUrl)
}

@GetMapping("/auth/google/callback")
fun handleGoogleCallback(@RequestParam code: String, @RequestParam state: String, session: HttpSession) {
    // 4. Validate the state returned by the provider
    val savedState = session.getAttribute("oauth_state") as? String
    
    if (savedState == null || savedState != state) {
        throw AccessDeniedException("Invalid OAuth state parameter. Potential CSRF attack.")
    }

    // 5. Clear the state once used
    session.removeAttribute("oauth_state")

    // Proceed to exchange code for tokens...
}
```

**Best Practices:**
- Always use a random, high-entropy `state` value.
- Store the state in a secure, server-side session or an encrypted, `HttpOnly` cookie.
- Alternatively, use **PKCE (Proof Key for Code Exchange)** for public clients (like mobile apps), which provides even stronger protection against code interception.

**Tools:** Spring Security OAuth2 (handles state automatically), Ktor Authentication, Manual Review
