PKCE for Secured Applications

Justus Nithushan
Chain Analytica
Published in
7 min readDec 9, 2020

Application grant types aka flows are methods by which a client application can gain tokens so as to get a limited access on a protected resource.

There are lots of flows that are used by the techie community. PKCE is one of the best practiced flow that fits well on mobile and Single Page Applications (SPAs). It can be seen as an extension of Authorization code flow (well suited for any applications that can securely store it’s client secret key value). In contrast to authorization code flow, PKCE is well suited for applications that cannot keep it’s client secret key safe.

Of course, there is another flow, which is called implicit flow that was recommended earlier for native apps and SPAs in which the access token is directly provided as a query parameter in response to a successful login along with the redirect URL, without an extra code exchange step. Now it is considered as deprecated because there is no guarantee whether the access token is received by the client application or not. A malicious code can intercept and get the tokens from the URL itself.

Well, then how PKCE overcome this issue? You can think of PKCE as an authorization code flow technique except that it uses a variable key instead of a static secret key of the client application. In PKCE flow client application sends a code challenge value in order to request an authorization code which will be preserved by the Authorization Server for later verification. The code challenge value is created using code verifier value in a particular manner. In order to request a token, in exchange of the code, the code verifier value is sent to the authorization server. Authorization Server, by comparing the code challenge value and code verifier value, confirms that the application is the legitimate one so that it can exchange the token with it. In case if we were using the static secret key, it will be easy for any malicious code that has the client id and client secret to pretend itself as the original client and request a token exchange for the code that can be obtained by the URL. As we use a random code challenge and code verifier values that are very hard to guess, the PKCE flow resolves the above mentioned issue. How to achieve this randomness is a key topic which will be covering in the upcoming sections below.

Before going deep into PKCE let’s have a look at how authorization code flow works. Below is step by step process that demonstrate how a client application gets access to a protected resource with the permission of the resource owner.

  1. Resource owner/User clicks the login link in the client application.
  2. The client sends a request for authorization code, to the Authorization Server.
  3. Authorization Server responds by presenting the login page to the user.
  4. User enters his/her credentials.
  5. Authorization Server verifies the credentials and if everything is fine, it present a consent page to the user.
  6. User permits the client to access the protected resource.
  7. Authorization Server sends an authorization code to the client app.
  8. Client request the Authorization Server to exchange the code to access token.
  9. If everything is fine, Authorization Server responds with an access token and refresh token.
  10. Client request Resource Server to access the protected data using the access token.
  11. Resource Server request Authorization Server for validate the access token.
  12. Authorization Server validates the token.
  13. If validation is successful, then the Resource Server sends the requested data to the client.

Hope the authorization code flow is clear, now let us see how PKCE flow works by looking at the step by step process that happens when an application tries to access a protected resource. Oops!! it is the same. Then what is the difference between those two?

Although the workflow of the above two are same, they are differed in the requests itself. To be precise the request parameters that a client passes in order to request a login page so as the authorization code and the parameters passed to exchange code to token are a bit different in what is used in authorization code flow and what is used in PKCE flow. A basic PKCE workflow is illustrated in the diagram below.

Basic PKCE workflow

In order to get the login page and receive an authorization code from the Authorization Server we will need to hit the authorization endpoint in a browser with certain query parameters. The below list shows the query parameters and their values.

  • response_type=”code”
  • client_id=”you_client_id”
  • redirect_uri=”a_preferred_redirect_uri”
  • scope=”your_scope”
  • state=”random_alphanumeric_characters_of_moderate_length”

Note that the response_type must be exactly ‘code’ and other parameters should be changed according to your application.

We will see how these parameters differs from PKCE. Below list shows the query parameters and their values.

  • response_type=”code”
  • client_id=”you_client_id”
  • redirect_uri=”a_preferred_redirect_uri”
  • scope=”your_scope”
  • state=”random_alphanumeric_characters_of_moderate_length”
  • code_challenge_method=”S256 |plain”
  • code_challenge_value=”your_code_challenge_value”

Note that here also response_type must exactly be ‘code’ and others be changed according to your application requirements.

