Scripting

This section introduces scripts as a general concept in the Curity Identity Server. For details on how to write a script see the developer guide’s scripting section.

Introduction to scripts

The Curity Identity Server is highly customizable on the server side. Many features can simply be configured, but many times it’s desirable to do more advanced operations. This ranges from doing custom validation of input parameters when a user authenticates or creates an account, to issuing tokens with a different structure than what’s provided by default. Even more advanced scenarios include issuing multiple tokens, or tokens from endpoints that normally don’t provide a token such as the introspection endpoint.

For this purpose the Curity Identity Server provide a configurable subsystem of procedures or scripts. Scripts are compiled at configuration time and executed efficiently on the server alongside other components as an integral part of the request pipeline, depending on the type of requests, more than one type of script may be executed.

The following types of scripts are available

  • Token Procedures - Used when issuing tokens
  • Validation Procedures - Used when validating incoming form data
  • Transformation Procedures - Used when transforming data such as usernames
  • Filter Procedures - Used when filtering data such as which authenticator to select
  • EventListener Procedures - Used to react to Server Events
  • Global Scripts - - Provides global functions that become available in all procedures

For details about each type, please consult the Scripting Guide.

Tip

Scripts are written in JavaScript and it’s recommended to convert the entire script into a base64 encoded string before setting it in the configuration, to avoid having to xml encode individual characters.

Procedures during authentication

Authentication can be done in many ways, but there are typically two main requests that are interesting to look at from a script perspective.

  1. Displaying a list of authentication methods
  2. Posting the credentials to the authentication or registration endpoint from a webform

For how to filter the listing of authentication methods, see authentication filters.

When posting credentials to the authentication service, the following pipeline is executed:

../../_images/script-processing-example.png

Fig. 34 Processing of authentication request

There are many things going on during authentication and most steps are configurable in different ways. As the illustration shows, one of the first things that takes place is input validation. There are always built in validations that will execute, but sometimes more validation is desired. A good example of that could be to validate the format if a phone number, or a social security number etc. To do this a procedure can be added on that endpoint to validate the input data.

Listing 73 Validation Procedure validating a phone number
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function result(object) {
        var errors = {};
        var phoneRegex = /^\+\d{2}\d+$/;
        var matches = phoneRegex.exec(object.phoneNumber)

        if (!matches) {
                errors.phonenumber = "error.validation.invalid.phonenumber";
        }

        return errors;
}

More on validation procedures and how to write those can be found in the validation procedure section in the developer guide.

After the input validation has taken place, the pipeline continues and before the SSO session is created a step of name and attribute transformation is executed. Here one or many transformers will add, update and remove data on the authenticated object. This is the second place where the admin can choose to execute a script.

Listing 74 Example of Transformation Procedure
1
2
3
4
function result(attributes) {
        attributes.subject = attributes.subject + "@phonenumber";
        return attributes;
}

The above examples illustrate how authentication using phone number as username can be validated with custom validation before processed, and before the session is created, the username was transformed into a string containing @phonenumber as a marker appended to the username.

Procedures during token issuance and processing

In the same way as procedures are used during the authentication processing, procedures are used in token issuance and processing in the Security Token Server.

During authentication procedures are optional and used for additional functionality, but during token issuance they become a key component.

../../_images/script-token-gen-example.png

Fig. 35 Token generation example

In the common cases the factory default scripts provided with the Curity installation will be enough. But when updates are needed, these can be used as a base for updates and it’s recommended to keep them as is and add other scripts alongside with the default ones. The factory default scripts are found at $IDSVR_HOME/etc/init in the named subfolders.

Tip

Factory default procedures are located in $IDSVR_HOME/etc/init under named folders, see the configuration section for details on how to use this.

Configuring Scripts

Scripts are configured in the processing section of the configuration schema. There all procedures are defined and then later referenced by the subsystem configuration that needs it.

Script Types

The following types and subtypes of scripts are possible to configure:

filter-procedure

Filter procedures perform filtering operation on data. The currently only supported type of filters is authenticator filters. See here for configuration reference.

authenticator

Filter procedure for authenticator selection.

These are used to filter which authenticators are displayed when a user starts authentication. For more about authentication filtering see the authenticator filtering guide.

See the filter procedure configuration reference for more details on the parameters available.

global-script

Global JavaScript functions and libraries can be placed here. All global scripts are made available to any other script before compilation. Commonly these are used to created reusable functions, or to include libraries such as underscore.js etc. Global scripts are not sub-typed.

