NetScaler Native OTP – Prevent Enrollment of Additional Devices Externally

Download NetScaler Native OTP Device Limit Guide:
Full Version (GUI) | Short Version (CLI)

With the introduction of NetScaler 12.0 (build 51.24 to be exact), Citrix enhanced the value of NetScaler Unified Gateway even more by embedding the native support for one-time password (OTP). Initially, the OTP mobile apps were provided by third-parties, for example, Google and Microsoft Authenticators, but recently Citrix added this support to their own Citrix SSO mobile app.

I will not describe the details and benefits of NetScaler’s native OTP in this article. You are welcome to read about the solution in the Citrix Blog Post NetScaler Unified Gateway Provides One Time Password (OTP), Natively or watch the YouTube video here. There are several very detailed guides available to help you understand and configure the feature, such as Carl Stalhood and George Spiers guides as well as Citrix’s own NetScaler One Time Password (OTP) Guide. In this blog post I will share with you how we can limit the number of enrolled devices to one.

We first got this question from one of our clients, but after that we heard it many times from partners, fellow Citrix experts and other clients. At Citrix Synergy 2018 in Anaheim we shared the solution with Citrix NetScaler Engineering Team – the creators of nFactor and OTP support. Several Citrix customers and partners asked for this during Synergy sessions, so finally (sorry for the delay, guys) I am publishing it here.

Initially, the number of devices that can be enrolled for OTP was not limited. This, to some extent, defeated the purpose of the Multi Factor Authentication with OTP, as any user could register an OTP device as long as the user’s credentials were known. Citrix will limit this in the next builds of 12.0 and 12.1 to five devices, but even this may be unacceptable to most clients. In their guides Carl and George recommend limiting the access to OTP Management to internal networks only by configuring the Login Schema Policy with CLIENT.IP.SRC.IN_SUBNET(subnet/mask) expression:
http.req.cookie.value(“NSC_TASS”).eq(“manageotp“) && client.IP.SRC.IN_SUBNET(

We decided to take this further:

  • Prevent enrollment of any additional devices externally. This means the user can enroll OTP device(s) if he has not enrolled any devices yet. However, we will prevent device enrollment from external networks if the user has previously enrolled an OTP device.
  • Do not limit the number of devices that can be enrolled from internal networks.

OTP stores device enrollment secrets in an Active Directory attribute that accepts Strings. Citrix’s documentation uses the userParameters Active Directory attribute. This guide assumes that you are using the standard (recommended) userParameters AD attribute.

Once an OTP device is enrolled/registered, NetScaler will write a string into the userParameters attribute of the user in the following format:


The trick is to take the userParameters attribute and write it to NetScaler’s internal User Attribute (I will use Attribute #7 in this guide), so it can later be used in the Login Schema and Authentication Policy expressions to evaluate if the user had already enrolled a device.

Since we have to know whether or not NetScaler should display the second (OTP) password field after the LDAP authentication happens, we will modify the way we present the credential prompts to users. To sum it up, instead of this:

We will do this:

Step 2

As a bonus this method gives us more flexibility to check other conditions before deciding to prompt the user for the second authentication factor. For example, check the AD Group membership. Here is an example scenario where the company security policy requirements are as follows:

  • Regular users should not be prompted for 2FA to access the Unified Gateway
  • Privileged (SSL VPN or say RDP Proxy) users must be prompted for 2FA to get access

In this case, we can check the user’s AD Group membership before displaying the Authenticator Passcode prompt.


We are going to build an authentication flow that includes the native OTP functionality. Here is the list of requirements:

  • The solution will support both single and multi-factor authentication
  • Only members of CTX-NetScaler-OTP AD Group will have the ability to use multi-factor authentication and enroll their devices
  • Users that are not members of CTX-NetScaler-OTP AD Group will be able to login with single-factor (LDAP) and have limited access to the Unified Gateway resources (managed by Session Policies, filtered by the AD Group)
  • Users will be limited to enroll a single device if accessing the Unified Gateway externally

Authentication Logical Flow and nFactor Policies

You can download the Short (CLI) Version of the guide or Full (GUI) Version that also includes some other considerations, such as Active Directory and Service Accounts.

Additional tip:
Check out How Do I Citrix NetScaler CLI series and grab a NetScaler CLI Troubleshooting cheat sheet to help you with your configurations.  It’s a handy cheat sheet that contains important commands, paths, and shortcuts, that are available on the net, but it usually takes way too much time to find them.

Please feel free to leave your comments and suggestions here, contact me via LinkedIn, email or follow me on Twitter.

By Stan Demburg

Explore how iRangers are changing the game by designing Virtualization technology solutions adjusted to real business needs.

For latest updates, follow us on LinkedIn.

2 thoughts on “NetScaler Native OTP – Prevent Enrollment of Additional Devices Externally

  1. My goal on this was to have 2Fa only enabled for users that were a member of an AD group. If they weren’t you would log in the first page with username and password, then get in to your citrix desktops. If you were a member of the group, the next page after login would be to enter your 2Factor key. I followed these instructions to a T, but they seem to get fuzzy towards the end of dealing with the group memberships and creating the session policies for them. After creating the two session policies, I went to bind the RDP.. one to the NS-Internal… AAA group, but I get an error:

    “Advanced VPN Session Policy cannot be bound if Classic VPN Session Policy is already bound to any entity (i.e. aaa user, aaa group, vpn vserver, vpn global)”

    Don’t understand that, as these are new policies that are just created and are not bound to anything. But even beyond that, I don’t see anywhere where you would map an AD account to this netscaler group, which is what I thought was part of this article. What I did instead, was in the authentication policy put my AD group in for AAA.USER.IS_MEMBER_OF() (and I used AAA.Users because netscaler said that HTTP.REQ.USERS was deprecated).

    When I go to the site, I get the login, but when I log in it goes to the next page where I get “No Active Policy During Authentication” error. I guess I’m stuck on where to go next. Any help would be great. Thanks

    1. Hi Ryan,
      The reason you’re getting the “Advanced VPN Session Policy cannot be bound if Classic VPN Session Policy is already bound…” error is because you have a Classic Session Policy bound somewhere else (ex. directly to your Gateway vServer, globally or AAA Group).

      In general, my advice is to create Advanced Session Policies, then unbind all your Classic Session Policies from everywhere and bind the new Advanced policies. Chances are you using simple “NS_TRUE” classic expressions which translate to “TRUE” advanced expression. The only valid reason to keep the Classic Policies is if you are using EPA checks as part of your Session Policies. In this case, you can migrate EPA to nFactor and get rid of Classic Policies (this still has some limitations) or keep the Classic Policies. If you want to keep Classic Policies, then use “NS_TRUE” classic expression instead of the “TRUE” advanced one.

      The AD Group membership check actually happens during the Authentication (which is before any Session Policy gets applied), after the LDAP Factor and before any of the OTP factors. It is done in the “UG-noauth-OTP-Verify_authpol” authentication policy:

      add authentication Policy UG-noauth-OTP-Verify_authpol -rule “AAA.USER.IS_MEMBER_OF(\”CTX-NetScaler-OTP\“) && HTTP.REQ.USER.ATTRIBUTE(7).CONTAINS(\”#@\”)” -action NO_AUTHN

      Replaced HTTP.REQ.USER. by AAA.USER – as you mentioned, the first one is deprecated.

      Stan Demburg

Leave a Reply

Your email address will not be published. Required fields are marked *