Securely Consume Your WSO2 Cloud APIs From Mobile/Single-Page Applications

Erandi Ganepola
The Startup
Published in
8 min readOct 27, 2020

--

Unlike confidential clients, public clients such as applications running in a browser or on a mobile device are unable to keep registered client secret safe. Hence there should be a secure way of addressing this use case. In this article, we will discuss how to securely invoke your WSO2 API Cloud APIs from a public client with generic try out sample requests.

We have explained this scenario’s implementation for a mobile application using Flutter with our second article -“Flutter mobile application to consume your WSO2 Cloud APIs with PKCE”.

Problem

When invoking APIs securely, embedding the credentials/access tokens (ex: client-secrets) in app distributions is not recommended because apps can be decompiled and sensitive data can be exposed. Also, public clients such as single page applications source code is available in browser end. Hence unable to keep registered client secret safe.

As a security mechanism, we can use the “authorization code grant type” for authorization. It authenticates with an authorization server using a combination of client-id and authorization code. But then again authorization code can be intercepted by a third party causing security threats.

Solution Overview

Choosing recommended security standards

When developing SSO mobile applications or web applications securely, the recommended way for public clients is “authorization code grant with proof key for code exchange. Refer to the RFC-7636 spec on Proof Key for Code Exchange (PKCE) by OAuth Public Clients for more details.

Authorization code grant with PKCE is introduced to mitigate the authorization code interception attacks and effects of embedding client credentials in app distributions/source codes. Therefore PKCE will make authorization flow more secure by providing a way to generate a code-verifier and code challenge that are used when requesting the access token. So that an attacker who intercepts the authorization code can’t make use of the stolen authorization-code.

Implementing security and more

Implementing security standards by ourselves is time consuming. Additionally, monitoring, throttling and many more are required to be handled. In summary, we need a matured API Management solution to address those.

When considering the above factors, WSO2 API Cloud is a great solution. Refer to this article for more details on selecting the right API Management solution — A Guide to Selecting the Right API Management SaaS.

Prerequisites to setup in WSO2 Cloud

  • Create an account in WSO2 Cloud if you don’t have one already and login to Publisher portal.
  • Create an API using an existing swagger definition. You can find the relevant swagger in swagger.yaml file. When creating your API, set context as /demo and set the production, sandbox backend endpoints to https://restcountries.eu/rest/v2.
    Also, select Subscription Tiers in Manage tab as necessary (We have set to Unlimited in our API). Then publish your API. At the end of this article your API will be calling REST Countries Capital City endpoint as the backend.
  • Visit WSO2 API Store and create an application. Enable (put a tick for the Code grant box) code grant with the callback URI. We have set callback URI to http://localhost:8000 in this sample. Then generate keys. You will need client_id (consumer key) and client_secrete (consumer secrete) values for the future use in this article.
  • Subscribe to previously published API from the newly created application.

Note:
If you need to try out this scenario without exposing your client secret (without sending your client_secret in any of the authentication requests) which is the recommended way, you need to enable “Allow authentication without client secret configuration” under the OIDC service provider config in WSO2 API Cloud to use Authorization code grant type with PKCE without client secret.

For that please send an email to cloud@wso2.com to configure it for your application mentioning your tenant domain and client application name.
If not, you can proceed with the default configurations in WSO2 Cloud and skip this step.

Authentication and Invocation Flow

Let’s identify the flow of this scenario. We can divide the complete flow into the following sections.

  • Authentication via Authorization code grant with PKCE
  • API invocation with a valid access token
  • API invocation with an invalid access token (refreshing access token)

Now let’s go through the above sub-topics one by one in detail.

Authentication Flow

In the following diagram, we have shown how login and token generation flows work with Authorization code grant with PKCE for a mobile application.

Let’s assume user needs to login to a mobile application and search for a capital of a country to get more information. Once user clicks the“Login” button of the application, mobile browser opens “/authorize” endpoint in the browser.

You can try out this same scenario as follows with your browser and couple of CURL requests itself. Since it’s convenient for all, let’s try this sample in that simplest way.

Try out the following steps

1. Let’s create a simple verifier.
It needs to have a minimum length of 43 characters and a maximum length of 128 characters in length to be compatible with the specification - RFC7636.

Ex: 12345678901234567890123456789012345678901234567890

2. Encode your verifier with base64. You can use an online tool or the Linux command as follows:

echo <verifier> | base64

Following is the example base64 encoded verifier.

MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTA

3. Next generate code challenge from code verifier.
You need to take the SHA256 hash of the verifier first. Then encode it from base64.
You can use the following command in Linux. Replace <base64 encoded verifier> with your value.

echo -n <base64 encoded verifier> | openssl dgst -binary -sha256 | openssl base64

4. Then resulting code_challenge will look like this:

cCKis6m4FKRN6dlJo9sJ4irCXlleTp7Sf1JRqvlVcn

Note:
Check your Base64 encoded code verifier and the code challenge, whether any of them contain “=” or “+” or “/” in the encoded values. If yes, those should be updated as follows:

* Replace each “=” with empty string

* Replace each “+” with “-”

* Replace each “/” with “_”

5. Copy paste the following request in your browser URL after replacing <your_client_id> and <your_code_challenge> values into your ones.

  • client_id : Consumer ID you obtained in ‘Prerequisites to setup in WSO2 Cloud’ step by creating an application in WSO2 API Store.
  • code_challenge : Value we obtained in the previous step (step 04).
  • code_challenge_method : Code challenge algorithm used in this case is SHA256, denoted as S256 in below URL.
  • redirect_uri : Call back URI we configured when creating the application in ‘Prerequisites to setup in WSO2 Cloud’ step.
  • scope : OAuth2 scopes required (which permissions should be delegated to the client)
  • state: A random string used for CSRF protection. Also this gives your app a chance to persist data between the user being directed to the authorization server and back again, such as using the state parameter as a session key.
