Course contentsShow
← Node.js
Lesson 978 of 1,9809. Authentication and Identity ManagementFree lesson

Securing the Redirect Flow with CSRF Tokens

Use state parameters to prevent Cross-Site Request Forgery during external login handshakes.

In this lesson, you will learn how to use state parameters in OAuth2 flows to prevent attackers from hijacking your users' identity during the authentication redirect.

When a user logs in via an external provider (like Google or GitHub), your application initiates an "Authorization Code Flow." Your server redirects the user to the provider, which then sends them back to your redirect_uri with a code. However, a malicious actor could intercept this redirect or trick a logged-in user into clicking a crafted link, causing them to unknowingly link their account to an attacker's profile.

To prevent this, we use the state parameter.

How it Works

The state parameter acts as a unique, cryptographically strong token generated by your server before the redirect begins. You store this value in the user's session and send it as a query parameter to the identity provider.

  1. Generation: Your Node.js server generates a random string (e.g., using crypto.randomBytes) and stores it in the user's session (which you've learned to manage with Redis).
  2. Handshake: You include this state in the redirect URL sent to the provider.
  3. Verification: When the identity provider redirects the user back to your app, it includes that same state value in the URL. Your server must compare the returned state with the one stored in the user's session.

If the values do not match—or if the state is missing—you must abort the authentication process. Because the attacker cannot access the user's session cookies, they cannot guess or forge the correct state token, effectively neutralizing the CSRF attempt.

// Before redirecting to the provider
const state = crypto.randomBytes(16).toString('hex');
req.session.oauthState = state; // Store for verification

const authUrl = `https://provider.com/auth?client_id=...&state=${state}`;
res.redirect(authUrl);

// Upon receiving the callback
if (req.query.state !== req.session.oauthState) {
    return res.status(403).send('CSRF detected: State mismatch.');
}

Key Takeaway: Always inject a random state parameter into your OAuth2 authorization requests and verify it upon the provider's callback to ensure the session initiating the handshake is the same one completing it.