nFactor Extensibility

nFactor authentication framework provides the flexibility of adding customizations to make the logon interface more intuitive for rich user experience. You can add custom login labels, custom login credentials, customizing UI displays and so on.

With nFactor, each factor can have its own logon screen. In each logon screen you can present any information from any of the previous factors or more information that is invisible in other factors. For example, your last factor can be an informative page where the user reads instructions and click continue.

Before nFactor, custom login pages were limited and customizations and needed support. It was possible to replace the tmindex.html or apply rewrite rules to change some of its behavior. However, it was not possible to achieve the underlying functionality.

The following nFactor related customizations are captured in detail in this topic.

  • Customize login labels
  • Customize UI to display images
  • Customize Citrix ADC nFactor logon form

Assumptions

You are familiar with nFactor, Shell commands, XML, and text editors.

Prerequisites

  • Customization described in this topic is possible only when RfWeb UI theme (or theme based) is configured on Citrix ADC.
  • Authentication policy must be bound to the authentication, authorization, and auditing virtual server, else the flow does not work as intended. For details, see CTX224241.
  • You have the following items related to nFactor
    • XML schema
    • JavaScript
    • Authentication actions
    • Authentication virtual server
    • Citrix ADC version 11.1 and later

Customize logon labels

To customize logon labels, you need the following:

  • The XML schema that describes how the logon page looks.
  • The script.js file that contains the JavaScript that is used to change the rendering process.

How it works

The JavaScript parses the XML file, rendering each item inside the <Requirements> tag. Each element corresponds to a line in the HTML form. For example, a login field is a line, the password field is another line, and so is the logon button. To introduce new lines, you must specify them in the XML schema file using the StoreFront SDK. The StoreFront SDK allows the logon page with an XML schema to use the <Requirement> tag and define elements on it. These elements allow to use JavaScript to introduce in that space whatever HTML elements are required. In this case, a line is created with some text in the form of HTML.

The XML that can be used is as follows:

<Requirement>
<Credential>
<Type>nsg-custom-cred</Type>
<ID>passwd</ID>
</Credential>
<Label>
<Type>nsg-custom-label</Type>
</Label>
</Requirement>

<Requirement>: Space provided in the logon page. The credential fills the space, and the other parts route the engine into the correct information. In this case, type nsg-custom-cred. This is defined as plain text and the label is defined for its body. The requirement XML is paired with the JavaScript code to achieve the required results.

// Custom Label Handler for Self Service Links
CTXS.ExtensionAPI.addCustomAuthLabelHandler({
getLabelTypeName: function () { return "nsg-custom-label"; },
getLabelTypeMarkup: function (requirements) {
return $("< Your HTML Code Here>");
},
// Instruction to parse the label as if it was a standard type
parseAsType: function () {
return "plain";
}
});
//Custom Credential Handler for Self Service Links
CTXS.ExtensionAPI.addCustomCredentialHandler({
getCredentialTypeName: function () { return "nsg-custom-cred"; },
getCredentialTypeMarkup: function (requirements) {
return $("<div/>");
},
});

The XML portion indicates the logon page what to display, and the JavaScript code provides the actual text. The credential handler opens up the space and the label fills the space. Because all authentication traffic is now invisible to rewrite and responder, you can change the look and feel of the page. Configuration to customize login labels

1. Create and bind a theme based on RfWeb.

add vpn portaltheme RfWebUI_MOD -basetheme RfWebUI

bind vpn vserver TESTAAA –portaltheme RfWebUI_MOD

The path for the files based on the theme is available in the directory; /var/netscaler/logon/themes/RfWebUI_MOD

2. Add the following snippet to the end of script.js file:

Note: Failing to include the preceding lines inside the correct file or missing to include any JavaScript functions prevent the XML from being loaded. The error can only be seen in the Developer Console of the browser with the following text: “Undefined Type nsg-custom-cred.”