https://gateway.api.cloud.wso2.com/authorize?response_type=code&client_id=<your_client_id>&code_challenge=<your_code_challenge>&code_challenge_method=S256&redirect_uri=http://localhost:8000&scope=openid,profile,offline_access&state=xyzABC123

Then you will be redirected to the following UIs. There you need to enter your credentials and grant access to get the auth code.

If authorization is successful, you will get the code in the browser URL as shown in the following image:

Note:
In this case, we don’t have an actual web app running on http://localhost:8000. But in a real use case, you should implement your SPA web app/mobile app to handle this callback by extracting the code from the URL.

In case of a mobile app, we start the “redirect_uri” from the app id (ex: org.wso2.demoapp://auth-callback) so that our app will be prompted automatically upon successful redirection.

6. Next send a POST request to obtain access and refresh tokens as follows.

You have to replace tenantDomain, client_id, client_secret, code_verifier and code with your values.

Note:
If you have enabled “Allow authentication without client secret configuration” under the OIDC service provider config in WSO2 API Cloud to use Authorization code grant type with PKCE without client secret which is the recommended way, that we have mentioned in “Prerequisites to setup in WSO2 Cloud” section, you do not have to pass client_secret to any of the CURL requests we are using in this section.

curl --request POST \
--url 'https://gateway.api.cloud.wso2.com/token?tenantDomain=<your_tenant_domain>' \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=authorization_code' \
--data 'client_id=<your_client_id>' \
--data 'client_secret=<your_client_secret>' \
--data 'code_verifier=<your_code_verifier>' \
--data 'state=xyzABC123' \
--data 'code=<your_code>' \
--data 'redirect_uri=http://localhost:8000' -v

Then you will receive a response as follows with tokens:

{
“access_token”:”5a144abb-753f-3a74-b4ce-xxxxxxxxx",
”refresh_token”:”9073a35e-04ee-319f-98d4–xxxxxxxxx”,
”scope”:”default”,
”token_type”:”Bearer”,
”expires_in”:3600
}

API invocation with a valid access token

Following diagram shows how to invoke an already published API using a valid access token via a mobile application.

Now let’s invoke your already published API via a CURL command for your ease. You have to use the access token retrieved from the previous step in the CURL request.

Replace your tenant domain in the URL (replace the word ‘‘erandiorg’’ with your tenant domain), replace the word “colombo” with a capital of a country as you like, replace <access_token> with the token you obtained in the previous step.

curl -k -X GET “https://gateway.api.cloud.wso2.com/t/erandiorg/demo/v1.0/capital/colombo" -H “accept: application/json” -H “Authorization: Bearer <access_token>” -v

If you have done everything correctly, you will get a success response similar to the following with status code 200. Congratulations!!

[
{
"name":"Sri Lanka",
"topLevelDomain":[
".lk"
],
"alpha2Code":"LK",
"alpha3Code":"LKA",
"callingCodes":[
"94"
],
"capital":"Colombo",
....
"languages":[
{
"iso639_1":"si",
"iso639_2":"sin",
"name":"Sinhalese",
"nativeName":"සිංහල"
},
{
"iso639_1":"ta",
"iso639_2":"tam",
"name":"Tamil",
"nativeName":"தமிழ்"
}
],
"flag":"https://restcountries.eu/data/lka.svg",
"regionalBlocs":[
{
"acronym":"SAARC",
"name":"South Asian Association for Regional Cooper* Connection #0 to host gateway.api.cloud.wso2.com left intact
ation",
"otherAcronyms":[

],
"otherNames":[

]
}
],
"cioc":"SRI"
}
]

API invocation with an invalid access token (refreshing access token)

Following diagram shows an API invocation with an invalid access token.

Access tokens are getting expired after a defined period of time (by default it’s 3600s). In that kind of situation, when user searches a capital of a country, by sending the GET request to API Cloud gateway with an invalid access token, validation gets failed and response comes with a error message and 401 status code.

Now you need to refresh your access token. You can do it as follows.

Replace your tenantDomain in the URL (replace the word ‘‘erandiorg’’ with your tenant domain), client_id, client_secret, refresh_token with the values you obtained in the previous step.

curl --request POST \
--url 'https://gateway.api.cloud.wso2.com/token?tenantDomain=erandiorg' \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=refresh_token' \
--data 'client_id=c86yvBYoLhvqnzX3u0xxxxxx' \
--data 'client_secret=rdGWZE14i8AnswQ1AN4xxxxxx' \
--data refresh_token=9073a35e-04ee-319f-98d4–xxxxxxxxx9073a35e-04ee-319f-98d4–xxxxxxxxx -v

Then you will receive another valid (valid for 3600s by default) access token for your use:

{
“access_token”:”c1a3738e-44c0–3775–98bb-xxxxxxxx”,
”refresh_token”:”c48b8e4d-95c4–35d7–8535–xxxxxxx",
”scope”:”default”,
”token_type”:”Bearer”,
”expires_in”:3600
}

That’s all about the authentication with PKCE, token generation and API invocation flows.

Hope you enjoyed the article!

Let’s meet with our second article (“Flutter mobile application to consume your WSO2 Cloud APIs with PKCE”) to figure out how we have implemented this flow with a Flutter mobile application.

--

--

Erandi Ganepola
The Startup

Solutions Architecture | Thinker | @WSO2 LLC for North America