Developer Deep Dive: Live Experience Authentication

By Oracle Global Pre-sales Consulting

The first step in integrating Oracle Live Experience into your app is gaining access to the service. Live Experience SDK uses a JSON Web Token (JWT) as the access key. It is up to the developer to request a JWT and keep it up to date.

A REST call is used to retrieve a JWT access token. See Oracle Live Experience REST API Reference for more information.

While this REST call can be made from your app directly, it requires your tenant's authentication (client_id/client_secret) credentials. If these credentials change, you'll need to update the app on all devices it has been deployed to. A better approach is to have a web application make the REST call on the device's behalf. This gives you a central point of control and allows integration with an existing enterprise authentication or SSO platform.

An example implementation of centralized authentication in iOS in Authenticate with Live Experience for iOS.

This article will explain what happens after you retrieve a JWT. We will look at the JWT in more detail, waiting for authentication, how to handle JWT expiration, and steps to troubleshoot authentication problems in your application.

Taking a Closer Look at the JWT Access Token

The REST call mentioned above returns the JWT embedded inside a JSON object: (The tokens are shortened to save space):

{
  "access_token": "eyJhbG...",
  "expires_in": "1200",
  "id_token": "eyJhbG...",
  "state": "0",
  "token_type": "Bearer"
}

We are interested in the first two key/value pairs, access_token and expires_in. The JWT itself is stored in access_token and expires_in tells us how many seconds until this JWT is no longer valid.

We will need to parse the JSON to get these two items. For example, let's say the above JSON was in the variable restData.

In JavaScript you'd parse the JSON like this:

var obj = JSON.parse(restData);
var token = obj.access_token;
var expString = obj.expires_in;
var expires = Number(expString);

In Swift on iOS you would parse the JSON like this:

let json = JSONSerialization.jsonObject(with: restData, options: []) as? [String: Any]
let token = json?["access_token"] as? String
let expString = json?["expires_in"] as? String
let expires = Double(expString)

Note:

expires_in is provided as a string so we need to convert it to a number.

If you look at the JWT inside access_token it may appear to be random text, but it actually has a defined structure. The data is Base64Url encoded and includes an RSASHA256 signature. I will spare you the details of JWT decoding, but in case you're curious the payload of a Live Experience JWT looks like this:

{
  "aud": "LiveExperienceDemo1",
  "iss": "auth162442238d0m",
  "exp": 1523983737,
  "server_version": "18.3.1",
  "iat": 1523982537,
  "nonce": "26741",
  "tenant_role": [
    "guest"
  ],
  "username": "1fntfqg6k45nh4o8t3rt"
}

Thankfully, we don't need to worry about what's inside the JWT. We just pass it as is to the Live Experience SDK. For example, in iOS:

Controller.shared.service.authToken = token

Waiting for Authentication

Since access tokens are requested over a network connection the request takes some time to complete. If you attempt to add the Live Experience widget before you have a valid token, the widget will fail to appear in your app. Therefore you need to keep track of the authentication process and wait for it to complete before placing your widget. Network requests happen in a background thread so your app will not wait and continue to run during this time. You will need some kind of notification, callback, or completion handler to let you know when the token has been fetched.

Here is an example of using a completion handler in Swift on iOS to wait for authentication before placing the widget:

fetchToken() {
  Controller.shared.addComponent(viewController: view)
}

Handling Access Token Expiration

The JWT access token is only valid for a finite period of time. Using an expired JWT will cause operations to fail. As you saw above, we are told how long a token is valid through expires_in. This value is normally 1200 seconds or 20 minutes. Expired tokens are not refreshed. We just fetch a new token and pass it to the Live Experience SDK as we did above.

How do you know when the JWT is expired? This is something you need to track yourself. One way is to set a timer in your app to fetch a new token after 1200 seconds. Here is a timer in Swift:

