How to Save Web Interface Usernames in a Cookie

In the article titled “How to Enable AutoComplete for Web Interface Logon”, I explained how to enable the AutoComplete functionality of web browsers in order to save usernames and/or passwords for Citrix Web Interface. As the article explained, there are a lot of moving parts to the solution such as web browser settings, Protected Storage, JavaScript workarounds, etc. In this article, I will explain how to accomplish something similar by using cookies to remember the last username entered for Web Interface.

In the article titled “How to Enable AutoComplete for Web Interface Logon”, I explained how to enable the AutoComplete functionality of web browsers in order to save usernames and/or passwords for Citrix Web Interface. As the article explained, there are a lot of moving parts to the solution such as web browser settings, Protected Storage, JavaScript workarounds, etc. In this article, I will explain how to accomplish something similar by using cookies to remember the last username entered for Web Interface.

How it Works (if you do not really care how it works, you can just download the modification at the end of this post)

There are really only two parts to this solution. Part 1 – store the username in a cookie. Part 2 – get the username from the cookie and populate the “user name” field at logon. There is a little Web Interface SDK work to obtain the username, but I will explain what is going on.

 

Part 1 – Storing the Username in a Cookie

There are two steps to accomplish this part.

First, we need to get the username after it is validated by Web Interface. An ideal place to get the authenticated username is applistView.ascx. applistView.ascx is the user control in Web Interface that is responsible for displaying a list of applications a user has access to. So, we know if we got to applistView.ascx in the Web Interface logon/application enumeration process that a username should be available.

Second, we need to store the username in a cookie (this is pretty easy in comparison to getting the username).

Here is the code:

Modify applistView.ascx

Find the following text (around line 10):

<!–#include file=”../serverscripts/include.aspxf”–>

Paste the following right after this line

<%

//----------------------------------------------------------------
// WI mod
//----------------------------------------------------------------

string username = String.Empty;

// Get username
com.citrix.authentication.tokens.AccessToken accessToken = com.citrix.wi.pageutils.Authentication.authGetPrimaryAccessToken(wiContext.getWebAbstraction());
if(accessToken != null)
{
if(accessToken is com.citrix.authentication.tokens.GuestToken)
{
// Guest
username = wiContext.getString("Guest");
}
else
{
username = accessToken.getUserIdentity();
}
}

HttpCookie userCookie = Request.Cookies.Get("WI_username");

if(userCookie == null)
{
// Cookie doesn't exist, so create it
userCookie = new HttpCookie("WI_username");
}
else if(userCookie.Value != username)
{
// Need to update the cookie value
Response.Cookies.Set(Request.Cookies.Get("WI_username"));
}

userCookie.Value = username;
userCookie.Expires = DateTime.Now.AddDays(100);
Response.Cookies.Add(userCookie);

//----------------------------------------------------------------
// End WI mod
//----------------------------------------------------------------

%>

Code explanation:

First, we need to get an AccessToken (see line 10 above). In the Web Interface object model, an AccessToken “…encapsulates information that may be used for authorization and authentication when a Subject requires access to a resource.” What that means in layman’s terms is an AcessToken basically holds your username, password, domain, identity, etc. (you can actually use the PasswordBasedToken Interface to get the password of a user if you wanted to as described in this article).

After we get the AccessToken, we can test to see if this is an anonymous user or not (an anonymous user would have a GuestToken) on lines 12-23 above. If the token is not a GuestToken, then we know we have an authenticated user. There are currently 3 methods you can use to get the user details from the AccessToken:

  1. getAccountIdentity() – gets an AccountIdentiy object.
  2. getShortUserName() – returns the username as a string. Example – if your user identity was domain\jasonco, getShortUserName() returns “jasonco”.
  3. getUserIdentity() – returns the entire logon identity as a string. Example: domain\username or user@domain (if using UPN). I chose getUserIdentity() in the code above.

The rest of the code is pretty straight forward – it just creates or updates the cookie named WI_username. You could actually name this cookie anything you wanted.

Part 2 – Retrieving/Populating the Username from the Cookie

This part is quite a bit easier than the first part. All we need to do is to get the cookie, if any, from the previous code and populate the username field on the log on screen. The logon screen code can be found in loginMainForm.inc.

Here is the code:

Modify loginMainForm.inc

