X

Best Practices from Oracle Development's A‑Team

Oracle CX Sales and Service - Outbound REST with x-www-form-urlencoded

Tim Bennett
CX Solution Architect

Introduction

This article shows how outbound REST calls can be made from App Composer Groovy using a content type of x-www-form-urlencoded.

It assumes a basic knowledge of how to call REST APIs from Groovy and follows on from 2 articles on this subject:

https://www.ateam-oracle.com/engagement-cloud-rest-in-brief

https://www.ateam-oracle.com/engagement-cloud-outbound-rest-using-idcs-oauth

 

Note - the method is not described in CX Sales documentation.

 

Background

Engagement Cloud supports 2 payload formats when handling REST calls - JSON and XML. Irrespective of what the Content-Type is set to, the outbound message will be sent as one of these formats.

The method uses the fact that if a JSON formatted payload contains a urlencoded value, but the content type is set to x-www-form-urlencoded, the following happens:

Payload:

{
  "data": "scope=urn:opc:idm:__myscopes__&grant_type=client_credentials"
}

Received Form Values:

Form values
{"data":"scope urn:opc:idm:__myscopes__
grant_type client_credentials"}

 

Note, the receiver has treated the JSON payload as form data and has parsed it correctly into 2 form values (the form key/value pairs are separated by the &). Note what happens when the payload is "topped and tailed" with &:

Payload


{
  "data": "&scope=urn:opc:idm:__myscopes__&grant_type=client_credentials&"
}

 

Received form values:

Form values
{"data":" (empty)
scope urn:opc:idm:__myscopes__
grant_type client_credentials
"} (empty)

 

The above contains 2 empty form values with meaningless keys, and 2 "normal" values with the correct keys. As long as your endpoint ignores empty form values, it will accept and process the above JSON payload correctly as a x-www-form-urlencoded message.

 

App Composer Groovy Example

The method is illustrated via a contrived example which uses an Action button that makes 2 REST calls from Groovy and saves the retrieved details in a custom field. The high level Action button flow is:

Obtain a token from IDCS using the Client Credentials grant - this requires a x-www-form-urlencoded payload

Use the token to call the IDCS user endpoint

Save the user details in a custom field 

 

The purpose of the example is simply to illustrate the technique and is not necessarily a valid use case.

See https://docs.oracle.com/en/cloud/paas/identity-cloud/rest-api/op-oauth2-v1-token-post.html for the IDCS runtime OAuth REST endpoints.

 

IDCS Prerequisites

1) Create a Client Application in IDCS, making sure that Client Credentials is selected as an Allowed Grant Type

2) Note the Client ID and Client Secret

 

Engagement Cloud - App Composer configuration

1) Create web service references to the token endpoint, and user details endpoint:

{{HOST}}/oauth2/v1/token  - use the Client ID and Client Secret from IDCS as the Basic credentials

{{HOST}}/admin/v1/Users

 

2) Create a custom long text field called UserDetails_c on any object and expose it on a detail page

3) Create a new Action on the object and place the Action Button on the detail page

4) The Groovy on the Action button is:

 


// reference to IDCS token service
def tokenSvc = adf.webServices.tokenService

def headers = [:]
def params = [:]

//set the Content TYpe Header
headers.'Content-Type' = 'application/x-www-form-urlencoded'
tokenSvc.requestHTTPHeaders = headers

// put the form data into a single string with the pre/post &
def values = "&scope=urn:opc:idm:__myscopes__&grant_type=client_credentials&"

params.d=values

//call the service and retrieve the bearer token
def token = tokenSvc.POST(params).access_token

// reference to IDCS user service
def userSvc = adf.webServices.userService

// remove the content type header, set the Auth header to the Bearer token obtained above
headers.remove('Content-Type')
headers.Authorization="Bearer ${token}"
userSvc.requestHTTPHeaders = headers

// build filter for the current user
def queryParms = [:] 
queryParms.filter=("userName eq \"${adf.context.getSecurityContext()?.getUserName()}\"").toString()
println(queryParms.filter)

// apply the query parameters to the service 
userSvc.dynamicQueryParams = queryParms 

// Call the IDCS User Service
setAttribute("UserDetails_c", userSvc.GET())

 

 

Final thoughts

So far, this technique has only been tested with OAuth use cases - IDCS, Azure, and OPA. The Authorization mechanisms available in App Composer are enhanced on a regular basis, so perhaps it will become unnecessary, but in the meantime if it works for you, then use it.

 

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha

Recent Content