Skip to main content
Skip table of contents

Defining Workflows

Workflows provide the flexibility of the TrustBuilder platform.  A workflow is made up of a number of activities that are linked together by connections. A workflow is executed from its initial state to its final state. The route through the workflow is determined by returned values from scripts. So a return value matches a connection value/label, that determines the route and executes the next activity.

Workflow Elements

Connections → A Connection is a link between two other elements. It marks the route that the workflow can take. Each connection can be given a Label and a Value (by right-clicking on the connection). The Label is for display purposes, whereas the Value has to correspond to an element's output value, for this connection to be followed. As best practice, define the same 'value' for value and label. If no value is specified, the workflow takes the label into consideration. 

Conditions → A condition evaluates one or more input parameters, and determines the outcome of a decision as to which step to execute next.  A condition will always return a single value, which will determine the route to follow.  This value is marked on the exiting connection.  Connections marked "otherwise" are followed when no other connection can be followed.

Scripts → A piece of JavaScript code which is executed. This can execute anything. Each activity in a workflow (adapter, component, condition, sub-workflow, script) can have a script related to it to process, for instance, a response from an adapter. Say we have a JDBC adapter that does a query the response from the query is read in a script and processed possibly then set to data or a template.

Templates → Templates are used within scripts to return, for instance, an html page or a JSON message to the client. In that sense, they differ from Templates within the scope of TrustBuilder. Which are used as pages used by the gateway/orchestrator for rendering, for example: password/login pages,  IDP selection for a specific SP, etc.

Components → Components are pre-configured complete configurations. They can contain adapters, services, workflows, scripts and a configuration of their own. The scripts within a component are, however, not configurable. A Component activity is created within a workflow and is called as a step in the workflow. So a component might do a task such as validate a certificate against certificates within a certificate store, for instance. All the logic and scripting will be contained within the component, but it can be configured for URLs, ports and such. The workflow will call the component and continue with its response.

Services → Services are a way to interact with 3rd party code from within a workflow run by the TrustBuilder Core. A service is a wrapper around a java library, that is then exposed to scripts within the workflow. This way the java code can be called directly from the scripts. There are also several packaged services within TrustBuilder that expose Java methods; Eg. date and time, or encryption.

Adapters → An adapter is used by the TrustBuilder Core within a workflow to connect to any resource. This could be a call to an LDAP server or a call to a database to get details of a user for instance. Within the adapter such things as connection URL/port or datasource name or JDBC URL or database username password are configured. An Adapter Activity in a workflow is a refence within a workflow to a pre-configured adapter.

Usage in TB.Connect

Workflows can be created to serve several purposes. Below we have listed the different ways workflows can be used within TB.Connect.

  • Derived Attributes → Applies a workflow to derive a new User Attribute from other attributes.   Derived attribute workflows are executed when the user session changes. A simple example is to derive the Full Name from the First Name and the Surname.

  • Internal IDP → Applies a workflow to authenticate and/or authorize a user. For example, a company has several Active Directories (AD). For any given user, they don't know which AD to look into. The workflow will look in each AD separately, and may even store which AD the user can be found in as an attribute. The workflow can reuse this attribute to make future look-ups more efficient.

  • HTTP Request Handling → A stand-alone workflow can handle any kind of HTTP request, execute logic and make a response. These workflows are accessible by the path /idhub/tb/{workflowname}

  • Session Workflow → Go to Settings > Authentication > General > Session Workflow ID. The session hook is called by TB.Connect when a session is created or destroyed. This allows you to create custom logic whenever one of the actions above occurs. An example when this is used is to destroy an ISAM session. The workflow adds additional headers in the response.

Derived Attributes workflow

In the definition of a derived attribute, the administrator specifies the workflow that will produce the value of the attribute for the user logging in. One workflow can provide the value for multiple derived attributes. The input parameters of derived attribute workflows are "credential", "sp_request" (if it's relevant), "idp_request" (if it's relevant) and "headers".

The response of the workflow is expected to be a simple value response containing the following structure:

CODE
<subtype_name>: {
    <attr_name>: ["value1", ... ,"value3"],
    ...
    <attr_name>: ["value1", ... ,"value3"]
 }
 ...
<subtype_name>: {
    <attr_name>: ["value1", ... ,"value3"],
    ...
    <attr_name>: ["value1", ... ,"value3"]
 }

As an example, the following workflow script produces a value for the attribute Full Name by concatenating the First Name and Last Name attribute values of the principal.

