Mitigate CSRF Attacks with State Parameters

You can deny malicious requests by using the state parameter to hold a correlation value for verification.

A CSRF attack can occur when a malicious program causes a user's web browser to perform an unwanted action on a trusted site on which the user is currently authenticated. This type of attack specifically targets state-changing requests to initiate an action instead of getting user data because the attacker has no way to see the response to the forged request.

The state parameter helps mitigate CSRF attacks to ensure that the response belongs to a request that was initiated by the same user. For the most basic cases the state parameter should be a nonce, used to correlate the request with the response received from the authentication.

Most modern OIDC and OAuth2 SDKs, including Auth0.js in single-page applications, handle the state generation and validation automatically.

  1. Before redirecting a request to the IdP, have the application generate a random string. For example:


The allowed length for state is not unlimited. If you get the error 414 Request-URI Too Large try a smaller value.

  1. Store this string locally. Choose a method based on your type of application. For example:

    • For regular web apps, use a cookie or session
    • For a single-page app, use local storage in the browser
    • For a native app, use memory or local storage
  2. Add the state parameter to the request (URL-encoding if necessary).

    // Encode the String

    After the request is sent, the user is redirected back to the application by Auth0. The state value will be included in this redirect. Note that depending on the type of connection used, this value might be in the body of the request or in the query string.

  3. Retrieve the returned state value and compare it with the one you stored earlier. If the values match, then approve the authentication response, else deny it.

// Decode the String
var decodedString = Base64.decode(encodedString);
if(receivedState === retrieveStateStoredLocally()) {
 	// Authorized request
} else {
 	// This response is not for us, reject it

Keep reading