Timer.scheduledTimer(timeInterval: expires, target: self, selector: #selector(fetchToken), userInfo: nil, repeats: false)

Another is to compute an expiration time by adding 1200 seconds to the current system time when the token was retrieved. So, if the time when you fetch the token was 10:00 the expiration time would be 10:20. An example of this in Swift looks like:

let expireTime = Date().addingTimeInterval(expires)

You then compare the current time to the expiration time to see if the token has expired.

Twenty minutes is a long time and you may launch your app several times during that period. Storing your token in persistent storage allows it to survive after the app exits. Here is an example of storing a token and its expiration time in persistent storage with Swift:

UserDefaults.standard.set(token, forKey: "LXtoken")
UserDefaults.standard.set(expireTime, forKey: "tokenExpireTime")

Troubleshooting

As you develop your Live Experience app you will run into bugs related to authentication and token handling. The following information will be useful as you debug your code.

REST Authentication Failure

If your credentials (client_id and client_secret) are invalid, the REST API will issue a 401 HTTP response code with the following payload:

{"description":"Incorrect client credential","error":"invalid_client"}

Verify your credentials match those in the Live Experience tenant Admin Console.

Authentication Succeeds but Call Times Out

If your application-side client uses the same logon credentials as for Associate Desktop, authentication will succeed and you can make calls from your app to Live Experience, but calls will remain stuck in the "Connecting" state and the call will time out.

In brief, calls will fail to connect because you can't call yourself.

To resolve this issue, in the SDK, ensure that CommunicationFragment.service.serUserID("emailAddress") is set to a different email address than the one you use to authenticate with Live Experience.

Network Issues in the SDK

Once you have passed a token to the SDK it is used for the SDK's own network requests. If these network requests fail the Live Experience SDK provides no notification to your application. The only way to see if a failure has occurred is to monitor the console log of your application for HTTP response errors. Here are some common failure scenarios and how they appear in the console.

Note:

These examples use the iOS console. The JavaScript and Android consoles produce similar error messages.

Scenario Does Not Exist

Live Experience allows you to define engagement scenarios for your application. You select the engagement scenario you want to use by setting the *appLocation* context attribute in the SDK. If you choose a scenario that has not been defined in the Admin Console, the widget will not appear. When this happens, look for HTTP status code 404 in the console:

OracleLive | ERROR | ScenarioClient.swift:setupScenario(completionHandler:):46 |
Error when fetching scenario. HTTP Response Status code: 404
OracleLive | ERROR | ScenarioClient.swift:setupScenario(completionHandler:):51 |
Http response status is not correct 
Optional(<NSHTTPURLResponse: 0x6040000306a0> { URL: 
https://live.oraclecloud.com/tenant/api/tenants/Demo1/match-meta-scenario/?version=18.3.2 } 
{ Status Code: 404, Headers {
OracleLive | ERROR | Controller.swift:updateScenario(completionHandler:):697
Could not get scenario Collaboratio: Error Domain=OracleLive Code=101 "(null)"
OracleLive | ERROR | ScenarioClient.swift:setupScenario(completionHandler:):46 |
Error when fetching scenario. HTTP Response Status code: 404
OracleLive | ERROR | ScenarioClient.swift:setupScenario(completionHandler:):51 |
Http response status is not correct 
Optional(<NSHTTPURLResponse: 0x604000033420> { URL: 
https://live.oraclecloud.com/tenant/api/tenants/Demo1/match-meta-scenario/?version=18.3.2 } 
{ Status Code: 404, Headers {
OracleLive | ERROR | Controller.swift:updateScenario(completionHandler:):697 |
Could not get scenario Collaboratio: Error Domain=OracleLive Code=101 "(null)"

Wrong Tenant Specified

You specify your Live Experience tenant by setting the Controller.shared.service.tenantID attribute. If the access token you retrieved is for a different tenant, HTTP status 500 will appear:

OracleLive | ERROR | AvailabilityClient.swift:getAvailability():44 | 
Error when fetching agent availability. Status code: 500
OracleLive | ERROR | AvailabilityClient.swift:getAvailability():49 | 
Http response status is not correct 
Optional(<NSHTTPURLResponse: 0x60000003d540> 
{ URL: 
https://live.oraclecloud.com/tenant/api/tenants/Demo1/application/?version=18.3.2 
} 
{ Status Code: 500, Headers {

Invalid Token

If you pass a token that has already expired to the Live Experience SDK, you will see HTTP status 401 in the console:

OracleLive | ERROR | ScenarioClient.swift:setupScenario(completionHandler:):46 | 
Error when fetching scenario. HTTP Response Status code: 401
OracleLive | ERROR | ScenarioClient.swift:setupScenario(completionHandler:):51 | 
Http response status is not correct Optional(<NSHTTPURLResponse: 0x600000430300> {
URL: 
https://live.oraclecloud.com/tenant/api/tenants/Demo1/match-meta-scenario/?version=18.3.2 } 
{ Status Code: 401, Headers {

Call Attempt After a Token Has Expired

If your token expires after the widget is already displayed in your app, nothing will happen. If you click on the call button, however, you will see the following console error message:

OracleLive | ERROR | CallQueueClient.swift:pushContext():184 | Response 401, fail

You will also see a popup in your application's UI.

Return to the Docs Home Page

Live Experience Get Started