Find the following text (around line 106):

<td colspan="2">
    <input type='text' name='<%=Constants.ID_USER%>' id='<%=Constants.ID_USER%>'
        class='loginEntries<%=viewControl.getExplicitDisabled()?" loginEntriesDisabled":""%>'
        maxlength='<%=Constants.LOGIN_ENTRY_MAX_LENGTH%>' <%=viewControl.getExplicitDisabledStr()%>
        tabindex='<%=Constants.TAB_INDEX_FORM%>'

Paste the following right after this text:

<%
//----------------------------------------------------------------
//       WI mod
//----------------------------------------------------------------

HttpCookie userCookie = Request.Cookies.Get("WI_username");

if(userCookie != null)
{
%>
    value='<%=userCookie.Value %>'
<%

}

//----------------------------------------------------------------
//       End WI mod
//----------------------------------------------------------------
%>

Code explanation:

The <input… tag above is the field where you fill in your username in loginMainForm.inc. The code inserted above sets the value of the input tag to whatever was stored in the WI_username cookie (if there was anything stored in the cookie at all).

If you want to implement this in your environment and do not feel like copying and pasting all this code, you can download the modified files below:

download Modification for Web Interface 5.1

How to Enable AutoComplete for Web Interface Logon

The logon page for Citrix Web Interface explicitly disables the web browser functionality of saving form data. But, what if you want to let your users save their username (especially if they have a particularly long UPN)? This article will show you how to re-enable this functionality for both Web Interface 4.x and 5.x.

The logon page for Citrix Web Interface explicitly disables the web browser functionality of saving form data. But, what if you want to let your users save their username (especially if they have a particularly long UPN)? This article will show you how to enable AutoComplete functionality for both Web Interface 4.x and 5.x.

Disclaimer – I would highly recommend only implementing this solution in a secure internal environment. You do not want your usernames or passwords floating around out there in the public.

 

Citrix Web Interface AutoComplete

 

 

How Web Interface disables AutoComplete
Web Interface hasn’t always disabled the AutoComplete functionality of web browsers.  I actually think it is wise Citrix did start disabling AutoComplete by default because a lot of Web Interface sites are external-facing and AutoComplete can be a bad thing in an external scenario. The way Web Interface disables AutoComplete is by adding a property to the <form> tag in the HTML.  Simply adding autocomplete=”off” will disable AutoComplete for all <input> elements within the <form>. If you look at the rendered WI login page’s HTML, you will see <form… autocomplete=”off”>.

Enabling AutoComplete for Web Interface 4.x
Enabling AutoComplete for Web Interface 4.x is actually quite simple.  All you really need to do is get rid of the autocomplete=”off” property in the <form> tag.  To do this:

Modify loginView.ascx

Change this:  <form method="POST" action="<%=PAGE_LOGIN%>" name="NFuseForm" autocomplete=”off”>

To this:  <form method="POST" action="<%=PAGE_LOGIN%>" name="NFuseForm">

A possible downside to this is that passwords will be saved as well.  If you do not want passwords to be saved, you can tell just the password field to disable AutoComplete.  To do this:

Modify loginMainForm.inc

Change this: <input type='password'…

To this: <input autocomplete="off" type='password'…

Enabling AutoComplete for Web Interface 5.x
Enabling AutoComplete for web Interface 5.x is the same as enabling AutoComplete in Web Interface 4.x with one exception.  Web Interface 5.x does not use a <input type=”submit” /> button to submit form data.  Instead, Web Interface 5.x uses JavaScript to submit the form data.  This causes an issue with AutoComplete because AutoComplete only commits data to Protected Storage via a <input type=”submit” /> button.  So, to get around this bug “feature”, we need to do some trickery.

Since AutoComplete only works with <input type=”submit” /> buttons, we need to add one of these buttons to our login page.  We do not actually want to see this button since it is just there to facilitate the AutoComplete functionality, so we set the display style on the submit button to none (<input type=”submit” style=”display: none” />)

Finally, we need to manipulate the Web Interface JavaScript code that submits to form to tell it to “virtually press” our hidden submit button (this will cause AutoComplete to save the data).  Here are all the steps necessary to implement AutoComplete in Web Interface 5.x:

Step 1 – Modify layout.ascx