// Custom Label Handler for Self Service Links
CTXS.ExtensionAPI.addCustomAuthLabelHandler({
getLabelTypeName: function () { return "nsg-custom-label"; },
getLabelTypeMarkup: function (requirements) {
return $("<a href=\"https://identity.test.com/identity/faces/register\" style=\"font-size: 16px;\" style=\"text-align: center;\">Self Registration</a><br><a href=\"https://identity.test.com/identity/faces/forgotpassword\" style=\"font-size: 16px;\" style=\"text-align: center;\">Forgot Password</a><br><a href=\"https://identity.test.com/identity/faces/forgotuserlogin\" style=\"font-size: 16px;\" style=\"text-align: center;\">Forgot User Login</a>");
},
// Instruction to parse the label as if it was a standard type
parseAsType: function () {
return "plain";
}
});
//Custom Credential Handler for Self Service Links
CTXS.ExtensionAPI.addCustomCredentialHandler({
getCredentialTypeName: function () { return "nsg-custom-cred"; },
getCredentialTypeMarkup: function (requirements) {
return $("<div/>");
},
});

Loginschema used in this example

<?xml version="1.0" encoding="utf-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
<Status>success</Status>
<Result>more-info</Result>
<StateContext/>
<AuthenticationRequirements>
<PostBack>/nf/auth/doAuthentication.do</PostBack>
<CancelPostBack>/Citrix/Authentication/ExplicitForms/CancelAuthenticate</CancelPostBack>
<CancelButtonText>Cancel</CancelButtonText>
<Requirements>
<Requirement>
<Credential>
<ID>login</ID>
<SaveID>Username</SaveID>
<Type>username</Type>
</Credential>
<Label>
<Text>User name</Text>
<Type>plain</Type>
</Label>
<Input>
<AssistiveText>Please supply either domain\username or user@fully.qualified.domain</AssistiveText>
<Text>
<Secret>false</Secret>
<ReadOnly>false</ReadOnly>
<InitialValue></InitialValue>
<Constraint>.+</Constraint>
</Text>
</Input>
</Requirement>
<Requirement>
<Credential>
<ID>passwd</ID>
<SaveID>Password</SaveID>
<Type>password</Type>
</Credential>
<Label>
<Text>Password:</Text>
<Type>plain</Type>
</Label>
<Input>
<Text>
<Secret>true</Secret>
<ReadOnly>false</ReadOnly>
<InitialValue/>
<Constraint>.+</Constraint>
</Text>
</Input>
</Requirement>
<Requirement>
<Credential>
<Type>nsg-custom-cred</Type>
<ID>passwd</ID>
</Credential>
<Label>
<Type>nsg-custom-label</Type>
</Label>
</Requirement>
<Requirement>
<Credential>
<ID>loginBtn</ID>
<Type>none</Type>
</Credential>
<Label>
<Type>none</Type>
</Label>
<Input>
<Button>Please Log On</Button>
</Input>
</Requirement>
</Requirements>
</AuthenticationRequirements>
</AuthenticateResponse>

Execute the following commands to load custom schema to config.

add authentication loginSchema custom -authenticationSchema custom.xml

add authentication loginSchemaPolicy custom -rule true -action custom

bind authentication vserver AAATEST -policy custom -priority 100 -gotoPriorityExpression END

The following figure displays the login page that is rendered with this configuration.

Localized image

Customize UI to display images

nFactor allows for customized display with the use of loginschema files. There might be a requirement for further customizations other than those offered by the built-in loginschema files. For example, displaying a hyperlink or writing custom logic in the UI. These can be achieved using ‘custom credentials’ that comprise of loginschema extension and corresponding javascript file.

For UI customization to display images, a deployment flow in “Citrix ADC-Swivel” integration is used as an example.

There are two factors in this flow.

  • First factor: Checks user’s AD credentials.
  • Second factor: Prompts for user logon based on group membership.

Localized image

In this flow, all users go through first factor. Before second factor, there is a pseudo factor to check if some users can be omitted from “swivel” factor. If user requires “swivel” factor, an image and a text box are displayed to enter code.

Solution

