What is the correct way to implement the OAuth2 state parameter?


I’m trying to understand how to properly implement state in an OAuth2 client, to mitigate CSRF attacks. This answer explains the attack scenario pretty well, to summarize:

  1. The attacker starts the authorization code flow, and gets a code from the authorization server
  2. The attacker traps his redirect url (e.g. https://client.com/exchangecodefortoken?code=attacker_code), and tricks a victim into accessing that link
  3. The victim is logged in with the attacker account, and if he upload sensitive info to the client application, that info will be uploaded to the attacker’s account meaning the attacker will have access to it.

The RFC vaguely says that state should be derived from the user session, for example using a cookie:

The client MUST implement CSRF protection for its redirection URI. This is typically accomplished by requiring any request sent to the redirection URI endpoint to include a value that binds the request to the user-agent’s authenticated state (e.g., a hash of the session cookie used to authenticate the user-agent). The client SHOULD utilize the “state” request parameter to deliver this value to the authorization server when making an authorization request.

This question implements this by creating an id cookie and a random state for each session, and correlating those two in the client DB. The client then handles an authorization code callback (e.g https://client.com/exchangecodefortoken?code=abc&state=def) by verifying that:

params.state == DB_at(cookies[id]).state 

It seems to me that an attacker can still bypass this kind of protection, because he knows his own id cookie:

  1. The attacker starts the authorization code flow, and gets a code from the authorization server
  2. The attacker traps his redirect url (e.g. https://client.com/exchangecodefortoken?code=attacker_code&state=attacker_state). He then uses that URL to prepare a link that when clicked, also sets the id cookie to the attacker’s id cookie (not very familiar with cookies, but this answer implies you can do this)
  3. The victim accesses the malicious link, and is redirected to the client with the attacker’s state and id cookie
  4. The request passes aforementioned verification and the victim is logged in with the attacker’s account – the attack succeeded.

How should you securely implement the state parameter?

(I’ve also seen answers like this one that claim that the attack is the other way around: at the end the attacker gets access to the victim’s account – and not vice versa. I though the explanations given for this scenario are incorrect/incomplete, but please correct me if I’m wrong on this)