token-procedure

Token Procedures are the most prominent scripts. They are invoked on calls to the various token processing and generating endpoints. Each endpoint-kind will provide its own context to the procedure, and will expect a result object of a certain type. To distinguish between these types, each token procedure is typed with an :endpoint-types marker which is the same marker that is used when configuring endpoints. For details on parameters when configuring token-procedures see the configuration reference.

oauth-assisted-token

The oauth-assisted-token type marks the script compatible with the OAuth assisted token endpoint.

oauth-authorize

The oauth-authorize type marks the script compatible with the OAuth authorize endpoint. This endpoint is responsible for the OAuth Implicit flow and the OAuth Code flow, as well as the OpenID Connect companion flows.

oauth-introspect

The oauth-introspect type marks the script compatible with the ref:OAuth introspection endpoint<oauth_endpoints_introspect>. This script is different from the other in the sense that most of the times it does not issue tokens, but merely produce the result of the introspection as json content. However, a common pattern is to issue internal tokens from the introspect endpoint, which is possibly by updating these procedures.

oauth-token

The oauth-token type marks the script compatible with the OAuth token endpoint. This endpoint is responsible for the OAuth client credentials flow, Resource owner password credentials flow, refresh token flow and the token call of the code flow, as well as the OpenID Connect companion flows.

transformation-procedure

Transformation Procedures are used to transform attributes. The commonly used place is within an attribute transformer configured on an authenticator. Transformation procedures are not sub-typed.

validation-procedure

Validation procedures are used in the Curity Authentication Server for input validation. When custom requirements are needed for input validation, such as certain password rules during creation of passwords, or formats of emails or phone numbers, these procedures can be used to implement these more advanced or custom scenarios.

request

The only type of validation procedure available is the request type.

event-listener-procedure

EventListener procedures are used to handle Server Events.

Preparations

It is recommended to base64 (RFC 4648) encode the script before setting it in the configuration. However as a shortcut to avoid this step, especially in development environments scripts can be provided as JavaScript source code files placed in the $IDSVR_HOME/etc/init folder in the appropriate sub-directory.

Configuring using etc/init

There are 5 sub-folders under $IDSVR_HOME/etc/init that are used for the source files. Each folder may contain subfolders for the sub-type of the script. The structure looks as follows with the default files in place:

Listing 75 File structure of $IDSVR_HOME/etc/init scripting
├── filter-procedures
│   └── authenticator
│       └── authenticator-filter.js
├── global-scripts
│   ├── claimutils.js
│   └── delegation.js
├── token-procedures
│   ├── oauth-assisted-token
│   │   └── assisted-token-procedure.js
│   ├── oauth-authorize
│   │   └── authorize-procedure.js
│   ├── oauth-introspect
│   │   └── introspect-procedure.js
│   └── oauth-token
│       └── token-procedure.js
├── transformation-procedures
│   └── simple-transformer.js
└── validation-procedures
    └── request
        ├── account-registration-validator.js
        ├── forgot-password-validator.js
        ├── phonenumber-validator.js
        └── set-password-validator.js

Note

Each file will be parsed and converted into a base64 encoded (RFC 4648) configuration setting with the name of the file as the id and the folder and sub-folder as type and subtype.

Create a new script

  1. Add a new .js file in the appropriate location
  2. Update the running configuration of the server
Listing 76 Reload the current configuration
$ ${IDSVR_HOME}/bin/idsvr --reload

Using --reload causes the server to merge the existing configuration with the files in $IDSVR_HOME/etc/init. If a complete replace is needed instead, then --force-reload can be used.

Example adding script using etc/init

This example sets a new validation procedure that can be used during registration of new accounts to validate that the License agreement has been accepted before allowing the registration to continue.

  1. Create a new file in $IDSVR_HOME/etc/init/validation-procedures/request named my-validator.js

  2. Edit the file in any editor and add the following content

    Listing 77 Example of validation procedure
    function result(object) {
            var errors = {};
    
            var acceptTerms = object.request.getFormParameter("agreeToTerms");
    
            if (acceptTerms !== 'on') {
                    errors.acceptTerms = "error.validation.terms";
            }
    
            return errors;
    }
    
  3. Update the running configuration

Listing 78 Reload the configuration to update the scripts
$ ${IDSVR_HOME}/bin/idsvr --reload

Now a new configuration entry has been added to the running configuration with the following format:

