Customizing an existing product page using the Loginapp REST UI SDK

While in most cases, existing product pages of the Loginapp REST UI such as the password or mTAN authentication pages can be customized by modifying SASS/CSS only, it might not be sufficient for all use-cases.

  • Examples, where SASS/CSS is not sufficient, are among others:
  • Adding new UI elements to a page, e.g. a subtitle.
  • Changing the order UI elements.
  • Adding dynamic behavior, e.g. a password meter.

To that end, the Loginapp REST UI SDK provides a mechanism that allows to further customize existing product pages.

To avoid problems with caching, it is recommended to use SASS whenever possible.

Modify an existing page with separate HTML files (preferred method)

For optimal performance, the SDK inlines the HTML file's content at build time.

Path to the folder where custom HTML files have to be placed:
$CUSTOM_DIRECTORY/src/assets/custom/templates

  • Names of the custom HTML files:
  • before-container.html
  • before-content.html
  • after-content.html
  • after-container.html
Show moreShow less
customBlocks() { 
            return [ 
                { 
                    html: this.fetchHtml('assets/custom/templates/before-container.html'), 
                    position: this.customBlockPosition.BEFORE_CONTAINER, 
                }, 
                { 
                    html: this.fetchHtml('assets/custom/templates/before-content.html'), 
                    position: this.customBlockPosition.BEFORE_CONTENT, 
                }, 
                { 
                    html: this.fetchHtml('assets/custom/templates/after-content.html'), 
                    position: this.customBlockPosition.AFTER_CONTENT, 
                }, 
                { 
                    html: this.fetchHtml('assets/custom/templates/after-container.html'), 
                    position: this.customBlockPosition.AFTER_CONTAINER, 
                } 
            ]; 
        }

Basic structure for JavaScript with custom HTML layout

Customizations using JavaScript take place here:
$CUSTOM_DIRECTORY/src/assets/custom/js/iam-custom.js

The basic structure when customizing a page is as follows:

iam.api.customizing.initialize = function () { 
  
    // Define a new page 
    class MyCustomProductPage extends iam.api.types.CustomProductPage { 
        // ... 
    } 
  
    // Register the page: 
    iam.api.customizing.registerPage(new MyCustomProductPage()); 
};

The page has to be defined by extending from:
iam.api.types.CustomProductPage

The page has to be registered subsequently, using:
iam.api.customizing.registerPage

When the Loginapp REST UI loads a new page, Airlock IAM checks each custom page whether it is applicable or not.

For the remainder of this section, the surrounding function iam.api.customizing.initialize is not shown in the code examples. However, remember that all JavaScript customizations must happen within this function as shown above.

Selecting applicable pages with JavaScript

In order to decide which customizations of pages are applicable, the Loginapp REST UI SDK provides an API function isApplicable as illustrated below:

class MyCustomProductPage extends iam.api.types.CustomProductPage { 
  
    isApplicable({pageId, flowId, stepId}) { 
        return pageId === 'iamAuthPassword'; // example for customizing the product's password page 
    } 
}

With this method, it is possible to programmatically define whether this customization of a page should be applied or not. The Loginapp REST UI invokes this method whenever a new page is displayed.

  • The arguments provided are:
  • pageId – a unique ID for each page of the Loginapp REST UI.
  • flowId – the flow currently in progress (may be undefined for non-flow pages).
  • stepId – the flow's step currently in progress (may be undefined for non-flow pages and flow pages without an explicit step ID).

Note that the UI SDK allows multiple page customizations to be applicable. Each page customization determined to be applicable will be applied to the page being displayed.

Inline HTML in JavaScript

In order to modify an existing page, specific life cycle methods are provided as below:

Show moreShow less
class MyCustomProductPage extends iam.api.types.CustomProductPage { 
  
    // Invoked whenever the page is active and is being entered/navigated to 
    onPageEnter() { 
        // ... 
    } 
  
    // Invoked whenever the page is active and is being left 
    onPageExit() { 
        // ... 
    } 
  
    // Invoked upon language change 
    onLanguageChange(language) { 
        // ... 
    } 
  
    // Convenience method to provide custom HTML 'blocks' at four defined positions 
    // on the page. Each of those positions is optional. There are four different 
    // positions where custom blocks can be displayed, in vertical order: 
    // - BEFORE_CONTAINER 
    // - BEFORE_CONTENT 
    // - AFTER_CONTENT 
    // - AFTER_CONTAINER 
    customBlocks() { 
        return [ 
            { 
                html: '<div>before container</div>', 
                position: this.customBlockPosition.BEFORE_CONTAINER, 
            }, 
            { 
                html: '<div>before content</div>', 
                position: this.customBlockPosition.BEFORE_CONTENT, 
            }, 
            { 
                html: '<div>after content</div>', 
                position: this.customBlockPosition.AFTER_CONTENT, 
            }, 
            { 
                html: '<div>after container</div>', 
                position: this.customBlockPosition.AFTER_CONTAINER, 
            } 
        ]; 
    } 
  
    // Additional HTML rendering that cannot be done using custom blocks. 
    // Note that this function can be used in combination with custom blocks. 
    // Caution: this method can be invoked more than once on a page: 
    // when entering the page, but also every time when switching the language. 
    // For correct behavior, it must therefore be ensured that previous renderings are undone. 
    render () { 
        // ... 
    } 
}

Full product page example for JavaScript with inline HTML

The following example shows the customization of the password page. It defines all custom blocks and additionally implements the render method to define a subtitle:

Show moreShow less
iam.api.customizing.initialize = function () { 
  
    class MyCustomPasswordPage extends iam.api.types.CustomProductPage { 
  
        isApplicable({pageId, flowId, stepId}) { 
            return pageId === 'iamAuthPassword'; // Only apply to the password page 
        } 
  
        onPageEnter() { 
            console.log('entering: iamAuthPassword'); // for the sake of illustration 
        } 
  
        onPageExit() { 
            console.log('exiting: iamAuthPassword'); // for the sake of illustration 
        } 
  
        customBlocks() { 
            return [ 
                { 
                    html: '<div>before container</div>', 
                    position: this.customBlockPosition.BEFORE_CONTAINER, 
                }, 
                { 
                    html: '<div>before content</div>', 
                    position: this.customBlockPosition.BEFORE_CONTENT, 
                }, 
                { 
                    html: '<div>after content</div>', 
                    position: this.customBlockPosition.AFTER_CONTENT, 
                }, 
                { 
                    html: '<div>after container</div>', 
                    position: this.customBlockPosition.AFTER_CONTAINER, 
                } 
            ]; 
        } 
  
        render() { 
            // Example to illustrate how to add a subtitle: 
            const cardHeader = document.getElementsByClassName('iam-card-header')[0]; 
            let subtitle = document.getElementById('customSubtitle') 
            if (subtitle) { 
                // Undo previous renderings: 
                subtitle.parentNode.removeChild(subtitle); 
            } 
            subtitle = document.createElement("h3"); 
            subtitle.setAttribute('id', 'customSubtitle'); 
            subtitle.appendChild(document.createTextNode(this.translate('custom.login.subtitle'))); 
            cardHeader.appendChild(subtitle); 
        } 
    } 
  
    // Register the page: 
    iam.api.customizing.registerPage(new MyCustomPasswordPage()); 
}