Change this:  <form method="post" action="<%=FormAction%>" name="<%=Constants.ID_CITRIX_FORM%>" autocomplete="off">

To this: <form method="post" action="<%=FormAction%>" name="<%=Constants.ID_CITRIX_FORM%>">

 

Step 2 – Modify loginMainForm.inc

Add the following to the bottom (right after </table>):

<input type="submit" value="submit" id="hiddenSubmitButton" style="display:none" />

This is our hidden fake submit button that will trigger AutoComplete to save the data.

Step 3 – Modify login.js

Change this:

function submitForm() {
    if (!isSubmitted) {
        changeLoginBtnColor(false);

        isSubmitted = true;

        var loginBtn = document.getElementById("loginButtonWrapper");
        if(loginBtn){
            loginBtn.style.color = "#aaaaaa";
        }

        disableLinks();

        document.forms[0].submit();
    }
}

To this:

function submitForm() {
    if (!isSubmitted) {
        changeLoginBtnColor(false);

        isSubmitted = true;

        var loginBtn = document.getElementById("loginButtonWrapper");
        if(loginBtn){
            loginBtn.style.color = "#aaaaaa";
        }

        disableLinks();

        var hiddenSubmitBtn = document.getElementById("hiddenSubmitButton");
        if(hiddenSubmitBtn.click) {

                hiddenSubmitBtn.click();
        }
        else {
                document.forms[0].submit();
        }
        return false;
    }
}

The highlighted code basically tells the JavaScript submit function to “virtually click” the hidden submit button we added to loginMainForm.inc.
Again, if you want to disable passwords from being saved, add autocomplete=”off” to all the <input type=’password’ … /> fields in loginMainForm.inc.

 

 

Other factors of using AutoComplete
In order for AutoComplete to save form data, the feature needs to be turned on in your web browser.  For Internet Explorer, this can be found by going to Tools -> Internet Options -> Content tab.

Internet Explorer AutoComplete

For Firefox, this can be found by going to Tools -> Options -> Privacy.

FF-AutoComplete

AutoComplete stores form data in Protected Storage – which means that the Protected Storage service needs to be running.   Protected Storage can, on occasion, get corrupted.  See this Microsoft support article about this situation: http://support.microsoft.com/kb/306895

Citrix turns 20

Citrix turns 20 today. In this post, I will tell you how I got introduced to Citrix several years ago.

Citrix Citrix is 20 years old today (still under the drinking age in the Unites States). I remember when I was first introduced to Citrix. It was back around 1999 when I used to work for an integrator. Most of the work I did was Novell related(yes I was a CNE back in the day). Anyway, one of the projects that came down the pipeline was something totally different. My role in the project was to visit a multitude of nursing homes throughout Mississippi and Louisiana, install a Cisco router and switch (I was actually a CCNA then too), install NICs in computers, and install this thing called a Citrix client on the PCs. My final test was to make sure the Citrix client could “talk” to the Citrix server in the main location. I didn’t really realize what was going on with this Citrix thing until about the 5th install I did. It was then that I started to think this was some pretty cool stuff. By the way, this was all WinFrame 1.x (based on Windows NT 3.51).

I started to learn more about Citrix and later got certified as a CCEA. The integrator I was working for became part of the Citrix channel and I soon found myself working with some really cool SEs like Barry Flanagan. Barry still tells a story from the first time we met – I showed him where I installed a Java Citrix client on a Novell server (Novell supported Java by then). I was like – “Barry, look – you can run NWadmin directly from the Novell server console (NWadmin was a Windows app you used to administer Novell servers).” I guess he thought that was cool, because he still talks about it.

A lot has happened since then. I went to work for Citrix in Fort Lauderdale for a while. I started this Citrix-focused website. Citrix greatly diversified their product portfolio. I’ve written thousands of lines of code (for free) to extend Citrix products. I became a CTP. And, I’ve done quite a bit of technical public speaking (mostly Citrix focused).

So, I guess it is a good thing that I had to travel to all those nursing homes to setup that Citrix thing. By the way, I still remember some of those roads in Louisiana. I remember driving down “Devil’s Swap Road”. I also had to drive down a road that didn’t have a name, but I was told I would recognize it because the sugar cane was really tall that time of year. Also, as it turns out, nursing home food isn’t that bad.