You can now clearly see the difference between the above two requests. In addition to the query parameters that is used in normal authorization code flow there are two new parameters in PKCE code flow namely code_challenge_method and code_challenge_value. Don’t worry folks, in the next section we will discuss on how to generate code_challenge_value.

Before generate code challenge value we will have to generate another value named code_verifier which will be used in the next request.

Generate code verifier and code challenge value

According to IETF the code verifier value be generated as below

The code verifier SHOULD have enough entropy to make it
impractical to guess the value. It is RECOMMENDED that the output of
a suitable random number generator be used to create a 32-octet
sequence. The octet sequence is then base64url-encoded to produce a
43-octet URL safe string to use as the code verifier.

Let us see a simple JavaScript implementation that can be used in either mobile application development and SPA development.

var byteArray = new Uint8Array(32);
window.crypto.getRandomValues(byteArray);
var codeVerifier = base64UrlEncoder(Array.from(byteArray));

base64UrlEncoder() is a method that takes an array of numbers and return a base64urlencoded value. The implementation of it is given below.

base64UrlEncoder(value){
const stringValue = String.fromCharCode.apply(null, value);
const base64Encode = btoa(stringValue);
return base64Encode.replace(/\+/g, ‘-’).replace(/\//g,‘_’).replace(/=/g, ‘’);
}

We have created code_verifier value. Let us see how to create code challenge value out of it.

According to IETF the code challenge value be generated as below

The client then creates a code challenge derived from the code
verifier by using one of the following transformations on the code
verifier:

plain
code_challenge = code_verifier

S256
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

If the client is capable of using “S256”, it MUST use “S256”, as
“S256” is Mandatory To Implement (MTI) on the server. Clients are
permitted to use “plain” only if they cannot support “S256” for some
technical reason and know via out-of-band configuration that the
server supports “plain”.

The plain transformation is for compatibility with existing
deployments and for constrained environments that can’t use the S256
transformation.

In order to obtain code challenge value (with S256) the code verifier must be encoded using ASCII encodings. Then get a secured hash value out of the encoded value with SHA256 hash function. Then get the base64URL encoded value of the resulted hash value. Enough talking let’s implement this with JavaScript.

async generateCodeChallenge(codeVerifier){
const encoder = new TextEncoder();
const encode = encoder.encode(codeVerifier);
const digest = await window.crypto.subtle.digest('SHA-256', encode);
const code_challenge_value = base64UrlEncoder(Array.from(new Uint8Array(digest)));
return code_challenge_value;
}

Note that here we have used the previous code verifier value and base64UrlEncoder method that are mentioned above to generate the code challenge value.

We have now created code verifier and code challenge values. The code challenge value is used in the request we discussed above. Let’s catch up from there. A login page is sent when we hit the above created URL in a browser with all the query parameters that we discussed. If the user’s credentials are correct and verified successfully, it will be redirected to the redirect_url along with some other query parameters in which ‘code’ is one of them. We will then extract the value of the ‘code’ parameter which is our authorization code that will be used to get an access token.

To get an access token we will have to send a post request to the token endpoint of the authorization server along with some form data with application/x-www-form-urlencoded content type. The form data will have the following key and corresponding values. Note that the keys and values below are here separated using a ‘:’ .

For Authorization Code flow:

  • grant_type : “authorization_code”
  • client_id : “your_client_id”
  • client_secret : “your_client_secret”
  • code : “extracted_code_value_from_the_above_request”
  • redirect_uri : “your_redirect_uri”

Here, the grant type value must be exactly ‘authorization_code’ and others must be changed according to your application. The value of the code parameter is the one we earlier extracted from the query parameters that was sent by the Authorization Server in response to the successful login along with the redirect_uri that we specified in the request.

For PKCE flow:

It is same as the above parameters except we will not send client_secret as our application is public not confidential and we will have to send our code verifier value that we generated earlier. Putting it altogether, the parameter details are below.

  • grant_type : “authorization_code”
  • client_id : “your_client_id”
  • code : “extracted_code_value_from_the_above_request”
  • redirect_uri : “your_redirect_uri”
  • code_verifier: “code verifier value generated earlier”

If everything is fine the Authorization Server will respond with an access token, which the client application can use it to access the protected resource.

Hope you enjoyed reading this blog post. Please don’t forget to give some clapping.

--

--