PKCE Extension

Client Secrets are not meant to be exposed, this means it is not advisable to use the regular Authorization Code Flow in Native Applications or Single Page Applications as this flow requires the client_secret to be used when exchanging an authorization_code for an access_token

The Proof Key for Code Exchange by Oauth Public Clients(PKCE) is an extension to the OAuth Authorization Code Flow. The advantage of implementing this lies in the ability to verify that a client is who they claim to be, by requiring a code_challenge and code_verifier thereby preventing authorization code interception attack.

Setting up

Before starting, you will need the client_id and scope from your Provider for your Connect Application.

Your Application will also need to handle requests on your redirect_uri. This could be a regular HTTP redirection or a custom scheme URI redirection which will be captured by a Native Application.

Step 1 - Getting the authorization_code

Building the Login Link

The first step is to redirect your User (that you want to authenticate) to your Provider’s authorize URL (e.g. https://[providerFQDN]/oauth/authorize) with the following query parameters:

Note
A unique code_verifier must be generated everytime the authorization url is built, and this value will be transformed to generate the code_challenge in the query parameters below by "Base64-encoding" the SHA256 value of the code_verifier. The code_verifier SHOULD be longer than 43 characters
client_id
required
string

Your Connect Application client ID

code_challenge
required
string

A dynamically generated code_challenge value which will be verified during the token request, Base64(sha256(code_verifier)). This value must be unique for every authorization request and longer than 43 characters

code_challenge_method
required
string

The method used to generate the code_challenge, it will be applied during the verification of authorization code, value must be S256

redirect_uri
required
string

A valid A valid redirect_uri (must match any one registered with your Connect Application)

response_type
required
string

Should be code

scope
required
string

A space-separated list of strings (e.g. openid phone email)

state
optional
string

This parameter should be used for preventing Cross-site Request Forgery and will be passed back to you, unchanged, in your redirect URI (255 characters max).

A common redirect_uri would be https://your.website.com/oauth/callback, it can be anything you want but it must match one of the redirect_uris in your Connect Application configuration.

Code Sample

The following code uses Node.js and express with the pug rendering engine to illustrate a typical web application.

To do so, this application needs to display the correct link to Connect.

Caution
The following code is not production ready
// app.js
app.get("/", function(req, res) {
  const codeVerifier = 'A_unique_random_code_verifier_with_minimum_length_of_43_should_persisted';
  const codeVerifierBuffer = Buffer.from(codeVerifier);
  const digest = await crypto.subtle.digest('SHA-256', codeVerifierBuffer);
  const codeChallenge = btoa(digest);

  res.render("index", {
    login_url: `https://[providerFQDN]/oauth/authorize?client_id=${client_id}&redirect_uri=${redirect_uri}&response_type=code&scope=${scope}&code_challenge=${codeChallenge}&code_challenge_method=S256`
  });
});
// templates/index.pug
a(href=login_url) Login

login_url is just a link that you can put anywhere on your page. The Authentication process with Connect starts as the User clicks on this link (and leaves your Application).

When the User has successfully authenticated on Connect, he or she will be redirected back to your Application using the redirect_uri with the authorization_code as a code parameter in the URL.

Step 2 - Getting the access_token

You need to take the authorization_code you just received and exchange it for an access_token.

To do that, you just need to do a POST HTTP request to the token URL of your Provider (e.g. https://[providerFQDN]/oauth/token) with the following parameters:

POST
https://provider-FQDN/oauth/token
client_id
required
string

Your Connect Application client ID

code_verifier
required
string

The unique random code_verifier generated in Step 1 above

code
required
string

The code you got in the redirection query params

grant_type
required
string

Should be authorization_code

redirect_uri
required
string

Following this request, you will get a response from Connect containing the following:

Note
The length of tokens can vary and exceed 255 characters. Keep that in mind if you want to store them in a database as a VARCHAR will not be large enough.
Warning
Both the access_token and the refresh_token are sensitive information and should NEVER be shared with your User’s browser as a mean of session identification (e.g. cookie).

Code Sample

The following code shows how your redirect_uri route can proceed to exchange the code with an access_token.

Note

For the sake of clarity, we’re using the following node packages:

  • request to make a POST HTTP request

  • jsonwebtoken to verify a JWT token

Caution
The following code is not production ready
// app.js
const request = require("request");
const jwt = require("jsonwebtoken");

app.get("/oauth/callback", function(req, res) {
  const payload = {
    client_id: client_id,
    code_verifier: code_verifier,
    code: req.query.code,
    grant_type: "authorization_code",
    redirect_uri: redirect_uri
  };

  request.post(
    "https://[providerFQDN]/oauth/token",
    { form: payload },
    function(err, httpResponse, body) {
      const json_response = JSON.parse(body);
      jwt.verify(json_response.access_token, client_secret, function(err, decoded_jwt) {
        if (err !== null) {
          // something went wrong
        } else {
          // Yay! 🤗 Your User has successfully authenticated
          // `decoded_jwt` contains the information about the token
        }
      });
    }
  );
});
Warning
Once again: keep in mind that you should NOT share the access_token and refresh_token with the User’s browser (or any other unsecure device like a mobile) or any application that you do not trust.
Table of Content