28 October 2021

1. Token Resolver

Some destinations, like the Http destination, may require some additional tokens to be established prior to performing the actual http reqeust. To support that, you can register token resolvers within the destinations file.

<Destinations>
    <TokenResolver id="keycloak" cacheFor="300s">
        ...
    </TokenResolver>
</Destinations>

You can define as many token resolvers as needed, just ensure that each have a unique identifier.

The cacheFor directive specifies the duration how long the generated tokens may be reused. The tokens are cached per url/header/payload combination, meaning that if your token resolver uses dynamic values, the caching is sensitive to the resolved values and thus each token resolver may contain many cache entries.

1.1. Configuration Format

On a high level, the token resolver configuration contains three parts:

Request

Defines how the request will be constructed when requesting the tokens. There is support for constructing requests using either x-www-form-url-encoded key/value pairs or JSON request bodies.

Response

Defines what data from the response we are interested in when contructing our tokens. Currently you can take values from response headers and/or JSON values from the response body.

Tokens

Specifies the tokens to be used and how to build these.

Below is a complete example for a token resolver that works against Keycloak to establish an authorization token according to the OAuth2 client credentials flow.

<Destinations>
    <TokenResolver id="keycloak-1" cacheFor="5m">
        <Request url="http://localhost:30180/auth/realms/${job.param.keycloak_realm}/protocol/openid-connect/token">
            <Params>
                <Param name="grant_type" value="client_credentials" />
                <Param name="scope" value="${job.param.keycloak.scopes}" />
                <Param name="client_id" value="tif" />
                <Param name="client_secret" value="${tif.setting.keycloak.tif.secret}" />
            </Params>
        </Request>
        <Response>
            <Json>
                <Map jsonPath="$.access_token" as="access_token" />
                <Map jsonPath="$.token_type" as="token_type" />
            </Json>
        </Response>
        <Tokens>
            <Token name="Authorization" macro="${token_type} ${access_token}" />
        </Tokens>
    </TokenResolver>

    <Http id="http-dest-with-keycloak" tokenResolver="keycloak-1" url="..." />
</Destinations>

In this example, the request is a param name/value request and the response is of type JSON.

It is also possible to send out JSON data and map the response headers into tokens, see example below:

<TokenResolver id="monitor" cacheFor="24H">
    <Request
        url="http://monitor:8001/${tif.setting.monitorLanguageCode}/${job.param.companyNumber}/login">
        <Json> <!-- implies application/json + method = POST -->
            <Object>
                <Property name="Username" string="${job.param.monitorUserName}" />
                <Property name="Password" string="${job.param.monitorPassword}" />
                <Property name="ForceRelogin" bool="true" />
            </Object>
        </Json>
    </Request>
    <Response>
        <Header name="X-Monitor-SessionId" as="sessionId" />
    </Response>
    <Tokens>
        <Token name="X-Monitor-SessionId" value="${sessionId}" />
    </Tokens>
</TokenResolver>

The details of each element is described below.

1.2. <TokenResolver>

The top level element <TokenResolver> supports the following attributes.

Attribute Description Required

id

A unique identifier among all token resolvers.

Yes

cacheFor

An optional duration definition specifying how long time the token may be cached.

If omitted, no caching takes place. Example values:

  • 30s

  • 3m30s

  • 1h

  • 1h30m

  • 1d

  • 1d12h

No

Supported child elements:

  • <Request>

  • <Response>

  • <Tokens>

1.3. <Request>

Specifies how the request will be constructed.

This element supports the following attributes

Attribute Description Required

url

The URL to perform the request against

Yes

method

Specifies request method. If omitted, GET is used in case no body is configured and POST is used if a body is configured.

No

contentEncoding

Can be used to control the content encoding. Default is UTF-8

No

proxyOverride

Can be used to override global proxy settings in TIF

No

disableProxy

Can be used to disable using proxy server for token resolving.

No

The request will per default use the global http/https proxy setting unless overridden via the proxyOverride or disableProxy attributes.

