Understanding PKCE - Part 1 - Authorization flow

OAuth 2.0

OAuth 2.0 is a industry standard authorisation technique used in many applications.

PKCE is the extension of OAuth 2.0 which uses code_challenge and code_verifier to get access token in client applications.

In native applications, we usually rely on authenticating the user on browser side instead of server, but to authenticate the user, we can't use client secret which is basically used in OAuth flow in server side.
This is where PKCE comes in, PKCE stands for Proof Key Code Exchange, which uses randomly generated code verifier and code challenge which is to get authentication done in browser.

This helps helps in security as the code challenge and code verifier which acts as client secret in browser is dynamically generated.

How does PKCE flow work

PKCE flow usually works by

  1. Get Authorisation code by making call to authorization endpoint with code_challenge and code_verifier.

  2. The auth code thus generated is used to make call to token endpoint to get the token response.

  3. Token response consists of access_token which will be sent to further calls and refresh token which is used to regenerate access token when it expires.

  4. When both access_token and refresh_token expires, the process has to be repeated from step 1.

Diagram:

Authorisation flow:

This flow is used to get auth code by making call to auth endpoint with code challenge.
To get this we have to:

  1. We need to create a code verifier.

     const codeVerifier = crypto.randomUUID().replace(/-/g, "");
    
  2. We need to encode the code_verifier, which will get us code_challenge.

  3. We have to set query params for auth redirection url.

     const params = { client_id: "client-id",
     response_type: "code",
     scope: "openid offline_access eq1",
     redirect_uri: "https://localhost:8080/callback",
     code_challenge: codeChallenge,
     code_challenge_method: "S256",
     state: crypto.randomUUID()}
    
    • client_id: This is the identifier used for the authorisation process.

    • response_type: This tells the auth endpoint how to execute, in this case, response_type will be "code". Since we are generating auth code.

    • scope: This talks about permissions allowed.

    • redirect_uri: This determines where the auth endpoint should redirect the control. This url contains code for access_token generation.

    • code_challenge: This is the encrypted code_verifier. This acts like client secret for PKCE.

    • code_challenge_method: This gives the info about the encryption method used for encrypting code_verifier.

    • state: Random string which is generated in application, which can be used to validate while starting token generation.

  4. Once this is set, we have to write the code to redirect the control to auth endpoint with the above params.

     window.location.assign(authEndpoint?client_id=uc-client-id&response_type=code&scope=openid%20profile%20offline_access&redirect_uri=https%3A%2F%2Flocalhost%3A8080%2Fcallback&code_challenge=base64(sha256(uuid))&code_challenge_method=S256&state=uuid)
    
  5. Once this is done, we will land on redirect_uri mentioned in param, with auth code and state as request params.

     https://localhost:8080/callback/?code=authzCode&state=uuid
    
  6. This auth code is used to get access token from token endpoint.

With this, we complete the first part of authentication flow, where we got the auth code from authorization endpoint. The code that is obtained now will be used to make call to the token endpoint to generate the access_token which will be used to authenticate the user.
In this method, we are using code verifier and code challenge which is randomly generated, thus we don't need to store any secrets on the browser.

Reference: