This UDP is to provide an APIC API to call the backend with a JWT, rather than just Username/Password on the Invoke policy.
For versions of APIC v10.x
More details about user-defined policies (aka custom policies) may be found in the IBM API Connect documentation: User-defined policies.
Today, there's only a basic authentication call from the APIC Invoke Policy to target systems.
This custom policy will enhance the Invoke Policy to be able to use a JWT to call target systems, allowing for the request from APIC to backend target systems to authenticate a JWT rather than Basic Authentication.
Please familiarize yourself with the JWT-Auth-Invoke-policy.yaml in the JWT-Auth-Invoke-policy.zip in this repo.
The properties are set accordingly per diagram below for the user to input values during design time.
During runtime, the property values will be assigned to the local.parameter() keys, respectively in memory of the gateway, and could be called upon within the Assembly section as shown in the diagram below:
The assembly section contains the policies similar to the OOTB policies encapsulated into this custom policy.
- set-variable: takes the values input from the custom policy and set it to the value for each key. For example, the udp-audclaim that is set in the Properties section will take what the developer inputs, and, during runtime, value will be set to the $(local.paramter.udp-audclaim) in the header aud-claim. These are headers set for the Invoke policy.
- set-variable:
version: 2.0.0
title: setVar for JWT-call
actions:
- set: message.headers.x-ibm-client-id
value: $(local.parameter.udp-clientid)
type: any
- set: message.headers.x-ibm-client-secret
value: $(local.parameter.udp-clientsecret)
type: any
- set: message.headers.aud-claim
value: $(local.parameter.udp-audclaim)
type: any
- set: message.headers.content-type
value: 'application/json'
type: any
- set: message.headers.accept
value: 'application/json'
type: any
- gatewayscript: gws:Logger – is not necessary to the functionality of the solution but it logs details for debugging. As you will notice the script only has console logging, which will output the system logs. You will see these outputs in the trace section in the test tool.
- gatewayscript:
version: 2.0.0
title: 'gws:Logger'
source: >-
var apim = require('apim');
console.log('****Completed setVar for JWT-call****');
console.debug('****local.parameter-OUTPUT: ' +
JSON.stringify(apim.getvariable('local')));
console.debug('****Setting the following clientid: ' +
apim.getvariable('local.parameter.udp-clientid'));
console.debug('****Setting the following clientsecret: ' +
apim.getvariable('local.parameter.udp-clientsecret'));
console.debug('****Setting the following aud-claim: ' +
apim.getvariable('local.parameter.udp-audclaim'));
console.log('****Sending data for JWT****');
- Invoke: invoke-JWT-gen – similar to the Invoke policy in the Assemble design, this will take the user input value from
$(local.parameter.udp-jwtgenurl)
and$(local.paramter.udp-ttl)
reflected in the diagram below and source code.
- invoke:
title: invoke-JWT-gen
version: 2.0.0
verb: GET
target-url: $(local.parameter.udp-jwtgenurl)
follow-redirects: false
timeout: 60
parameter-control:
type: blocklist
values: []
header-control:
type: blocklist
values: []
inject-proxy-headers: false
backend-type: json
cache-response: time-to-live
cache-ttl: $(local.parameter.udp-ttl)
output: jwt-response
stop-on-error: []
- gatewayscript: The last gatewayscript will log the jwt response from the Invoke in debug level logging, the last 5 characters of the jwt in normal level logging, and set the jwt to the authorization header to be used for the proceeding policy.
- gatewayscript:
version: 2.0.0
title: gatewayscript
source: >+
console.log('****Invoke to JWT Gen completed****');
var apim = require('apim');
console.debug('****Full response from JWT Gen: ' + JSON.stringify(apim.getvariable('jwt-response')));
var vJWTResponse = (apim.getvariable('jwt-response').body).toString();
console.debug('****jwt-response.body from JWT Gen invoke: ' + vJWTResponse);
apim.setvariable('message.headers.authorization', vJWTResponse);
var vObfuscatedJWT = vJWTResponse.substr(vJWTResponse.length - 5);
console.log('****JWT set to Authorization header (showing-last-5-characters-only): ' + vObfuscatedJWT);
var vJWTGenUrl = apim.getvariable('local.parameter.udp-jwtgenurl');
console.log('****JWT-gen URL: ' + vJWTGenUrl);
console.debug('****local output: ' + JSON.stringify(apim.getvariable('local')));
NOTE: When you use this custom-JWT-udp policy, you may get the following error:
This is complaining about the cache-ttl: $(local.parameter.udp-ttl)
within the invoke policy because it expects a string/numeric value, thus the special characters triggers a schema validation error.
Run the following steps to avoid this issue: Turn off the schema validation in the catalog by going to the Catalog Setttings > Publish validations > Edit > uncheck the Validate custom policy schema in assembly
and Save.
This test will only test the generation of the JWT with the audience and issuer claim set to the token. The client id and secret validation are turned off for this test.
Here is a jwt-generate api service that will be the JWT provider, which will be generating the JWT that ultimately is invoked to the backend.
You may published this on APIC and use this for the UDP to call the service in order to get the JWT.
jwt-generate_1.0.0.yaml
- Create a new API with the UDP inputting the audience-claim required for the JWT (as shown in the diagram below). Do not apply any security schemes for this test.
NOTE: You may get the jwt-generate api service endpoint from that apis Endpoint section:
-
Publish the API.
NOTE: You will notice the following error below. You may ignore that, but please republish the api.
-
Once published, go to the Test tab of the api and click Send to invoke the jwt-generate service.
At this point, the jwt-udp api you've just tested has called the jwt provider api to retrieve a token, to which then the JWT is assigned to the Authorization header for it to be avilable downstream in a backend call.
You will see from the diagram below that the header contains "authorization" from the syslog trace, which will flow down stream to the next policy, which may be an Invoke policy to a backend service that requires the JWT.