The solution for customizing UI to display images contains two parts;

  • Loginschema extension.
  • Custom script to process the loginschema extension.

Loginschema extension

To control form rendering, a custom ‘id’/’credential’ is injected into the loginschema. This can be done by reusing existing schema and modifying as per the requirement.

In the example, a loginschema that has only one text field (such as /nsconfig/loginschema/LoginSchema/OnlyPassword.xml) is considered. The following snippet is added to the loginschema.

<Requirement><Credential><ID>swivel_cred</ID><Type>swivel_cred</Type><Input><Text><Hidden>true</Hidden><InitialValue>${http.req.user.name}</InitialValue></Text></Input></Credential></Requirement>

In the snippet, “swivel_cred” is specified as “Type” of the credential. Because this is not recognized as a built-in ‘credential,’ UI looks for a handler for this type, and calls it if it exists. An initial value is sent for this credential which is an expression that Citrix ADC dynamically fills. In the example, it is the user’s name used to notify swivel server of the user name. It might not be needed all the time or it can be augmented with some other data. Those details must be added as required.

Javascript to handle custom credential

When the UI finds a custom credential, it looks for a handler. All custom handlers are written in /var/netscaler/logon/LogonPoint/custom/script.js for default portal theme. For custom portal themes, script.js can be found in the directory /var/netscaler/logon/themes/<custom_theme>/.

The following script is added to render mark-up for custom credentials.

CTXS.ExtensionAPI.addCustomCredentialHandler({
    // The name of the credential, must match the type returned by the server
    getCredentialTypeName: function () { return "swivel_cred"; },
    // Generate HTML for the custom credential
    getCredentialTypeMarkup: function (requirements) {
        var div = $("<div></div>");
        var image = $("<img/>");
        var username = requirements.input.text.initialValue; //Get the secret from the response
        image.attr({
            "style" : "width:200px;height:200px;",
            "id" : "qrcodeimg",
            "src" : "https://myswivelserver.citrix.com:8443/pinsafe/SCImage?username=" + username
        });
        div.append(image);
        return div;
    }
});

This snippet is for handling the mark-up for ‘swivel_cred’. Credential name highlighted must match the ‘type’ specified earlier in the loginschema extension. To generate mark-up, an image whose source points to swivel server needs to be added. Once this is done, UI loads image from specified location. Because this loginschema also has a textbox, UI renders that text box.

Note: Administrator can modify “style” of image element to resize the image. Currently it configured for 200x200 pixels.

Configuration for customizing UI to display images

nFactor configuration is better constructed bottom-up, that is the last factor first because when you try to specify ‘nextFactor’ for the previous factors, you require the subsequent factor’s name.

Swivel factor configuration:

add loginschema swivel_image –authenticationSchema /nsconfig/loginschema/SwivelImage.xml

add authentication policylabel SwivelFactor –loginSchema swivel_image

bind authentication policylabel SwivelFactor –policy <policy-to-check-swivel-image> -priority 10

Note: Download SwivelImage.xml from the loginschema used in the example.

Pseudo factor for group check configuration:

add authentication policylabel GroupCheckFactor

add authentication policy contractors_auth_policy –rule ‘http.req.user.is_member_of(“contractors”)’ –action NO_AUTHN

add authentication policy not_contractors _auth_policy–rule true –action NO_AUTHN

bind authentication policylabel GroupCheckFactor –policy contractors_auth_policy –pri 10 –nextFactor SwivelFactor

bind authentication policylabel GroupCheckFactor –policy not_contractors_auth_policy –pri 20

First factor for Active Directory login:

add ldapAction <>

add authentication policy user_login_auth_policy –rule true –action <>

bind authentication vserver <> -policy user_login_auth_policy –pri 10 –nextFactor GroupCheckFactor

In the configuration, three factors are specified of which one is implicit/pseudo.

Loginschema used in this example

The following is an example schema with swivel credential and a text box.

