18

PKCE explanation: https://www.oauth.com/oauth2-servers/pkce/

OAuth Flow example: https://developer.okta.com/docs/guides/implement-auth-code-pkce/use-flow/

My question is more specifically: Is there any reason to require state if PKCE is implemented?

state stops CSRF attacks by having the app server verify the state it gave the client is the same as the state the client gives it (after making the first OAuth request)

  1. App Server generates random state for each unique client & gives to client
  2. Client sends state to Auth Server, Auth Server returns with state & code
  3. Client sends state and code to App Server
  4. App Server ensures state in step 1 == state in step 2

However if you did this with PKCE:

  1. App Server generates random code_challenge for each unique client & gives it to the client (we can ignore the optional hashing for now)
  2. Client gives code_challenge to the Auth Server, Auth Server returns with code
  3. Client sends code to App Server
  4. App Server sends code & code_challenge in step 1 to Auth Server who verifies that code_challenge in step 1 == code_challenge in step 2

It seems like the exact same flow, except you have to store the code_challenge on your server instead of state being stored in either your server or the client's cookies and the end check is done by the Auth Server instead of the App Server.

2 Answers 2

19

The state is given back by the OAuth2 server in the redirected url, so the client app can verify that the request to open the authorization page was indeed triggered by itself.

The code_challenge is not given back by the OAuth2 server, but the client app must send the belonging code_verifier with the next request to exchange the authorization code for a token. The OAuth2 server can then be sure that it is the same client app which did the first request to open the authorization page.

Even if an attacker listened to the first request and the following redirect, (s)he cannot know the code_verifier, it is never sent over the wire. This makes it impossible to (mis)use the redirect and impersonate the client to get the token.

So yes both are required, the state makes the link between the open-authorization-page-request and the redirect, and the code_challenge makes the link between open-authorization-page-request and exchange-code-for-token-request.

5
  • 1
    Great answer! An RFC document related to OAuth2 best practices brought unnecessary confusion regarding state and PKCE. The document is here: tools.ietf.org/html/… It states the following: If PKCE [RFC7636] is used by the client and the authorization server supports PKCE, clients MAY opt to not use "state" for CSRF protection, as such protection is provided by PKCE. Some of my colleagues suggested that we don't need state anymore, as PKCE will do its work, but as you have pointed out, they serve different purpose.
    – luben
    Commented Sep 14, 2020 at 18:47
  • 1
    For me it is not clear which additional security is provided by the state when using PKCE. State and PKCE challenge are both verified after authorization redirection to the client app, state protecting from CSRF while PKCE from CSRF and MITM. Do you have an example of a threat that would not be mitigated by PKCE without state param ? Commented Apr 20, 2022 at 9:58
  • @AlexandreB - I cannot tell for sure, whether the missing state can be exploitet or not, but I cannot rule it out neither. There is simply no reason to omit the state, the work is minimal and if you want to be compilant with as many servers as possible there is good reason to deliver the state. On the other hand there is no reason to continue with an exchange if it wasn't your app which initiated the authorization. Commented Apr 20, 2022 at 11:45
  • @martinstoeckli On my side I use PKCE to prevent CSRF and MITM. It is configured as required for my client on the Auth Server. And I am using state to transmit the client state during the authorization flow (including a redirect uri). Commented Apr 21, 2022 at 12:30
  • PKCE should work as protection against CSRF: "The attacker would need to inject a code that is bound to the same code challenge that was used initially by the client. While the attacker can create codes bound to arbitrary code challenges (by using the client and authorzation server) the attacker cannot know the code challenge in this case." See danielfett.de/2020/05/16/pkce-vs-nonce-equivalent-or-not/… Commented May 21, 2022 at 0:20
2

state is not required if PKCE is used. As the OP has correctly deduced, PKCE defends the entire flow (from start (redirect) to end (token request)).

Using state will defend some part of the entire flow regarding the redirect and client app, but since PKCE defends the entire flow, state is redundant.

As mentioned in the best practice, you may use state to transport application state. What does this mean? Well, take the following situation:

  1. User goes to auth.foo.com.
  2. Redirects to google.com/accounts?redirect_uri=auth.bar.com.
  3. User completes login and goes to auth.bar.com.

Notice how auth.bar.com doesn't carry any information about the login attempt starting from auth.foo.com. If auth.bar.com and auth.foo.com use the same authentication server, auth.bar.com must have access to some session token to access information about the login. You cannot use a cookie from foo.com because the domain is different. In this case, state would make sense to use.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .