Loginapp Design Kit page settings configuration

Page settings allow configuring the behavior of certain UI pages. Some pages do not have any settings while others like the configurable UI require significant settings configurations.

Basic configuration and lookup strategy

UI page settings are configured using config.pages section in custom.sdk.config.js.

  • The config.pages section consists of an array of page settings:
  • Each entry defines the path of the page.
  • The first entry whose path matches the current browser URL is used to for rendering the page.
  • The path may contain a placeholder FLOW to resolve the flow ID at runtime or the flow ID may be hard coded.
config.pages = [ 
    // Password page 
    { 
        path: '/auth/flow/{{FLOW}}/password', 
        ... 
    }, 
    // Password change page 
    { 
        path: '/auth/flow/fixed-flow-id/password/change', 
        ... 
    }, 
    ... 
];

The above example shows two entries in config.pages. One uses the FLOW placeholder, the other hardcoding the flow ID.

Page settings currently do not support the use of stepId and all steps will use the same configuration.

Full page settings configuration

The following code block shows the full configuration of the config.pages section supported by the Loginapp Design Kit:

Show moreShow less
pages: [
    {
        path: '/auth/flow/{{FLOW}}/airlock-2fa/challenge',
        pageSettings: {
            showAppApprovalLink: true,
            // supported factors are 'ONLINE_QR_CODE' and 'ONE_TOUCH'
            factor: 'ONLINE_QR_CODE'
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/airlock-2fa/challenge-only',
        pageSettings: {
            showAppApprovalLink: true,
            // supported factors are 'ONLINE_QR_CODE' and 'ONE_TOUCH'
            factor: 'ONLINE_QR_CODE'
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/cronto/challenge',
        additionalAttributes: {
            'challenge': {
                'onlineValidation': true,
                'pushed': true,
                'pushDevice': {
                    'label': 'My iPhone',
                    // the platform property is only used if label is set to null
                    'platform': 'IOS'
                }
            }
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/password',
        pageSettings: {
            publicSelfServiceLink: '{{SPA}}/self-service/flow/default',
            userSelfRegistrationLink: '{{SPA}}/registration/flow/default'
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/password/change',
        pageSettings: {
            oldPasswordRequired: false
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/mtan',
        additionalAttributes: {
            resendPossible: true,
            phoneNumber: '+41791234567'
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/mtan/registration/verification/otp',
        additionalAttributes: {
            resendPossible: true,
            phoneNumber: '+41791234567'
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/airlock-2fa/additional-activation',
        pageSettings: {
            showAppDeviceActivationLink: true
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/airlock-2fa/migration',
        pageSettings: {
            showAppDeviceActivationLink: true
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/message',
        additionalAttributes: {
            messageId: 'default'
        }
    },
    {
        path: 'auth/flow/{{FLOW}}/matrix',
        additionalAttributes: {
            matrixChallenges: ['A13']
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/oauth2/consent',
        additionalAttributes: {
            grantableScopes: [
                {
                    scope: 'email',
                    granted: true,
                    updatedAt: '2024-10-23'
                },
                {
                    scope: 'phone',
                    granted: false,
                    updatedAt: '2023-08-15'
                },
                {
                    scope: 'address',
                    granted: false
                }
            ]
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/email/otp',
        additionalAttributes: {
            resendPossible: true,
            emailAddress: 'john.doe@example.com'
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/email/verification/otp/check',
        additionalAttributes: {
            emailAddress: 'john.doe@example.com'
        }
    },
    {
        path: '/auth/flow/{{FLOW}}/username',
        additionalAttributes: {
            captcha: {
                // supported types are 'RE_CAPTCHA' and 'H_CAPTCHA' - the property 'key' can also be set to use your own site key
                type: 'RE_CAPTCHA'
            }
        }
    },
    {
        path: '/self-service/flow/{{FLOW}}/approval/airlock-2fa/challenge',
        pageSettings: {
            showAppApprovalLink: true,
            // supported factors are 'ONLINE_QR_CODE' and 'ONE_TOUCH'
            factor: 'ONLINE_QR_CODE'
        }
    },
    {
        path: '/self-service/flow/{{FLOW}}/approval/airlock-2fa/challenge-only',
        pageSettings: {
            showAppApprovalLink: true,
            // supported factors are 'ONLINE_QR_CODE' and 'ONE_TOUCH'
            factor: 'ONLINE_QR_CODE'
        }
    },
    {
        path: '/self-service/flow/{{FLOW}}/approval/cronto/challenge',
        additionalAttributes: {
            'challenge': {
                'onlineValidation': true,
                'pushed': true,
                'pushDevice': {
                    'label': 'My iPhone',
                    // the platform property is only used if label is set to null
                    'platform': 'IOS'
                }
            }
        }
    },
    {
        path: '/self-service/flow/{{FLOW}}/approval/matrix',
        additionalAttributes: {
            matrixChallenges: ['A13']
        }
    },
    {
        path: '/self-service/flow/{{FLOW}}/approval/mtan',
        additionalAttributes: {
            phoneNumber: '+41791234567'
        }
    },
    {
        path: '/self-service/flow/{{FLOW}}/message',
        additionalAttributes: {
            messageId: 'default'
        }
    },
    {
        path: '/self-service/flow/{{FLOW}}/username',
        additionalAttributes: {
            captcha: {
                type: 'RE_CAPTCHA'
            }
        }
    },
    {
        path: '/registration/flow/{{FLOW}}/data/example',
        additionalAttributes: {
            captcha: {
                type: 'H_CAPTCHA'
            }
        }
    },
    {
        path: '/registration/flow/{{FLOW}}/message',
        additionalAttributes: {
            messageId: 'default'
        }
    },
    {
        path: '/registration/flow/{{FLOW}}/airlock-2fa/device/add',
        pageSettings: {
            showAppDeviceActivationLink: true
        }
    },
    {
        path: '/registration/flow/{{FLOW}}/verification/email',
        additionalAttributes: {
            captcha: null,
            emailAddress: 'john.doe@example.com'
        }
    },
    {
        path: '/registration/flow/{{FLOW}}/verification/phone-number',
        additionalAttributes: {
            captcha: null,
            resendPossible: true,
            phoneNumber: '+41791234567'
        }
    },
    {
        path: '/protected/flow/{{FLOW}}/airlock-2fa/device/add',
        pageSettings: {
            showAppDeviceActivationLink: true
        }
    },
    {
        path: '/protected/flow/{{FLOW}}/approval/airlock-2fa/challenge',
        pageSettings: {
            showAppApprovalLink: true,
            // supported factors are 'ONLINE_QR_CODE' and 'ONE_TOUCH'
            factor: 'ONLINE_QR_CODE'
        }
    },
    {
        path: '/protected/flow/{{FLOW}}/approval/airlock-2fa/challenge-only',
        pageSettings: {
            showAppApprovalLink: true,
            // supported factors are 'ONLINE_QR_CODE' and 'ONE_TOUCH'
            factor: 'ONLINE_QR_CODE'
        }
    },
    {
        path: '/protected/flow/{{FLOW}}/approval/cronto/challenge',
        additionalAttributes: {
            'challenge': {
                'onlineValidation': true,
                'pushed': false,
                'pushDevice': {
                    'label': 'My iPhone',
                    // the platform property is only used if label is set to null
                    'platform': 'IOS'
                }
            }
        }
    },
    {
        path: '/protected/flow/{{FLOW}}/approval/matrix',
        additionalAttributes: {
            matrixChallenges: ['A13']
        }
    },
    {
        path: '/protected/flow/{{FLOW}}/approval/mtan',
        additionalAttributes: {
            phoneNumber: '+41791234567'
        }
    },
    {
        path: '/protected/flow/{{FLOW}}/email/verification/otp/check',
        additionalAttributes: {
            emailAddress: 'john.doe@example.com'
        }
    },
    {
        path: '/protected/flow/{{FLOW}}/message',
        additionalAttributes: {
            messageId: 'default'
        }
    },
    {
        path: '/protected/flow/{{FLOW}}/mtan/registration/verification/otp',
        additionalAttributes: {
            resendPossible: true,
            phoneNumber: '+41791234567'
        }
    },
    {
        path: '/protected/flow/{{FLOW}}/vasco/device/activation',
        additionalAttributes: {
            activatableDevices: [
                {
                    id: '0',
                    serialNumber: '000-1111-33'
                },
                {
                    id: '1',
                    serialNumber: '111-2222-44'
                }
            ]
        }
    },
    {
        path: '/protected/portal',
        pageSettings: {
            portalGroups: [
                {
                    identifier: 'self-services',
                    portalTargets: [
                        {identifier: 'cronto-device-management'},
                        {identifier: 'address-change'},
                        {identifier: 'password-change'}
                    ]
                },
                {
                    identifier: 'custom-group',
                    portalTargets: [
                        {identifier: 'custom-target'}
                    ]
                }
            ]
        }
    },
    {
        path: '/error/message',
        pageSettings: {
            titleKey: 'general.error.title',
            message: {key: 'general.error.description', arguments: null}
        }
    }
],

To clear a value, use null. The following example removes the password reset link and the user self-registration link on the password page:

config.pages = [ 
    { 
        path: '/auth/flow/{{FLOW}}/password', 
        pageSettings: { 
            passwordResetLink: null, 
            userSelfRegistrationLink: null 
        } 
    } 
];

Field-based validation messages

Validation messages are configured separately for each page. The following example demonstrates how a validations section is added to the password page:

Show moreShow less
config.pages = [ 
    // Password page 
    { 
        path: '/auth/flow/{{FLOW}}/password', 
        validations: [ 
            { 
                htmlId: 'username', 
                messages: [ 
                    {key: 'error.validation-failed.required'}, 
                    {key: 'error.validation-failed.min-length', arguments: {requiredLength: 6}} 
                ] 
            }, 
            { 
                htmlId: 'password', 
                messages: [ 
                    {key: 'error.validation-failed.required'}, 
                ] 
            } 
        ] 
    }, 
];

In the above example, the UI fields for username and password are configured to display validation messages. Parameters to the validation message translation can be passed using arguments.

Custom configuration-based UI

Custom UIs are a special type of UIs that are characterized by their dynamic nature. While the content on most product pages is fixed, the content on custom UIs is either determined by a server-side configuration or by JavaScript.

The configuration of Airlock IAM provides a configuration option based on the Custom Configuration-Based UI plugin that enables administrators to configure the fields to display in a UI. Since the Loginapp Design Kit operates without an actual IAM instance, it provides means to define the content of the configuration-based UI. Below is a configuration showing the usage of each available type of UI control:

Show moreShow less
config.pages = [ 
    { 
        path: '/auth/flow/{{FLOW}}/ext/example', 
        uiElements: [ 
            { 
                type: 'form', 
                uiElements: [ 
                     { 
                        type: 'text', 
                        htmlId: 'itemText', 
                        text: 'some text w/o <i>HTML code</i>' 
                     },
                       { 
                        type: 'input', 
                        htmlId: 'itemUsername', 
                        label: 'authentication.data.item-label.username', 
                        property: 'username', 
                    }, 
                    { 
                        type: 'checkbox', 
                        htmlId: 'itemOfficePhone', 
                        label: 'authentication.data.item-label.office-phone', 
                        labelLeft: true, 
                        property: 'officePhone', 
                    }, 
                    { 
                        type: 'captcha' 
                    },  
                    { 
                        type: 'drop-down', 
                        htmlId: 'itemSelection', 
                        label: 'registration.selection.instructions', 
                        property: 'selection', 
                        options: [ 
                            { 
                                id: 'selectionOption1', 
                                label: 'registration.selection.options.airlock-2fa', 
                                value: 'value1', 
                                disabled: false 
                            }, 
                            { 
                                id: 'selectionOption2', 
                                label: 'registration.selection.options.cronto', 
                                value: 'value2', 
                                disabled: false 
                            } 
                        ] 
                    }, 
                    { 
                        type: 'radio', 
                        htmlId: 'itemRadio', 
                        label: 'registration.data.item-label.correspondence-language', 
                        property: 'radio', 
                        inline: true, 
                        options: [ 
                            {id: 'radioOption1', label: 'general.language.de', value: 'value1', disabled: false}, 
                            {id: 'radioOption2', label: 'general.language.en', value: 'value2', disabled: false} 
                        ] 
                    }, 
                    { 
                        type: 'button-group', 
                        buttons: [ 
                            { 
                                type: 'button', 
                                label: 'authentication.data.actions.continue', 
                                alignment: 'RIGHT', 
                                htmlId: 'continueButton' 
                            }, 
                            { 
                                type: 'cancel-button', 
                                alignment: 'RIGHT' 
                            } 
                        ] 
                    } 
                ] 
            } 
        ] 
    } 
];

Note that the custom configuration-based UI must be added to customUi of the navigation menu.

The Loginapp UI uses /ext path for pages that were explicitly configured using the Custom Configuration-Based UI plugin. However, IAM is also able to automatically infer the UI configuration for certain steps which results in pages being available using data instead of /ext in their path. For example: /auth/flow/{{FLOW}}/data/example . The underlying mechanisms and the configuration in the Design Kit however are the same.

Adding CAPTCHAs to custom pages

Two external CAPTCHA services are supported by Airlock IAM, reCAPTCHA and hCAPTCHA.

  1. To add a CAPTCHA element on a custom page:
  2. Add the type: 'captcha' to the list of uiElements (see example code in section Custom configuration-based UI).
  3. Define the path and CAPTCHA type in the page settings (see example code in section Full page settings configuration).

Overwriting automatic type formatting

For type: 'input' fields, validators are used to set the type of input (e.g. type: 'text'). To overwrite the validator, add a corresponding subtype to the type: 'input' section:

config.pages = [  
    {  
        path: '/auth/flow/{{FLOW}}/ext/example',  
        uiElements: [  
            {  
                type: 'form',  
                uiElements: [  
                    {  
                        type: 'input',  
                        subtype: 'email', 
                        htmlId: 'itemUsername',  
                        label: 'authentication.data.item-label.username',  
                        property: 'username',  
                    },  
                    ... 
  • Frequently used supported subtypes are:
  • 'text'
  • 'email'
  • 'password'
  • 'number'
  • Note that other input types may also be used with limited support. For example, there is no out-of-the-box support for formatting when using the subtype 'date'.
    A list of all possible values applicable as subtypes can be found here: (w3schools) HTML input types.

Custom JavaScript-Based UI

Even more flexible are pages that are entirely written in JavaScript and configured using the Custom JavaScript-Based UI plugin in an IAM instance. A custom JavaScript-based UI is nothing else than a custom configuration-based UI using an empty array for uiElements:

config.pages = [ 
    { 
        path: '/auth/flow/{{FLOW}}/ext/example', 
        uiElements: [] 
    } 
];

As mentioned, the page must be added to a customUi section of the navigation menu.