Sascha Preibisch

OAuth and PKCE (RFC 7636)

Blog Post created by Sascha Preibisch Employee on Nov 3, 2016

PKCE, RFC 7636

Proof Key for Code Exchange by OAuth Public Clients

In OAuth 2 there are different types of clients. Confidential clients and public clients. By design public clients are not able to maintain a client secret. A typical public client would be implemented in JavaScript. Another typical public client would be a native mobile app. To explain PKCE I am using the native app as an example.

 

Native apps that use the OAuth authorization code flow are required to associate (register) a custom scheme callback_uri in the device's operating system (OS). If the browser on the device finds the callback_uri as target URL it will forward it to the OS. The OS will find the associated app and launches it.

 

Unfortunately multiple apps could register the same callback_uri. The OS will pick one of those randomly. That is bad! It is bad because the callback_uri will include the issued authorization code! The randomly launched app is now in possession of the authorization code. And since the app is of type public it does not need to provide a secret to the Authorization Server when  exchanging the code for an access_token.

 

What happened here is that GoodApp requested an authorization code but EvilApp received it and is now able to use it. See the simple graphic below:

 

OAuth Authorization Request without PKCE

 

How does PKCE help?

RFC 7636 has specified new parameters to be included in the initial authorization request and the code exchange request. The initial request is sent to /authorize and its intention is to receive an authorization_code. The code exchange request is sent to /token to exchange the authorization_code for an access_token.

 

First step: The initial request to /authorize will include these additional parameters:

  1. code_challenge: a value that is only known to GoodApp (to keep it close to the upper image)
  2. code_challenge_method (optional): either plain or S256. plain indicates to the server that code_challenge is a plain value as given to the server. S256 indicates that it has been hashed using SHA-256

The authorization server will take those values (plain is the default if the code_challenge_method was not given) and associates them with the code that is being issued.

 

Second step: The code exchange request to /token will include one additional parameter:

  1. code_verifier: the value that was used to generate code_challenge for the initial request

 

Server side validation with grant_type=authorization_code:

The server will lookup the code_challenge and code_challenge_method that are associated with the given code. It will then do one the following validation checks:

  1. IF (code_challenge_method == plain) THEN compare(code_verifier, code_challenge)
  2. ELSE compare(sha-256(code_verifier), code_challenge)

 

Even if EvalApp (to keep it close to the upper image) would have been in possession of the authorization_code this time it would not be able to exchange it for an access_token because the server side validation would fail.

 

Summary

I hope this post helps understanding PKCE. I also hope that it helps understanding that supporting it requires just a small code change. And I hope it makes it obvious why PKCE should be used ALWAYS with public clients that use the the authorization code flow.

OTK (CA API OAuth Toolkit) will soon support it out of the box. Those customers who do not want to wait will find a blog post with instructions on how to implement PKCE support in the next few days here in this space.

 

As always, please leave a comment with questions or requests.

Outcomes