Note: When copying data for web browser, quotes might be displayed differently. Copy data in editors like notepad before saving them to files.

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
<Status>success</Status>
<Result>more-info</Result>
<StateContext></StateContext>
<AuthenticationRequirements>
<PostBack>/nf/auth/doAuthentication.do</PostBack>
<CancelPostBack>/nf/auth/doLogoff.do</CancelPostBack>
<CancelButtonText>Cancel</CancelButtonText>
<Requirements>
<Requirement><Credential><ID>swivel_cred</ID><Type>swivel_cred</Type><Input><Text><Hidden>true</Hidden><InitialValue>${http.req.user.name}</InitialValue></Text></Input></Credential></Requirement>
<Requirement><Credential><ID>passwd</ID><SaveID>ExplicitForms-Password</SaveID><Type>password</Type></Credential><Label><Text>Password:</Text><Type>plain</Type></Label><Input><Text><Secret>true</Secret><ReadOnly>false</ReadOnly><InitialValue></InitialValue><Constraint>.+</Constraint></Text></Input></Requirement>
<Requirement><Credential><Type>none</Type></Credential><Label><Text>Hello ${http.req.user.name}, Please enter passcode from above image.</Text><Type>confirmation</Type></Label><Input /></Requirement>
<Requirement><Credential><ID>saveCredentials</ID><Type>savecredentials</Type></Credential><Label><Text>Remember my password</Text><Type>plain</Type></Label><Input><CheckBox><InitialValue>false</InitialValue></CheckBox></Input></Requirement>
<Requirement><Credential><ID>loginBtn</ID><Type>none</Type></Credential><Label><Type>none</Type></Label><Input><Button>Log On</Button></Input></Requirement>
</Requirements>
</AuthenticationRequirements>
</AuthenticateResponse>

Output

Once the configuration is performed, the following image is displayed.

Localized image

Note: Image height and placement can be altered in the JavaScript.

Customize Citrix ADC nFactor logon form to show or hide fields

Citrix Gateway’s RfWeb UI allows for wide variety of customizations. This capability when combined with nFactor authentication framework lets customers configure complex flows without compromising existing workflows.

In this example, two authentication options, OAuth and LDAP are available from the Logon Type list. When the form is first loaded, user name and password fields (LDAP is shown first) are displayed. If OAuth is selected, all the fields are hidden because OAuth implies offload of authentication to a third party server. This way, administrator can configure intuitive workflows as per user convenience.

Note:

  • The values in the Logon Type list can be modified with simple modifications to the script file.
  • This section describes only the UI part of the flow. The run time handling of the authentication is outside the scope of this article. Users are recommended to refer to nFactor documentation for authentication configuration.

How to customize nFactor logon form

Customizing nFactor logon form can be classified into two parts

  • Sending right loginschema to the UI
  • Writing a handler to interpret loginschema and user selections

Send right loginschema to the UI

In this example, a simple claim/requirement is sent in the loginschema.

For this, SingleAuth.xml file is modified. SingleAuth.xml is shipped with Citrix ADC firmware and can be found in /nsconfig/loginschema/LoginSchema directory.

Steps to send loginschema:

1. Log in via SSH and drop to shell (type ‘shell’).

2. Copy SingleAuth.xml to a different file for modification.

Note: The destination folder is different from the default Citrix ADC loginschemas folder. cp /nsconfig/loginschema/LoginSchema/SingleAuth.xml /nsconfig/loginschema/SingleAuthDynamic.xml

3. Add the following claim to SingleAuthDynamic.xml.

<Requirement><Credential><ID>nsg_dropdown</ID><Type>nsg_dropdown</Type></Credential><Label><Text>Logon Type:</Text><Type>plain</Type></Label></Requirement>

4.Configure Citrix ADC to send this loginschema to load the first form.

add loginschema single_auth_dynamic –authenticationSchema SingleAuthDynamic.xml

add loginschemaPolicy single_auth_dynamic –rule true –action single_auth_dynamic

bind authentication vserver aaa_nfactor –policy single_auth_dynamic –pri 10

Script changes to load form and handle user events

You can modify the JavaScript that enables administrator to customize display for logon form. In this example, user name and password field are displayed if LDAP is chosen and are hidden if OAuth is chosen. Administrator can also hide only the password. Admins must append the following snippet to “script.js” that is at “/var/netscaler/logon/LogonPoint/custom” directory. Note: Because this directory is a global directory, create a portal theme and edit the “script.js” file within that folder, at "/var/netscaler/logon/themes/<THEME_NAME>".

CTXS.ExtensionAPI.addCustomCredentialHandler({
    // The name of the credential, must match the type returned by the server
    getCredentialTypeName: function () { return "nsg_dropdown"; },
    // Generate HTML for the custom credential
    getCredentialTypeMarkup: function (requirements) {
        var div = $("<div></div>");
        var select = $("<select name='nsg_dropdown'></select>").attr("id", "nsg_dropdown");

        var rsa = $("<option></option>").attr("selected", "selected").text("LDAP").val("LDAP");
        var OAuthID = $("<option></option>").text("OAuth").val("OAuth");
        select.append(rsa, OAuthID);

        select.change(function(e) {
            var value = $(this).val();
            var ldapPwd = $($(".credentialform").find(".CredentialTypepassword")[0]);
            var ldapUname = $($(".credentialform").find(".CredentialTypeusername"));
            if(value == "OAuth") {
                if (ldapPwd.length)
                        ldapPwd.hide();
                if (ldapUname.length)
                        ldapUname.hide();
            } else if(value == "LDAP") {
                if (ldapPwd.length)
                        ldapPwd.show();
                if (ldapUname.length)
                        ldapUname.show();
            }
        });
       div.append(select);
        return div;
    }
});

End-user experience

When an end user loads the logon page first time, the following screen appears.

Localized image

If OAuth is selected in Logon Type, user name and password fields are hidden.

Localized image

If LDAP is selected, user name and password are displayed. This way, the logon page can be dynamically loaded based on user selection.

Loginschema used in this example

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
<Status>success</Status>
<Result>more-info</Result>
<StateContext></StateContext>
<AuthenticationRequirements>
<PostBack>/nf/auth/doAuthentication.do</PostBack>
<CancelPostBack>/nf/auth/doLogoff.do</CancelPostBack>
<CancelButtonText>Cancel</CancelButtonText>
<Requirements>
<Requirement><Credential><ID>login</ID><SaveID>ExplicitForms-Username</SaveID><Type>username</Type></Credential><Label><Text>User name</Text><Type>plain</Type></Label><Input><AssistiveText>Please supply either domain\username or user@fully.qualified.domain</AssistiveText><Text><Secret>false</Secret><ReadOnly>false</ReadOnly><InitialValue></InitialValue><Constraint>.+</Constraint></Text></Input></Requirement>
<Requirement><Credential><ID>passwd</ID><SaveID>ExplicitForms-Password</SaveID><Type>password</Type></Credential><Label><Text>Password:</Text><Type>plain</Type></Label><Input><Text><Secret>true</Secret><ReadOnly>false</ReadOnly><InitialValue></InitialValue><Constraint>.+</Constraint></Text></Input></Requirement>
<Requirement><Credential><ID>nsg_dropdown</ID><Type>nsg_dropdown</Type></Credential><Label><Text>Logon Type:</Text><Type>plain</Type></Label></Requirement>
<Requirement><Credential><Type>none</Type></Credential><Label><Text>First factor</Text><Type>confirmation</Type></Label><Input /></Requirement>
<Requirement><Credential><ID>saveCredentials</ID><Type>savecredentials</Type></Credential><Label><Text>Remember my password</Text><Type>plain</Type></Label><Input><CheckBox><InitialValue>false</InitialValue></CheckBox></Input></Requirement>
<Requirement><Credential><ID>loginBtn</ID><Type>none</Type></Credential><Label><Type>none</Type></Label><Input><Button>Log On</Button></Input></Requirement>
</Requirements>
</AuthenticationRequirements>
</AuthenticateResponse>

Important: For more details about various nFactor related topics, see nFactor.