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
28 Replies to “NetScaler Gateway: SAML with multiple IDPs using nFactor”
Thank you for sharing this information. This is a bit over my head at the moment, and I’m trying to learn and implement this, and you’ve documented it well enough that I *THINK* I understand whats happening! I have a question regarding the following line —
add authentication policylabel nf-saml-select.auth.pol.label.select-next -loginSchema nf-saml-select.auth.schema.noschema.true
Since we’re specifying a loginschema, shouldn’t it be named “nf-saml-select.auth.schema.noschema” (no .true at the end, since .true is actually a loginschemapolicy)?
Hi Joe, thanks for the feedback and glad you find it useful! I think you might be right! Sadly I dont have my PoC running anymore so I hope I’ll manage to test this again some day to (potentially) correct the article.
I’m having some problems getting this to work right. I’m never prompted for the username, it just moves forward to my SAML authentication (which fails, but I believe that is unrelated). I don’t ever get the OnlyUser.xml to display.
Hi Joe, I’ve actually seen this happening before in some newer versions of NetScaler – but I don’t know if it’s fixed in the latest version. Are you using the latest build?
However, there is a workaround!
Apparently it seems that NetScaler doesn’t display Factors with only NO_AUTHN policies anymore – why? I don’t know.
So what you can do is add any second, lower (higher number) priority, policy. For example a LOCAL one. Just as a dummy.
This will make the factor to be displayed again. However, this second dummy policy will never match due to the lower priority.
That worked for me in one scenario where the above config failed after an update.
You mind sharing your firmware and result – if successful?
I’d then include a note in the article.
I actually got around this and was coming here to update you. I’m currently on 12.0 56.20.
I found CTX226488 that detailed the creation of a noauth policy. It says to use “http.req.url.contains(“/nf/auth/doAuthentication.do”)” as the rule instead of True. Sure enough, it works every time now!
Now I just have to figure out how to get my SAML actions set right. I’m getting errors from azure telling me that request property Subject is not supported. So much fun! :)
Thanks again for the notes on getting this setup!!
Nice! I wasn’t aware of that one. Probably a cleaner solution than the dummy-policy. Thanks for sharing!
You’re welcome! I’m happy to get more people hooked on playing around with the mighty nFactor :-)
For reference if someone else finds this in the future. If whatever IDP submits any sso attribute to the netscaler, that is going to create the subject element even if you don’t need it, and in the case of AAD where it’s not supported, you can disable it by running:
nsapimgr_wr.sh -ys call=ns_saml_dont_send_subject
on your netscalers.
This is a great article that fits exactly a scenario that we are trying to work through. However, we are having an issue federating with AzureAD as the IDP. After the Netscaler captures the username, it inserts it as the subject in the SAML request. AzureAD apparently does not support subject in SAML requests, despite documentation indicating that it would just ignore it. If I authenticate without capturing the user name first, the request is sent without a subject and authentication is successful.
I realize that this is a Microsoft problem, but I thought I might have better luck fixing it on the Netscaler than getting Microsoft to change. Any ideas?
Hi Wes, I’m not sure but the first – and only – thing that comes to my mind is traffic policies.
The NS forwarding credentials seems like a default behavior that might be hard/impossible to change.
Two things come to my mind – just wild guesses though
– in nFactor schemas when you drop down the “advance options” (or something similar) I think you can set username and password fields – maybe you can work with that
– in traffic policies you can actually tweak what is used for a SSO – it might be a very remote guess but maybe that can be used to be applied in this case as well (https://netscalerrocks.com/netscaler/flexible-sso-in-build-10-5-56-12/ has some indication on what I mean)
Did you ever find a solution for this? Struggling with the exact same thing at the moment.
I’m struggling as well. How can we fix the error “The SAML authentication request property ‘Subject’ is not supported and must not be set.”?
NetScaler is always going to try to add any type of SSO credentials in the subject, even if its blank, when chaining idp’s.
Here’s how you remove the subject property from the assertion..
nsapimgr_wr.sh -ys call=ns_saml_dont_send_subject
LikeLiked by 1 person
For future reference if anyone finds this thread.
The netscaler is always going to add the nameid attribute in the subject element for SAML when chaining IdP’s, and in the case of AAD where its not supported, you can disable it on your netscalers by running:
nsapimgr_wr.sh -ys call=ns_saml_dont_send_subject
Rembember that you might have to pick up the attribute again tho depending on your config, in order to sign on to whatever service you are trying to reach.
Disable sending the subject to Azure, run the command:
nsapimgr_wr.sh -ys call=ns_saml_dont_send_subject
Kindly add the command to the rc.netscaler file as well so that it will take effect even after the netscaler reboots.
Reference article : https://support.citrix.com/article/CTX122271
Thanks for this great source! Today we have configured a Citrix Access 13.1 Gateway SAML to Azure AD with Cascade Authentication Policies. We have created the dummy authentication policy to get rid of the ‘no active authentication policy’. Second, we have removed the subject with the provided command, but after a reboot, the subject was back again.
To make sure that the configuration is being preserved after a reboot, we have done the following the CLI::
1. Run the command ‘nsapimgr_wr.sh -ys call=ns_saml_dont_send_subject’.
2. Run the command ‘touch /nsconfig/rc.netscaler’
3. Run the command ‘chmod 644 /nsconfig/rc.netscaler’.
4. Lat but not least: echo “nsapimgr_wr.sh -ys call=ns_saml_dont_send_subject” >> /nsconfig/rc.netscaler
This is a great guide. I’m actually trying to do the reverse: I want make it possible so that administrators in one forest can login to the gateway and can access/manage other forests (not necessarily at the same time). Would you have an idea on how to do this? Not sure if I’m clear enough…
So you want to use authentication from one domain/IDP to SSO into another domains Citrix infrastructure?
This might be possible but would need appropriate shadow-accounts in the target domain (or a Domain Trust) to make SSO work I believe.
I have this all working but when I type in my username on NetScaler onylusername screen, it redirects to the Azure Sign in page and I have to enter the username again before entering the third screen to enter the password.
Can I pull the username I enter on the NetScaler straight through to the password screen? Have I missed a step?
I’m aware of that issue but unfortunately I’m not aware of any solution for that problem.
What happens if I attempt to do an IDP initiated sign-on?
Hi Dennis, from my understanding Netscaler understands IDP-initiated Sign-Ons in general. However I think in this scenario you’d struggle because the user never passed the first factor (enter username).
You may get around this by inserting a blind NOSCHEMA factor upfront with something like this:
Referer=IDP1 => SAML Policy 1
Referer=IDPn => SAML Policy n
true => Display Dialog to enter username
I did end up building it out in a test environment that doesn’t have Citrix FAS enabled yet so full authentication couldn’t be tested. I did have issues as expected and what you mention. I have also had very little luck in my testing using Referer= with nFactor. It seems that there may be too many redirects in the nFactor processing inside the ADC and the Referer seems to get lost before it hits the rules. It is also entirely possible that I did something wrong in my testing with it.
I did however come up with an alternative that works for my scenario in that my IdP can actually send the IdP request to a different URL than the ACS URL, so sending to a different HOST name works. I don’t know what other IdPs allow that sort of thing, but for the most part I don’t really care since the vast majority of my clients won’t need it.
I appreciate the response!