Skip to content
On this page

Authorization Code PKCE

When public clients (e.g., native and single-page applications) request Access Tokens, some additional security concerns are posed that are not mitigated by the Authorization Code Flow alone. This is because:

  • Native apps

    • Cannot securely store a Client Secret. Decompiling the app will reveal the Client Secret, which is bound to the app and is the same for all users and devices.

    • May make use of a custom URL scheme to capture redirects (e.g., MyApp://) potentially allowing malicious applications to receive an Authorization Code from your Authorization Server.

  • Single-page apps

    • Cannot securely store a Client Secret because their entire source is available to the browser.

    • Given these situations, OAuth 2.0 provides a version of the Authorization Code Flow which makes use of a Proof Key for Code Exchange (PKCE) (defined in OAuth 2.0 RFC 7636).

The PKCE-enhanced Authorization Code Flow introduces a secret created by the calling application that can be verified by the authorization server; this secret is called the Code Verifier. Additionally, the calling app creates a transform value of the Code Verifier called the Code Challenge and sends this value over HTTPS to retrieve an Authorization Code. This way, a malicious attacker can only intercept the Authorization Code, and they cannot exchange it for a token without the Code Verifier.

Instructions on how to integrate authorization code PKCE flow

  1. The user clicks Login within the application.

  2. Auth0's SDK creates a cryptographically-random code_verifier and from this generates a code_challenge.

  3. Auth0's SDK redirects the user to the Auth0 Authorization Server (/login endpoint) along with the code_challenge.

  4. Your Auth0 Authorization Server redirects the user to the login and authorization prompt.

  5. The user authenticates using one of the configured login options and may see a consent page listing the permissions Auth0 will give to the application.

  6. Your Auth0 Authorization Server stores the code_challenge and redirects the user back to the application with an authorization code, which is good for one use.

  7. Auth0's SDK sends this code and the code_verifier (created in step 2) to the Auth0 Authorization Server (/auth/token endpoint).

  8. Your Auth0 Authorization Server verifies the code_challenge and code_verifier.

  9. Your Auth0 Authorization Server responds with an ID Token and Access Token (and optionally, a Refresh Token).

  10. Your application can use the Access Token to call an API to access information about the user.

The API responds with the requested data.

  1. Create application

  • Choose Authenticator Type is Authorization PKCE Flow
  • Enter Redirect Url. The Redirect URL is the callback URL after user login success

  1. Add user to application
    • On the applications grid, click on the edit icon of record where you want to edit the application
    • Click on tab Application Users
    • Click on the Create button

  • Input data: First name, Email. Password and Confirm Password
  • Click on the Save button

  1. Integrated

When user want login to system, will redirect to page http://stg.token.tci-pf.net/login?clientId=&code_challenge=&code_challenge_method=. With clientId is provided when creating application, the code_challenge is encrypt plant text, code_challenge_method is method encrypt. Currently, the ATM system only supports 2 methods SHA246 and SHA512. Example:

http://stg.token.tci-pf.net/login?clientId=59uXQOdXyfFyWcZmB9IO&code_challenge=15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225&code_challenge_method=SHA256

After user login successfully, ATM will auto redirect url with code. Example: http://localhost:8080/callback?code=k2j5QFYN66ob0H5H12e50EorNkwxKgLV0XaRjnY9guF9kt8bex.

Now that you've acquired an authorization_code and have been granted permission by the user, you can redeem the code for an access_token to the resource. Redeem the code by sending a POST request to the /auth/token endpoint. Example:

Step 1: Call API to login (Refer to this link)

* Api:
  Name: authenticationApp
  Path: /auth
  Method: POST
  Payload:
{
  "loginId": "end.user13@gmail.com",
  "password": "H123456789",
  "clientId": "OJJvmTmatjCOTuqBxur2",
  "code_challenge": "code_challenge",
  "code_challenge_method":"SHA256"
}
- loginId: Email of user's application
- password: Password of user's application 
- clientId: Get from application
- code_challenge: generate a SHA-256 hexadecimal hash from string
- code_challenge_method: Support SHA256/SHA512


Example:
var axios = require('axios');
const { createHash } = require('crypto');

const code_challenge = createHash('sha256').update('123456789').digest('hex')
s
var data = JSON.stringify({
  "loginId": "end.user13@gmail.com",
  "password": "H123456789",
  "clientId": "OJJvmTmatjCOTuqBxur2",
  "code_challenge": code_challenge,
  "code_challenge_method":"SHA256"
});

var config = {
  method: 'post',
  url: 'https://api.stg.token.tci-pf.net/auth',
  headers: { 
    'Content-Type': 'application/json'
  },
  data : data
};

axios(config)
.then(function (response) {
  console.log(JSON.stringify(response.data));
})
.catch(function (error) {
  console.log(error);
});

Response data:

    {
        "redirectUrl":"http://url-callback",
        "code": "aY3hgPCdKzGj6IDYNJrEzgKazZQsAZ1MkfvaQfFoG8mmPIfocs"
    }

Step 2: Call API to generate token (Refer to this link)

* Api:
  Name: genTokenApp
  Path: /auth/token
  Payload:
{
  "clientId": "MXSAUwpZycuhd2owvV2j",
  "clientSecret": "QhRhyGuSTP7S9cU6riqR3vPHETq9vErX",
  "grant_types": "authorization_code",
  "code": "aY3hgPCdKzGj6IDYNJrEzgKazZQsAZ1MkfvaQfFoG8mmPIfocs",
  "code_verifier": "123456789"
}
- clientId: Get from application
- clientSecret: Get from application
- code_verifier: code before hash
- grant_types: "authorization_code"

Example:
var axios = require('axios');
var data = JSON.stringify({
  "clientId": "MXSAUwpZycuhd2owvV2j",
  "clientSecret": "QhRhyGuSTP7S9cU6riqR3vPHETq9vErX",
  "grant_types": "authorization_code",
  "code": "aY3hgPCdKzGj6IDYNJrEzgKazZQsAZ1MkfvaQfFoG8mmPIfocs",
  "code_verifier": "123456789"
});

var config = {
  method: 'post',
  url: 'https://api.stg.token.tci-pf.net/auth/token',
  headers: { 
    'Content-Type': 'application/json'
  },
  data : data
};

axios(config)
.then(function (response) {
  console.log(JSON.stringify(response.data));
})
.catch(function (error) {
  console.log(error);
});

Response data:

{
    "id_token": "",
    "access_token": "",
    "refresh_token": "",
    "exp_access_token": 600 // Time expiration access token is 600 seconds
}
  1. Refresh the access token

Access tokens are short lived. Refresh them after they expire to continue accessing resources. You can do so by submitting another POST request to the /auth/token endpoint. Provide the refresh_token instead of the code (Refer to this link). Example:

{
    "clientId": "DwSljVC4Ycr5YAv2Qbkf",
    "refresh_token": "",
    "id_token": "",
    "grantType": "refresh_token"
}

Response data:

{
    "id_token":"",
    "access_token":"", // New access token
    "refresh_token":"", // New refresh token
    "exp_access_token": 600 // Time expire access token. 600 seconds
}
  1. How to validate the access token rsa 256

Call API to get the login key (Refer to this link)

Example:

var jwksClient = require('jwks-rsa');
var client = jwksClient({
  jwksUri: 'https://api.stg.token.tci-pf.net/auth/.well-known/jwks.json'
});

function getKey(header, callback){
  client.getSigningKey(header.kid, function(err, key) {
    var signingKey = key.publicKey || key.rsaPublicKey;
    callback(null, signingKey);
  });
}

jwt.verify(token, getKey, options, function(err, decoded) {
  console.log(decoded.foo)
});

Decoded token:

{
  "iat": 1662607837,
  "iss": "atm",
  "exp": 1662608437,
  "aud": "dIwGvPwelNS3Je6AzH41t",
  "scope": {
    "email": "hao.appuser@yopmail.com",
    "id": "a34fZ4NmtQdXuk1k9_2Vt",
    "firstName": "Hao app user",
    "lastName": ""
  }
}
Authorization Code PKCE has loaded