Listing 79 Validation procedure configuration entry
<validation-procedure>
        <id>my-validator</id>
        <script>ZnVuY3Rpb24gcmVzdWx0KG9iamVjdCkgewoJCQl2YXIgZXJyb3JzID0ge307CgoJCQl2
        YXIgYWNjZXB0VGVybXMgPSBvYmplY3QucmVxdWVzdC5nZXRGb3JtUGFyYW1ldGVyKCJhZ3JlZVRv
        VGVybXMiKTsKCiAgICBpZiAoYWNjZXB0VGVybXMgIT09ICdvbicpIHsKCWVycm9ycy5hY2NlcHRU
        ZXJtcyA9ICJlcnJvci52YWxpZGF0aW9uLnRlcm1zIjsKICAgIH0KICAgIAogICAgcmV0dXJuIGVy
        cm9yczsKfQo=</script>
        <type>request</type>
</validation-procedure>

The procedure has been encoded into base64 and a new entry with the id of the filename has been created. This entry can now be referenced from other parts of the system where validation procedures are used.

Configuring using REST

In addition to adding configuration directly to the $IDSVR_HOME/etc/init/ directory other interfaces can be used as well. Most commonly the REST interface or the CLI (Command Line Interface) can be used.

Important

When adding configuration using REST the running configuration is updated directly. I.e. no file is placed in $IDSVR_HOME/etc/init/. It is recommended to backup the configuration when significant changes have been made for system recovery safe keeping. See Backing up the configuration for details.

To add the script created in the previous example follow these steps.

  1. Base64 encode the content of the script (recommended)

    Listing 80 Base64 encode a script
    $ base64 my-validator.js -o my-validator.js.b64
    
  2. Create a json message (xml is also supported) containing the encoded script in a new file called my-validator.json

    Listing 81 Validation procedure json
    {
            "validation-procedure": {
                    "id": "my-validator",
                    "script": "ZnVuY3Rpb24gcmVzdWx0KG9iamVjdCkgewoJCQl2YXIgZXJyb3JzID0ge307CgoJCQ
                    l2YXIgYWNjZXB0VGVybXMgPSBvYmplY3QucmVxdWVzdC5nZXRGb3JtUGFyYW1ldGVyKCJhZ3JlZVR
                    vVGVybXMiKTsKCiAgICBpZiAoYWNjZXB0VGVybXMgIT09ICdvbicpIHsKCWVycm9ycy5hY2NlcHRU
                    ZXJtcyA9ICJlcnJvci52YWxpZGF0aW9uLnRlcm1zIjsKICAgIH0KICAgIAogICAgcmV0dXJuIGVyc
                    m9yczsKfQo=",
                    "type": "request"
            }
    }
    
  3. Post the new procedure to the server to update the running configuration

    Listing 82 Posting new validation procedure as json using REST
    $ curl -X POST -H "Content-Type: application/vnd.yang.data+json" \
            -H "Authorization: Basic YWRtaW46UGFzc3dvcmQx" -d @my-validator.json \
            "https://localhost:6749/admin/api/rest/running/processing/procedures"
    
  4. To verify the new entry, do a GET on the specific validator just created

    Listing 83 Read validation-procedure using REST
    $ curl -k -H "Accept: application/vnd.yang.data+json" \
            -H "Authorization: Basic YWRtaW46UGFzc3dvcmQx" \
             "https://localhost:6749/admin/api/rest/running/processing/procedures/validation-procedure/my-validator?deep&with-defaults=true"
    

    This should produce the following response

    Listing 84 Response from REST interface when reading validation procedure
    {
            "base:validation-procedure": {
                    "id": "my-validator",
                    "script": "ZnVuY3Rpb24gcmVzdWx0KG9iamVjdCkgewoJCQl2YXIgZXJyb3JzID0ge307CgoJCQl2YXIgY
                    WNjZXB0VGVybXMgPSBvYmplY3QucmVxdWVzdC5nZXRGb3JtUGFyYW1ldGVyKCJhZ3JlZVRvVGVybXMiKTsKC
                    iAgICBpZiAoYWNjZXB0VGVybXMgIT09ICdvbicpIHsKCWVycm9ycy5hY2NlcHRUZXJtcyA9ICJlcnJvci52Y
                    WxpZGF0aW9uLnRlcm1zIjsKICAgIH0KICAgIAogICAgcmV0dXJuIGVycm9yczsKfQo=",
                    "type": "request"
            }
    }
    

Writing Scripts

The developer guide contains examples and instructions on how to implement these scripts.