See this document for details around proxy settings in TIF.

Supported child elements:

Element Description Required

<Json>

Defines that you will send JSON data to the server.

No

<Params>

Defines that you will send key/value parameters

No

<Header>

Defines additional headers to be part of the request

No

1.3.1. <Json>

The Json element is used to declare a JSON structure to be generated

See example below how to construct such Json definition:

<Request ...>
    <Json>
        <Object>
            <Property name="Username" string="${job.param.monitorUserName}" />
            <Property name="Password" string="${job.param.monitorPassword}" />
            <Property name="ForceRelogin" bool="true" />
            <Array name="test">
                <Property int="1" />
                <Property int="2" />
            </Array>
        </Object>
    </Json>
</Request>

This will generate a JSON payload like the example below:

{
    "Username" : "the resolved value from the job param monitorUserName",
    "Password" : "the resolved value from the job param monitorPassword",
    "ForceRelogin" : true,
    "test" : [ 1, 2 ]
}

You can use <Object>, <Array> and <Property> to define the structure.

The Property element supports the following attributes:

Attribute Description

string

Defines that the value is of type string

bool

Defines that the value is of type boolean

int

Defines that the value is of type int64

double

Defines that the value is of type double

number

Defines that the value is of type number

splitValueBy

Specify optional separator that can be used to split the value into an array instead

1.3.2. <Params>

The <Params> element is used to define a payload that is x-www-form-urlencoded (e.g. a form post).

Example:

<Request ...>
    <Params>
        <Param name="grant_type" value="client_credentials" />
        <Param name="scope" value="${job.param.keycloak.scopes}" />
        <Param name="test" value="a,b,c,d" splitBy="," />
    </Params>
</Request>

This will generate a payload like below

grant_type=client_credentials&scope=s1+s2+s3+s4&test=a&test=b&test=c&test=d

The <Params> element contains one or more <Param> elements, which supports the following attributes.

Attribute Description Required

name

Name of parameter

Yes

value

The value of the parameter

Yes

splitValueBy

Specify optional separator that can be used to split the value into an array instead

No

Defines additional request headers.

Example:

<Request ...>
    <Header name="h1" value="${job.param.dynamicValue}" />
    <Header name="h2" value="some static value" />
</Request>

Each <Header> element needs to have a name and value attribute.

1.4. <Response>

The <Response> element defines what data you are interested in from the response. You can save values from headers and/or data from a JSON response body.

The idea is that you will define what data you are interested in and associate that with a variable. Later on, you will use those variables to construct the final tokens.

When working with JSON response data you will use so called JSON paths to select data from the JSON structure to be saved.

For more detailed information about JSON path, see this document

Below is an example where the properties token_type and access_token are saved from the JSON object. Note that selecting anything else than plain literals (strings, numbers, booleans) are typically not supported.

<Response>
    <Json>
        <Map jsonPath="$.token_type" as="tokenType" />
        <Map jsonPath="$.access_token" as="accessToken" />
    </Json>
</Response>

The <Map> element supports the following attributes

Attribute Description Required

jsonPath

The JSON path expression to use

Yes

as

The name of the variable to save the value as

Yes

To save some header values into a named variable, see the example below:

<Response>
    <Header name="X-Monitor-SessionId" as="sessionId" />
    <Header name="Another-Header" as="another" />
</Response>

The <Header> element supports the following attributes

Attribute Description Required

name

Name of header to take

Yes

as

The name of the variable to save the value as

Yes

ignoreCase

If header names are case sensitive or not. Default is to ignore case.

No

1.5. <Tokens>

The final tokens to use are defined within the <Tokens> element.

See example below where the Authorization token is generated based upon the variables token_type and access_token

<TokenResolver>
    ...
    <Tokens>
        <Token name="Authorization" macro="${token_type} ${access_token}" />
    </Tokens>
</TokenResolver>

The <Token> element supports the following attributes

Attribute Description Required

name

Name of the token

Yes

macro

The macro to use when resolving the token value

Yes

value

The fallback value to use

No