How to Implement OAuth with PKCE using Okta & API Management

If you are building a mobile application, then the authorization code flow with a Proof Key for Code Exchange (PKCE) is the recommended method for controlling the access between your application and a resource server.

The Authorization Code Flow with PKCE is the standard Code flow with an extra step at the beginning and an extra verification at the end.

At a high-level, the flow has the following steps:

  • Your application generates a code verifier followed by a code challenge.
  • Your application directs the browser to the Okta Sign-In page, along with the generated code challenge, and the user authenticates.
  • Okta redirects back to your mobile application with an authorization code.
  • Your application sends this code, along with the code verifier, to Okta. Okta returns access and ID tokens, and optionally a refresh token.
  • Your application can now use these tokens to call the APIs in the resource server (i.e. The Axway API Management) on behalf of the user.

Set up your Application in Okta with PKCE

Set up your OpenID Connect application inside the Okta Developer Console:

  1. From the Applications page, choose Add Application:

  1. On the Create New Application page, select Native.

  1. Fill-in the Application Settings, then click Done.
    • Enter the configuration values for the App. (this can be changed any time)
    • For Login Redirect URL, (if testing using Postman use https://www.getpostman.com/oauth2/callback)
    • Select the Grant types allowed. The “Authorization Code” is selected by default.

  1. Click Done and your App is now ready to be used.

Since we are using PKCE for this use case, we only need to use the Client ID. (If Client Authentication is used instead, that option can be selected here. This will display the Client Secret.)

Configure your app in API Manager

Next, you will need to Login to the API Manager to create an App and use the Okta Client ID

Build the Okta Token Verification Policy in Policy Studio

It does the following:
1 – Creates the Rest Request with the clientid,secret and access token
2 – Calls the Okta Introspect URL to validate if the access token is still valid.  (Example: https://{yourOktaDomain}/oauth2/default/v1/introspect )
3 – If Valid – extract the token and check if the token is Valid, the client id, scopes are valid. Return True.
4 – If Invalid – return False.

Policy is also attached with this document


Add the Policy Under the OAuth Token Information Policies

And then Deploy, so the Policy is available in API Manager.

Secure your API in API Manager with the Policy

  1. Import a backend API and virtualize as Frontend API.
  2. Select OAuth (External) as FrontEnd Authentication from the drop-down.
  3. Select the policy previously created from the Token Information policy dropdown.
  4. Publish the API, grant the API access to the right Organization (the Organization where the App was created).
  5. Go to the App that was created and Grant API Access.
  6. The API is now ready to be tested.

Congratulations! You are now ready to accept Okta users!

Use the Authorization Code Flow with PKCE

Just like with the regular authorization code flow, you start by making a request to your authorization server’s /authorize endpoint. However, in this instance, you will also have to pass along a code challenge.

Your first step is to generate a code verifier and challenge:

  • Code verifier: Random URL-safe string with a minimum length of 43 characters.
  • Code challenge: Base64 URL-encoded SHA-256 hash of the code verifier.

For this example, we will create output like this:

{   "code_verifier":"M25iVXpKU3puUjFaYWg3T1NDTDQtcW1ROUY5YXlwalNoc0hhakxifmZHag",   "code_challenge":"qjrzSW9gMiUgpUvqgEPE4_-8swvyCtfOVvg55o5S_es" }

The code_challenge is a Base64-URL-encoded string of the SHA256 hash of the code_verifier. Your app will save the code_verifier for later, and send the code_challenge along with the authorization request to your authorization server’s /authorize URL.

Using the default Okta authorization server, then your request URL would look something like this:
https://{yourOktaDomain}.com/oauth2/default/v1/authorize?client_id=0oaf2no1n6hPswFV10h7
&response_type=code&scope=openid&redirect_uri=https://www.getpostman.com/oauth2/callback&st ate=state-8600b31f-52d1-4dca-987c-386e3d8967e9&code_challenge_method=S256&code_ challenge=qjrzSW9gMiUgpUvqgEPE4_-8swvyCtfOVvg55o5S_es

Note the parameters that are being passed:

  • client_id matches the Client ID of your Okta OAuth application that you created above. You can find it at the bottom of your application’s General tab.
  • response_type is code, indicating that we are using the authorization code grant type.
  • scope is openid, which means that the /token endpoint will return an ID token.
  • redirect_uri is the callback location where the user-agent will be directed to along with the code. This must match one of the “Login redirect URIs” you specified when you were creating your Okta application in Step 1.
  • state is an arbitrary alphanumeric string that the authorization server will reproduce when redirecting the user-agent back to the client. This is used to help prevent cross-site request forgery.
  • code_challenge_method is the hash method used to generate the challenge, which will always be S256.
  • code_challenge is the code challenge used for PKCE.

Using the browser, paste the Request URL build above. Enter your Okta credentials to login. If you do not have an existing session, this will open the Okta Sign-in Page.


If they have an existing session, or after they authenticate, they will arrive at the specified redirect_uri along with an authorization code:

https://app.getpostman.com/oauth2/callback?code=9hEsVbqAX7AV2hboadpd&state=
state-8600b31f-52d1-4dca-987c-386e3d8967e9

Important: This code can only be used once, and will remain valid for 60 seconds, during which time it can be exchanged for token.

Exchange the Code for Tokens

To exchange this code for access and ID tokens, pass it to your authorization server’s /token endpoint along with the code_verifier that was generated at the beginning:

Using Postman here to build the Request.

Important: Unlike the regular Authorization Code Flow, this call does not require the Authorization header with the client ID and secret. This is why this version of the Authorization Code flow is appropriate for native apps.

Note the parameters that are being passed:

  • grant_typeis authorization_code, indicating that we are using the authorization code grant type.
  • redirect_uri must match the URI that was used to get the authorization code.
  • code is the authorization code that you got from the /authorize
  • code_verifier is the PKCE code verifier that your app generated at the beginning of this flow.

If the code is still valid, and the code verifier matches, your application will receive back access token and ID tokens in JWT format:

{
"access_token": "eyJraWQiOiJCTjBsMm9ZXzQyYjd1dkxobnBKd1U4QWIySzRHa3dya3IxcWc5WDFsRVZJIiwiYWxnIjo
iUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULjFIZnYtR0wzVHg1eENJTWpXcVVHZzVhMkY0TmlnQlpxMVJxMUlBNk94TGsiLCJ
pc3MiOiJodHRwczovL2Rldi0zOTI0NTUub2t0YXByZXZpZXcuY29tL29hdXRoMi9kZWZhdWx0IiwiYXVkIjoiYXBpOi8vZGVm
YXVsdCIsImlhdCI6MTUyNjU5MzY4MSwiZXhwIjoxNTI2NTk3MjgxLCJjaWQiOiIwb2FmMm5vMW42aFBzd0ZWMTBoNyIsInVpZCI
6IjAwdWVsY29yZGpINVpmdWtmMGg3Iiwic2NwIjpbIm9wZW5pZCJdLCJzdWIiOiJhZG9yYWlyYWphbkBheHdheS5jb20ifQ.
Jmq6O0DfpgQo_87ZPRHovlSdR7gB1klRKwCWWCrLusPAExA0ZjYCD6X62oDj_gwIAqX5lQBhc8qVdjkfbV_4KSpXvJjpljSz
qy3CiF733iaabJJZpfg2dN9rr4y-i2V_ck4GDfaqMZB1ZxoZjcLeq5qSf-0VcSqxrdAoUqTgN12a1HvwDW79EBaD3fVH2JUSD
kh03ZlCfeZIn5e3B3Gyi8EThN-VP_Saoavu1QNb_e3h3RSTo29gP9RiVpVtbgo1uL9kDHxVonVy3StDViWkJ_kwipL996NTuVcP
dbCrTpwk6gOtJQcAss6kYsQIXMZzNqwYeKZH_z1DZb4iWIDaAA",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid",
"id_token": "eyJraWQiOiJCTjBsMm9ZXzQyYjd1dkxobnBKd1U4QWIySzRHa3dya3IxcWc5WDFsRVZJIiwiYWxnIjoiUlMy
NTYifQ.eyJzdWIiOiIwMHVlbGNvcmRqSDVaZnVrZjBoNyIsInZlciI6MSwiaXNzIjoiaHR0cHM6Ly9kZXYtMzkyNDU1Lm9rd
GFwcmV2aWV3LmNvbS9vYXV0aDIvZGVmYXVsdCIsImF1ZCI6IjBvYWYybm8xbjZoUHN3RlYxMGg3IiwiaWF0IjoxNTI2NTkz
NjgxLCJleHAiOjE1MjY1OTcyODEsImp0aSI6IklELk5FbjJ0YlhaNnNob0M5YzVMMklRQWh1SU00QUVIdFFGRFhMc0RQM1AzU
1UiLCJhbXIiOlsicHdkIl0sImlkcCI6IjAwb2VsYzVqM3ZZa21yYUNHMGg3IiwiYXV0aF90aW1lIjoxNTI2NTkzMjY1LCJhdF9o
YXNoIjoib0IweTVLaTZZNzA4UmxsSnJFMU1xUSJ9.VySjeJrGJoaYMUJOblBiPWy9tRIV1dntHOsHHDQD0tehx2KkditrzvCCCY
hZqSSytGhOz5fokpdEZ6Z30MrG2_TiwcjQBOjV8OnyvS3rFp6G-EPBb4PkCPLYlSqN-aothF56N33MEAVvDd74HtXrJsC8DNuKL
zJvGalbN-_gzBP55gboqwmUC_lJx7rjWqTJXNTLB0XqPFSIZs6eKPwEoo4mPG_ynAikwHSt71cgxJwnXxOKxsTTS4hC8RHRXHuP
zWRyhja52HkeV-96rbCL2DjNix--R1FvFyF6L2yuWq95s9ZL6DnuPBV01eBNN6pBB733kdf8cL1bZFHnKDnYhw"
}

Now, test the API secured in API Manager with the JWT Access Token:

Finally, check the API Gateway to review the results: