Both SAML as well as nFactor are two NetScaler features that are highly underrated in my opinion. In this example I’ll share with you how I did combine them in a customer deployment to create a quite unique login experience.
- Resulting User Experience
- Customizations to the Login Schema
In the deployment in question I had one customer with the following requirements:
- He’s running NetScaler Gateway along with a XenApp/XenDesktop farm
- They wanted to provide access to a variety of different independent organizations
- Ideally authentication should use SAML on NetScaler along with Citrix Federated Authentication Service (FAS) within XenApp/XenDesktop
- The customer wanted to provide a single URL for all access to that service
This way each organisation that get’s on-boarded just needs to setup an ADFS server, provides the IDP certificate and could be integrated with a minor NetScaler configuration. The customers would retain authority about their user directory and users could re-use the credentials they’re familiar with.
The solution, as you might have guessed, was nFactor. After accessing the service the users are asked for their E-Mail address.
Based on this the user is then getting redirected to their respective organization’s IDP.
This is realized by a two-stage nFactor configuration in which the first stage gathers the username and the next factor is determined based on the E-Mail suffix provided.
Now I won’t go into every detail of how nFactor works. That will be worth an article on it’s own. For now you could find some background in the product documentation Multi-Factor nFactor Authentication
On a high level, the configuration for the described scenario looks as follows:
Some points to note if you’re new to nFactor:
- nFactor in NetScaler Gateway utilizes a AAA vServer for it’s configuration
- The AAA server being the first factor has a “No Auth” policy bound which is always successful – we only gather input here
- Next factor always points to an Authentication Policy Label of which you can think of like “another AAA server” as the same entities are bound here
- The Policy Label then has a series of SAML policies bound which redirect based on the E-Mail suffix
Ok, so how do we configure this?
First of all you’ll need your SAML authentication profiles setup. I won’t go into details about that in this article. You’ll find some information in the product documentation SAML Authentication
Create the Second Factor (Policy Label)
We’ll start this implementation from the rear end and create our SAML policies first. These will apply based on the user’s suffix and this is basically where all the magic is happening.
add authentication Policy nf-saml-select.auth.pol.saml.if-domain-a -rule "HTTP.REQ.USER.NAME.SET_TEXT_MODE(IGNORECASE).AFTER_STR("@").EQ("domain-a.local")" -action nf-saml-select.auth.act.saml.domain-a add authentication Policy nf-saml-select.auth.pol.saml.if-domain-b -rule "HTTP.REQ.USER.NAME.SET_TEXT_MODE(IGNORECASE).AFTER_STR("@").EQ("domain-b.local")" -action nf-saml-select.auth.act.saml.domain-b
Next we’ll need to create a “empty” Login Schema for the SAML authentication. Why empty? Because SAML doesn’t need a schema on NetScaler since the IDP is performing the authentication.
add authentication loginSchema nf-saml-select.auth.schema.noschema -authenticationSchema noschema add authentication loginSchemaPolicy nf-saml-select.auth.schema.noschema.true -rule true -action nf-saml-select.auth.schema.noschema
Finally we can create a Policy Label combining the SAML policies with the Login Schema
add authentication policylabel nf-saml-select.auth.pol.label.select-next -loginSchema nf-saml-select.auth.schema.noschema.true bind authentication policylabel nf-saml-select.auth.pol.label.select-next -policyName nf-saml-select.auth.pol.saml.if-domain-a -priority 100 -gotoPriorityExpression END bind authentication policylabel nf-saml-select.auth.pol.label.select-next -policyName nf-saml-select.auth.pol.saml.if-domain-b -priority 110 -gotoPriorityExpression END
This concludes the second factor which could now be used as a next factor for the first factor.
Create the First Factor (AAA vServer)
First of all we’d need an Authentication Server to work with and bind an appropriate certificate. The server does not need to be addressable so you can just enter an empty IP address.
add authentication vserver nf-saml-select.aaa.vs.int SSL 0.0.0.0 bind ssl vserver nf-saml-select.aaa.vs.int -certkeyName wildcard.nerdscaler.lab
Next you’ll need the Authentication Policy which will be of type NO_AUTHN. This results in the first factor always being successful since we’re only gathering information. The actual authentication is completely up to the second factor.
add authentication Policy nf-saml-select.auth.pol.noauth -rule true -action NO_AUTHN
As a last piece we’ll also need another Login Schema. This time we’ll use the OnlyUsername.xml that is shipped with the NetScaler to start with
add authentication loginSchema nf-saml-select.auth.schema.onlyuser -authenticationSchema "/nsconfig/loginschema/LoginSchema/OnlyUsername.xml" add authentication loginSchemaPolicy nf-saml-select.auth.schema.onlyuser.true -rule true -action nf-saml-select.auth.schema.onlyuser
Finally we can bind everything to our Authentication Server and – most important – point to the previously created Policy Label as a Next Factor.
bind authentication vserver nf-saml-select.aaa.vs.int -policy nf-saml-select.auth.schema.onlyuser.true -priority 100 -gotoPriorityExpression END bind authentication vserver nf-saml-select.aaa.vs.int -policy nf-saml-select.auth.pol.noauth -priority 100 -nextFactor nf-saml-select.auth.pol.label.select-next -gotoPriorityExpression NEXT
This concludes the nFactor authentication itself.
Setup NetScaler Gateway for nFactor authentication
Finally we need to configure our NetScaler Gateway to point to the AAA vServer for authentication. This is done creating and setting a Authentication Profile.
add authentication authnProfile nf-saml-select.auth.prof -authnVsName nf-saml-select.aaa.vs.int set vpn vserver nf-saml-select.ngw.vs.ssl.443 -authnProfile nf-saml-select.auth.prof
This leaves you with the following…
Resulting User Experience
If the user logs in with a user from Domain-A…
…he gets redirect to Domain-A’s IDP.
If the user logs in with a user from Domain-B…
…he gets redirect to Domain-B’s IDP.
If a user logs in with a non-configured Domain he’ll receive a error message:
Customizations to the Login Schema
One of the great features of nFactor is the fact that you can finally fully customize your login form. So if you actually want to ask for an E-Mail instead of a UPN you can simply copy and customize one of the Login Schemas in /nsconfig/loginschema/LoginSchema/
Based on the OnlyUsername.xml I’ve created the following OnlyEmail.xml for 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>E-Mail</Text><Type>plain</Type></Label><Input><AssistiveText>Please supply E-Mail</AssistiveText><Text><Secret>false</Secret><ReadOnly>false</ReadOnly><InitialValue></InitialValue><Constraint>.+</Constraint></Text></Input></Requirement> <Requirement><Credential><Type>none</Type></Credential><Label><Text> Please enter E-Mail ...</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>
Which results in the following login form