2
votes

I'm taking a ASP.NET security course on OpenID Connect authorization code workflow with PKCE protection against code replay attack. There is one aspect of this process that I don't understand.

The process:

  1. Relying client generates PKCE code_verifier, hash it into code_challenge, and send the user to the authorization server with the code_challenge as a query parameter.

  2. The authorization server stores the code_challenge, issues authorization code, and redirects user back to client with the authorization code.

  3. The client sends the authorization code along with the original code_verifier to exchange for tokens. The authorization server verifies that the code_verifier indeed hashes into code_challenge before issuing the token.

My question is step #3: since HTTP is stateless, how does the client know which code_verifier to send along with the authorization code? Is this code_verifier stored in a cookie on the user agent?

2

2 Answers

1
votes

PKCE was originally intended for mobile clients, which typically make only a single authorization request. But it is now recommended for all clients using the authorization code flow.

You're correct that if the client is a web application serving many users, the client application needs to associate the correct code_verifier with the authorization request.

As you say, the client could store the code_verifier in a cookie, so it will be sent to the client together with the authorization code.

Or it could store it server side on the client and stick the key in the state parameter to the authorization request. The state will be part of the call to the redirect_uri if passed along with the authorization request.

0
votes

Using a cookie to store the "code verifier" appears to violate the spirit of the OpenId specification and just appears to be a big security hole.

In brief, the spec expects you to use S256 to hash the code verifier and the code verifier to be kept secret from eavesdropping or being guessed.

PKCE RFC Section 7.1 says the "The security model relies on the fact that the code verifier is not learned or guessed by the attacker. It is vitally important to adhere to this principle. As such, the code verifier has to be created in such a manner that it is cryptographically random and has high entropy that it is not practical for the attacker to guess." ... "The use of "S256" protects against disclosure of the "code_verifier" value to an attacker."

Now it does give an opening to using a cookie if you encrypt the "code verifier". Here is the what the spec says in Section 7.2: "If the code challenge method is "plain" and the code challenge is to be returned inside authorization "code" to achieve a stateless server, it MUST be encrypted in such a manner that only the server can decrypt and extract it."

Well, OpenId specification has a solution and Microsoft has provided an implementation to follow. Refer to the "state" parameter in the OpenId specification when calling the "authorization" endpoint. The Identity Providers will return the value back to the caller.

The way it is done in .NET Core is using the "state" query string parameter whereby the value is a dictionary but protected (encrypted). For help look through the .NET core code and look at the method "BuildChallengeUrl" in "Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler"

Also, this Site has a great idea that works. It's a valid option.