CODE
function getFullNameValues(workItem){
	var fn = workItem.input.value.attributes.common.first_name[0];
	var ln = workItem.input.value.attributes.common.last_name[0];
	var full_name = fn + " " + ln;
	var full_name_str = JSON.stringify(full_name).replace(/\"/g, "");
    workItem.output = tb.simpleResponse({
		common: {
			full_name: [full_name_str]
		}
	});
}

The workflow function is given an object as parameter, called workItem in this example, that contains the principal object (accessed via workItem.input.value) and where the output of the workflow must also be stored, in workItem.output. As shown in the example above, the output object can be created using tb.simpleResponse and passing as parameter a JavaScript object with the correct structure.

Finally, since the returned values have to be in JSON format, the full_name property is converted into a JSON String by the JSON.stringify method and the backward slashes \ created by the conversion are removed.

Session Hook workflow

In general authentication settings you can configure a session hook workflow id. This workflow will be executed whenever a  session is updated. The workflow gets a SimpleValueRequest as input that contains 5 parameters. 

  •  type: This is a string that has a  value "login" if the session is updated and a value "logout" if the  session is removed. 

  • response_headers: contains the response headers. 

  • credential: contains the session.

  • request_headers: contains the request headers. 

  • request_cookies: contains the request cookies. 

The response from the session hook  workflow is expected to be an object containing one property "headers",  which is a map of headers. These headers will be added to the response headers.

Provisioning workflow

For every IDP, it is possible to configure a provisioning workflow. This workflow gets executed when a request from that IDP is handled by TB.Connect. The response of the workflow can contain attribute definitions and accompanying values, which are added to the user's session.

The input parameters of a provisioning workflow are "credential", "sp_request" (if it's relevant), "idp_request" and "headers".

The response of the workflow is expected to be a simple value response containing the following structure:

CODE
<subtype_name>: {
    <attr_name>: ["value1", ... ,"value3"],
    ...
    <attr_name>: ["value1", ... ,"value3"]
 }
 ...
<subtype_name>: {
    <attr_name>: ["value1", ... ,"value3"],
    ...
    <attr_name>: ["value1", ... ,"value3"]
 }

Token Exchange Policy workflow

Implementation

This workflow is an extension on the Token Exchange grant (OAuth) in TrustBuilder.

Because the basic process only checks the validity of the tokens and IDP's, it will always return an access token under the Token Exchange grant. However, it may be requested to add additional business logic to whether or not an access token should be granted for the specified audience and/or scopes.

Therefore it's possible to extend the Token Exchange process by providing tailor-made decision policies.

The workflow output will be a simple 'allow' or 'deny.'

Example

Below is a simple example, to demonstrate the return value.

CODE
function checkPolicy (workitem) {
   workitem.output=tb.simpleResponse({
        allow: false
	});
}

As of TrustBuilder 10.3.0, you can also specify a custom error message. This will be returned in the "error_description" field as per the OAuth spec (https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 )

CODE
function checkPolicy (workitem) {
   workitem.output=tb.simpleResponse({
        allow: false,
        error: "only administrators are allowed to do this"
	});
}

Update user attributes

This feature is available as of TrustBuilder 9.5.9. In the policy workflow you can also update user attributes, which can then be passed along in the generated token. This can especially be useful if the backend application needs a custom identifier of the user or when application specific roles have to be communicated.

CODE
function checkPolicy (workitem) {
   workitem.output=tb.simpleResponse({
        allow: true,
        common : {
			applicationRoles: ["read", "write"],
			customIdentifier: ["john.doe"]
        }
	});
}

Cookie handling in workflows

This is an example of how you can set cookies in a workflow:

CODE
var cookies = {
	"TbSession": {                 // name of the cookie
		value: 'someIdentifier',   // content of the cookie
		maxage: '300',             // expiration time in seconds
		path: "/",                 // define a (sub) path to optionally limit the scope of the cookie
		httponly: true,            // do not expose the cookie to javascript; defaults to false
		secure: true               // only send the cookie via https; defaults to false
	},
	"RememberMe": {
		value: '1',
		maxage: '300',
		path: "/"
	},
};
var header{
	"Content-type": "text/html",
	'X-Application': 'Trustbuilder'
};
tb.generateResponse('my doc',headers,cookies,200);

Encryption Services

Argon 2

be.securit.trustbuilder.service.EncryptionService

argon2Hash()

generates an Argon2 hash using the ARGON2id type.

  • parameters:

    • password : String : the password to hash

    • base64EncodedSalt : String : the salt to use, base 64 encoded to a String

    • parallelism : int : the number of threads to use

    • resultLength : int : the desired tagLength, this is the desired number of bytes in the hash. Note that is not equal to the length of this function's result

    • memorySizeInKb : int : amount of memory (in kilobytes) to use

    • iterations : int : number of iterations to perform

  • return value:

An encoded hash. This encoded hash contains all parameters used to calculate the hash, so it alone suffices to verify a password.

argon2Verify()

Verifies a given password against a argon2 encoded hash string

  • parameters:

    • password : String : the password to verify

    • encodedHash : String : the encoded Argon hash string

  • return value:

    • boolean : true if the password was successfully verified, false otherwise

Example

CODE
function argon2Hash (workitem){
	var service = tb.getService('Argon2');
	var pwd = 'passw';
	var b64 = tb.base64Encode('some random string', true);
	var parallelism = 4;
	var resultLength = 64;
	var memorySizeInKb = 4096;
	var iterations = 10;
	var hash = service.argon2Hash(pwd, b64,parallelism,resultLength,memorySizeInKb,iterations);
	
	tb.log(hash);
}

function argon2Verify (workitem){
	var service = tb.getService('Argon2');
	var pwd = 'passw';
	var hash = '';
	var result = service.argon2Verify(pwd, hash);
	tb.log(result?'success':'fail');
}

PBKDF2

2 methods were added for the PBKDF2 password hashing - pbkdf2Hash(password, base64EncodedSalt, iterations, keyLength, algorithm) password :

  • the password string to hash.

  • base64EncodedSalt : a regular base64 encoded String representing the chosen salt (The standard recommends a salt length of at least 64 bits. The US National Institute of Standards and Technology recommends a salt length of 128 bits.)

  • iterations : number of iterations for the PBKDF2 algorithm

  • keyLength : desired length of the resulting key in bits

  • algorithm : one of the available PBKDF2 algorithm variations, mainly specifying the hash algorithm to use.

At the time of writing the available ones are :

  • PBKDF2WithHmacSHA1

  • PBKDF2WithHmacSHA224

  • PBKDF2WithHmacSHA256

  • PBKDF2WithHmacSHA384

  • PBKDF2WithHmacSHA512

The function returns a regular base64 encoded String representing the resulting hash bits. - pbkdf2Verify(password, hash, base64EncodedSalt, iterations, keyLength, algorithm) password : the password string to verify.

  • hash : a regular base64 encoded String representing the hash to verify against

  • base64EncodedSalt : a regular base64 encoded String representing the chosen salt (The standard recommends a salt length of at least 64 bits. The US National Institute of Standards and Technology recommends a salt length of 128 bits.)

  • iterations : number of iterations for the PBKDF2 algorithm

  • keyLength : desired length of the resulting key in bits

  • algorithm : one of the available PBKDF2 algorithm variations, mainly specifying the hash algorithm to use. The function returns true if the password is verified against the hash, and false otherwise

LTPA2 Token

Method: newLpta2Encoder

This API Service can be used to create LTPA2 tokens.

Parameters

  • privateKey : PrivateKey object : the private key to use, to sign the tokens.

  • secretKeyString : String : Base64 encoded shared symmetric key (AES) to use to encrypt the token.

Return Value

An LptaTokenEncoder object. Object that can be used to encode/decode and verify the signature of LTPA tokens, using the configured keys.

Method: decodeLTPAToken

decodeLTPAToken : method which decodes an encoded ltpa token string.

parameters

  • token : String : the encoded ltpa token String

return value : 

  • LtpaToken object

methods:

  • getExpire : long : expiry time in seconds since epoch

  • getHost : String

  • getLtpaVersion : LtpaVersion enum object

  • getNamingProvider : String

  • getPlainUserMetadata : String : the plain unencoded token String

  • getPort : int

  • getServerName : String

  • getType : String

  • getUser : String

  • getAuthenticationMethod : String

  • toString : String : the encoded token string

Method: encodeLTPAToken

parameters:

  • ltpaToken : LtpaToken object : to create an unsigned token, see ltpa2TokenBuilder() method

return value : 

  • String : encoded ltpa token string

Method: ltpa2TokenBuilder

return value : Token Builder object

usage example :

CODE
var token = builder.setUser("user\\:defaultWIMFileBasedRealm/uid=wpsadmin,o=defaultWIMFileBasedRealm")
                   .setAuthenticationMethod("authMethod")
                   .setExpire(1535545274)
                   .setHost("host")
                   .setNamingProvider("namingProvider")
                   .setPort(8484)
                   .setServerName("serverName")
                   .setType("type")
                   .build();

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.