Download full document:

StoreFront Authentication Form Generation

Introduction

Citrix has devised a common authentication protocol that is implemented by its next generation services and gateway platforms, referred to here as StoreFront Services and NetScaler Gateway. Citrix has implemented client support for this common authentication protocol in native user agents, such as Receiver and Worx, for the major device platforms, notably Windows, Mac, iOS, Android, Linux, and Receiver for Web for browser-based user agents.

An important part of the common authentication protocol is a generic forms language, which is described in the Receiver Common Authentication Forms Language document [2]. This language is designed to allow third parties to express a wide range of authentication forms using a set of user interface constructs that are similar to basic HTML forms; see RFC 1867 http://www.ietf.org/rfc/rfc1867.txt and 2388 http://www.ietf.org/rfc/rfc2388.txtfor more details.

Third parties can extend the authentication methods supported by StoreFront Services and NetScaler Gateway by implementing server logic that emits and consumes forms defined in the common forms language; see the Citrix Authentication Software Development Kit [1] for more details.

This document aims to enable Citrix partners to understand how StoreFront generates the forms and to enable them to customize the existing form definitions, create new forms, and new sequences of forms to create new authentication conversations.

Overview

Forms-based authentication is a conversation using the HTTP protocol where the client responds to a series of forms generated by the server to establish an identity on the server. The HTTP API is described in the Citrix Common Forms API document [3]. There are several StoreFront components that interact to achieve the generation of the forms and the handling of the HTTP responses.

StoreFront makes extensive use of service location and inversion-of-control (IoC) containers; see http://martinfowler.com/articles/injection.html for more details of this software pattern.

Forms Generation Components for a conversation requiring Active Directory credential validation Forms Generation Components for a conversation requiring Active Directory credential validation

Handling HTTP requests and responses is the responsibility of a small set of built-in MVCs (model view controllers). In most cases there will be no need for custom form implementers to implement new controllers. For general forms, where there is no requirement to validate Active Directory credentials, the FormsAuthenticationController ASP.Net MVC controller should be used. If validating Active Directory credentials is required, then the ExplicitFormsAuthenticationController controller should be used. The MVC controllers validate the requests and extract the state associated with the conversation and then construct and invoke the conversation engine.

The conversation engine is responsible for running the conversation, which includes creating the appropriate conversation object and dispatching messages to it. There are two built-in conversation engines: ConversationEngine, used when validating Active Directory credentials is not required, and ExplicitConversationEngine, used when validating Active Directory credentials is required. Again, it is not anticipated that custom forms implementers will need to modify the conversation engine functionality.

The configuration for the MVC route includes the name, used by the Service Locator to find a class that implements the IFormsConversationFactory interface, stored in the IoC container. The appropriate conversation engine uses this to locate the custom conversation factory that uses any settings required, to create a new custom conversation object.

The custom conversation object is responsible for orchestrating the sequence of forms and dispatching messages to appropriate form objects. It is expected that custom forms implementers that require a complex series of forms will implement a conversation object. However, there are two built-in classes that provide the base functionality that implementers can build on. These are FormsConversationBase, used when validating Active Directory credentials is not required, and ExplicitConversation, used when validating Active Directory credentials is required.

An individual form is usually composed of two elements: the form object and the associated form template. The form object is responsible for generating a dynamic data model that is passed to the forms template engine with the name of the forms template to generate the form that will be transmitted to the client. The form object is also responsible for processing the response to the form generated by the client. Although StoreFront has a small number of built-in form objects and their associated templates, custom forms implementers will generally have to implement new forms objects and templates.

The forms template engine is the built-in component that can be used by individual forms to generate the final Citrix Common Form XML from a template and dynamic data. It is not expected that custom forms implementers will be required to modify this component.

Form Conversation Factory

If a custom conversation is required, then a custom factory is required that implements the following interface:

namespace Citrix.DeliveryServices.Authentication.FormsProtocol.Conversations.Interfaces
{
    /// <summary>
    /// This interface represents a factory for instances of <seealso cref="IFormsConversation"/>
    /// </summary>
    public interface IFormsConversationFactory
    {
        /// <summary>
        /// Create a new instance of an object that implements <seealso cref="IFormsConversation"/>
        /// </summary>
        /// <param name="controllerContext">
        /// The <seealso cref="ControllerContext"/> for where the conversation will be run</param>
        /// <param name="templateEngine">
        /// The <seealso cref="FormsTemplateEngine"/> to use to render the conversation</param>
        /// <param name="cultures">The current language cultures</param>
        /// <returns>
        /// A new instance of an object that implements <seealso cref="IFormsConversation"/>
        /// </returns>
        IFormsConversation Create(ControllerContext controllerContext,
                                  FormsTemplateEngine templateEngine,
                                  IEnumerable<CultureInfo> cultures);
    }
}

This factory must be stored in the IoC container with a specific name, and this is usually achieved through the use of a start-up module (see the StoreFront Service Authentication SDK for more details on start-up modules). The appropriate conversation engine obtains the factory name from the route data associated with the start of the customized conversation in the web.config file. For example, the following is the route data for the built-in user name and password conversation:

<explicitAuthentication>
      <routeTable order="0">
        <routes>
          <route name="StartExplicitAuthentication" url="ExplicitForms/Start">
            <defaults>
              <add param="controller" value="ExplicitFormsAuthentication" />
              <add param="action" value="AuthenticateStart" />
              <add param="postbackAction" value="Authenticate" />
              <add param="cancelAction" value="CancelAuthenticate" />
              <add param="conversationFactory" value="ExplicitAuthentication" />
              <add param="changePasswordAction" value="StartChangePassword" />
              <add param="changePasswordController" value="ChangePassword" />
              <add param="protocol" value="ExplicitForms" />
            </defaults>
            <data />
          </route>
          <route name="ExplicitAuthentication" url="ExplicitForms">
            <defaults>
              <add param="controller" value="ExplicitFormsAuthentication" />
              <add param="action" value="Authenticate" />
              <add param="protocol" value="ExplicitForms" />
            </defaults>
            <data />
          </route>
          <route name="CancelExplicitAuthentication" url="ExplicitForms/Cancel">
            <defaults>
              <add param="controller" value="ExplicitFormsAuthentication" />
              <add param="action" value="CancelAuthenticate" />
              <add param="protocol" value="ExplicitForms" />
            </defaults>
            <data />
          </route>
        </routes>
      </routeTable>
    </explicitAuthentication>

The conversation engine uses the service locator to obtain the factory object with the specified name and then invokes the Create() method to obtain the custom conversation object.

Form Conversation

Most new authentication conversations will require a conversation object to orchestrate the sequence of forms. The StoreFront Services Authentication SDK contains samples that demonstrate creating custom conversation objects.

When a conversation is started, the first action is to create a ConversationState object that has the following API:

namespace Citrix.DeliveryServices.Authentication.FormsProtocol.Conversations
{
    /// <summary>
    /// This data class contains the conversation state that is required
    /// to be stored in the session state between requests.
    /// </summary>
    public class ConversationState
    {
        /// <summary>
        /// Constructor
        /// </summary>
        public ConversationState()
        {
            Extensions = new Dictionary<string, object>();
        }

        /// <summary>
        /// Gets or sets the <seealso cref="FormsTemplateEngine"/>.
        /// </summary>
        /// <remarks>
        /// This is used to render the forms that make up the conversation.
        /// </remarks>
        public FormsTemplateEngine TemplateEngine { get; set; }

        /// <summary>
        /// Gets or sets the current <seealso cref="IFormsConversation"/>
        /// </summary>
        public IFormsConversation CurrentConversation { get; set; }

        /// <summary>
        /// Gets or sets the <seealso cref="RequestToken"/> message
        /// sent at the start of the conversation.
        /// </summary>
        /// <remarks>
        /// This is used to generate the request security token response
        /// at the end of the conversation.
        /// </remarks>
        public RequestToken RequestToken { get; set; }

        /// <summary>
        /// Gets or sets the <seealso cref="ITokenService"/> used to
        /// issue the <seealso cref="IToken"/> generated at the end
        /// of the conversation.
        /// </summary>
        public ITokenService TokenIssuingService { get; set; }

        /// <summary>
        /// Gets or sets whether or not this conversation is delegated using the
        /// delegated forms protocol
        /// </summary>
        public bool Delegated { get; set; }

        /// <summary>
        /// Gets or sets the <seealso cref="IStringCipher"/> to use for the delegated result
        /// </summary>
        public IStringCipher Cipher { get; set; }

        /// <summary>
        /// Gets or sets a dictionary of extension objects
        /// </summary>
        public Dictionary<string, object> Extensions { get; private set; }
    }
}

This object is used to store the state of the conversation between HTTP requests and responses. The conversation object is re-created for each HTTP request or response, so custom conversation implementers should not attempt to add member variables to their custom conversation class to store data. Implementers should instead store their custom data in the conversation state Extensions dictionary if they require the data to persist between HTTP requests or responses.

A conversation object must implement the following interface:

    /// <summary>
    /// This interface represents a Forms conversation, or part of a conversation
    /// </summary>
    public interface IFormsConversation
    {
        /// <summary>
        /// Start the Form conversation
        /// </summary>
        /// <param name="absoluteBaseUrl">
        /// This is the absolute Url of the end-point to be used for
        /// the conversation.
        /// </param>
        /// <param name="absoluteCancelUrl">
        /// This is the absolute Url of the end-point to be used to
        /// cancel the conversation.
        /// </param>
        /// <param name="httpContext">The <seealso cref="HttpContextBase"/></param>
        /// <returns></returns>
        FormResult Start(string absoluteBaseUrl, string absoluteCancelUrl,
                         HttpContextBase httpContext);

        /// <summary>
        /// Cancel the current Form conversation
        /// </summary>
        /// <param name="httpContext">The <seealso cref="HttpContextBase"/></param>
        /// <returns></returns>
        FormResult Cancel(HttpContextBase httpContext);

        /// <summary>
        /// Get the next form in the conversation.
        /// </summary>
        /// <returns></returns>
        RenderedForm GetNextForm();

        /// <summary>
        /// Process the response to form
        /// </summary>
        /// <param name="httpContext">
        /// The <seealso cref="HttpContextBase"/> containing all the information
        /// regarding the form response.
        /// </param>
        /// <param name="FormData">The Form data</param>
        /// <returns></returns>
        FormResult ProcessFormResponse(HttpContextBase httpContext, NameValueCollection FormData);

        /// <summary>
        /// Gets the collection of credential types required for this conversation.
        /// </summary>
        IEnumerable<string> RequiredCredentialTypes { get; }

        /// <summary>
        /// Gets the host-relative endpoint for this conversation.
        /// </summary>
        string Endpoint { get; }

        /// <summary>
        /// Gets the host-relative endpoint to cancel this conversation.
        /// </summary>
        string CancelEndpoint { get; }
    }

    /// <summary>
    /// Enumeration of Form results
    /// </summary>
    public enum FormResult
    {
        /// <summary>
        /// More information is required. A new form will be issued to
        /// collect the required information
        /// </summary>
        moreInfo,

        /// <summary>
        /// The form failed
        /// </summary>
        fail,

        /// <summary>
        /// Authentication succeeded
        /// </summary>
        success,

        /// <summary>
        /// The user cancelled the form request
        /// </summary>
        canceled
    };

StoreFront Services contains a base class that implementers can inherit from:

Citrix.DeliveryServices.Authentication.FormsProtocol.Conversations.FormsConversationBase

This class implements the basic required functionality, but the implementer must implement the following methods:

        /// <summary>
        /// Cancel the current Form conversation
        /// </summary>
        /// <param name="httpContext">The <seealso cref="HttpContext"/></param>
        /// <returns></returns>
        public virtual FormResult Cancel(HttpContextBase httpContext)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Get the next form in the conversation.
        /// </summary>
        /// <returns></returns>
        public virtual RenderedForm GetNextForm()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Process the response to form
        /// </summary>
        /// <param name="httpContext">
        /// The <seealso cref="HttpContext"/> containing all the information
        /// regarding the form response.
        /// </param>
        /// <param name="FormData">The Form data</param>
        /// <returns></returns>
        public virtual FormResult ProcessFormResponse(HttpContextBase httpContext, 
                                                      NameValueCollection FormData)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Override this method to get the initial <seealso cref="IForm"/> in the conversation
        /// </summary>
        protected virtual IForm GetInitialForm()
        {
            throw new NotImplementedException();
        }

For conversations that require Active Directory credential validation, the conversation object must also implement the following interface:

namespace Citrix.DeliveryServices.Authentication.Forms.Engine.Interfaces
{
    /// <summary>
    /// This interface defines access to an <seealso cref="AuthenticationResult"/>
    /// </summary>
    public interface IAuthenticationResultHolder
    {
        /// <summary>
        /// Gets the result of the authentication conversation.
        /// </summary>
        AuthenticationResult AuthenticationResult { get; }
    }
}

Note: The FormsConversationBase class contains a default implementation of this interface.

Forms

Form objects within a conversation represent a high-level task, such as validating a user name and password. A form object is responsible for the generation of the specific Receiver Common Form XML for the task and the processing of the user agent’s response. A form object may repeatedly generate XML forms until its task is complete, for example to re-prompt for a password and show an error message if the previously submitted password was incorrect.

Forms must implement the following interface:

namespace Citrix.DeliveryServices.Authentication.Forms.Engine.Interfaces
{
    /// <summary>
    /// The expected next action for the form
    /// </summary>
    public enum FormState
    {
        /// <summary>
        /// Rendering the next form
        /// </summary>
        Rendering,

        /// <summary>
        /// Processing the response to the last form
        /// </summary>
        Processing,

        /// <summary>
        /// This form is complete
        /// </summary>
        Complete
    };

    /// <summary>
    /// This interface represents a single Form
    /// </summary>
    public interface IForm
    {
        /// <summary>
        /// Render the next form.
        /// </summary>
        /// <returns>The next <seealso cref="RenderedForm"/></returns>
        RenderedForm GetNextForm();

        /// <summary>
        /// Process the response to the last form
        /// </summary>
        /// <param name="httpContext">
        /// The <seealso cref="HttpContextBase"/> containing all the information
        /// regarding the form response.
        /// </param>
        /// <param name="formData">The form data.</param>
        /// <returns>The <seealso cref="FormResult"/> from processing the response</returns>
        FormResult ProcessFormResponse(HttpContextBase httpContext, NameValueCollection formData);

        /// <summary>
        /// Gets the collection of credential types required for this conversation.
        /// </summary>
        IEnumerable<string> RequiredCredentialTypes { get; }

        /// <summary>
        /// Gets the current state of the form
        /// </summary>
        FormState State { get; }
    }
}

StoreFront Services contains a base class that implementers can inherit from:

Citrix.DeliveryServices.Authentication.FormsProtocol.Forms.FormBase

This class implements the basic required functionality, but the implementer must implement the following methods:

        /// <summary>Render the next form.</summary>
        /// <returns>The next <seealso cref="RenderedForm"/></returns>
        public virtual RenderedForm GetNextForm()
        {
            throw new NotImplementedException();
        }

        /// <summary>Process the response to the last form</summary>
        /// <param name="httpContext">
        /// The <seealso cref="HttpContextBase"/> containing all the information
        /// regarding the form response.
        /// </param>
        /// <param name="formData">The form data</param>
        /// <returns>The <seealso cref="FormResult"/> from processing the response</returns>
        public virtual FormResult ProcessFormResponse(HttpContextBase httpContext,
                                                      NameValueCollection formData)
        {
            throw new NotImplementedException();
        }

The GetNextForm() method may use the forms template engine, described below, to generate the Receiver Common Form XML to be sent to the client. Then RenderedForm returned has the following structure:

namespace Citrix.DeliveryServices.Authentication.Forms.Engine.Interfaces
{
    /// <summary>
    /// This data class represents a rendered form
    /// </summary>
    public class RenderedForm
    {
        /// <summary>
        /// Gets or sets the rendered Form as an <seealso cref="XmlDocument"/>
        /// </summary>
        public XmlDocument Form { get; set; }

        /// <summary>
        /// Get or sets whether the associated form is the last form
        /// </summary>
        public bool Complete { get; set; }
    }
}

The Complete flag should be set to true if the form is not expecting the client to send a response back to the form being issued. This is usually required for terminal error messages, and will result in the conversation being terminated when the form has been sent to the client.

If the conversation returns FormResult.moreInfo to the Conversation Engine, then it will request the next form from the conversation object.

When a form has completed its processing, ProcessFormResponse should return FormResult.success. This signals to the conversation object that it should now use the next form in the sequence.

When all the forms have completed their processing, then the conversation is complete and the conversation returns FormResult.success to the conversation engine. This causes it to obtain the success result, which for non-delegated forms authentication is a Request Security Token Response message, which is returned to the client containing the access token.

Built-in Support

The StoreFront Authentication SDK has some built-in forms. It also provides access to the built-in support for verifying and managing Active Directory credentials.

Active Directory

The Authentication Service IoC container is populated with several objects that can be used to verify and manage Active Directory credentials, and are described below:

User Name with Password Support

The IoC container is populated with an object that implements the following interface:

namespace Citrix.DeliveryServices.Authentication.Explicit.Core
{
    /// <summary>
    /// A class to be used to create an instance that implements <seealso cref="IExplicit"/>.
    /// </summary>
    public class ExplicitFactory
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ExplicitFactory"/> class.
        /// </summary>
        /// <param name="explicitSettings">
        /// The <seealso cref="ExplicitSettings">explicit settings</seealso>
        /// </param>
        /// <param name="authenticationSettings">
        /// The general <seealso cref="AuthenticationSettings"/>
        /// </param>
        public ExplicitFactory(ExplicitSettings explicitSettings,
                               AuthenticationSettings authenticationSettings);

        /// <summary>
        /// Gets or sets the <seealso cref="ExplicitSettings"/>
        /// </summary>
        public ExplicitSettings Settings { get; private set; }

        /// <summary>
        /// Gets or sets the general <seealso cref="AuthenticationSettings"/>
        /// </summary>
        public AuthenticationSettings AuthenticationSettings { get; private set; }

        /// <summary>
        /// Create an instance that implements <seealso cref="IExplicit"/>
        /// </summary>
        /// <returns>An object that implements <seealso cref="IExplicit"/></returns>
        public IExplicit CreateInstance();
    }
}

The built-in ExplicitConversationFactory uses this to create an object that provides the user name with password support. One of these objects, implementing the interface below, is created for each ExplicitConversation object.

namespace Citrix.DeliveryServices.Authentication.Explicit.Core.Interfaces
{
    /// <summary>
    /// The interface to the explicit business logic
    /// </summary>
    public interface IExplicit
    {
        /// <summary>
        /// Gets the <seealso cref="ExplicitSettings"/>
        /// of this object.
        /// </summary>
        ExplicitSettings Settings { get; }

        /// <summary>
        /// Gets the general <seealso cref="AuthenticationSettings"/>
        /// of this object.
        /// </summary>
        AuthenticationSettings AuthenticationSettings { get; }

        /// <summary>
        /// Gets the full user identity of the account whose password has expired
        /// </summary>
        /// <remarks>
        /// Only returns a valid value when <see cref="Authenticate(string,string,string)"/>
        /// has been called within the current conversation and it has returned
        /// <see cref="AuthenticationStatus.FailedChangeExpiredSecret"/>
        /// </remarks>
        string ExpiredUser { get; }

        /// <summary>
        /// When a user calls an action that resets the state such as Authenticate we need to
        /// forget all previous cached information.
        /// </summary>
        void ClearState();

        /// <summary>
        /// Returns whether the given password is sufficiently complex
        /// </summary>
        /// <param name="password">The password to check</param>
        /// <returns>True, if meets complexity check</returns>
        bool PasswordMeetsComplexity(string password);

        /// <summary>
        /// Authenticate the specified credentials. If the password
        /// has expired and password changing is configured, the username and domain will
        /// be saved in the object state for use in a subsequent call to
        /// <see cref="ChangeExpiredPassword"/>
        /// </summary>
        /// <param name="userName">The username to verify</param>
        /// <param name="domain">
        /// The domain of the user, which can be null if the domain is specified in username
        /// </param>
        /// <param name="password">
        /// The password to verify, which can be an empty string
        /// </param>
        /// <returns>
        /// An <seealso cref="AuthenticationResult"/> indicating authentication status
        /// </returns>
        /// <exception cref="ExplicitAuthenticationException">if the credentials could not
        /// be authenticated</exception>
        AuthenticationResult Authenticate(string userName, string domain, string password);

        /// <summary>
        /// Perform a domain restriction check
        /// </summary>
        /// <param name="UserInfo">The <seealso cref="UserInfo"/> to check</param>
        /// <remarks>
        /// If the configuration contains a list of domains then perform a domain, or
        /// user principle name suffix, restriction check.
        /// </remarks>
        void PerformDomainRestriction(UserInfo UserInfo);

        /// <summary>
        /// Attempt to change the password for the user associated with the last
        /// call to <see cref="Authenticate(string,string,string)"/>.
        /// </summary>
        /// <param name="oldPassword">The existing password</param>
        /// <param name="newPassword">The new password</param>
        /// <returns>an <see cref="ChangePasswordResult" />
        /// indicating the password change status
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// If no user has been saved to the object state
        /// </exception>
        /// <exception cref="ExplicitAuthenticationException">
        /// If the password change operation failed
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// If oldPassword of newPassword is null.
        /// </exception>
        ChangePasswordResult ChangeExpiredPassword(string oldPassword, string newPassword);

        /// <summary>
        /// Attempt to change the password for the credentials indicated by the
        /// given principal.
        /// </summary>
        /// <param name="principal">
        /// The <seealso cref="IClaimsPrincipal"/> representing the user
        /// </param>
        /// <param name="oldPassword">The existing password</param>
        /// <param name="newPassword">The new password</param>
        /// <returns>
        /// an <seealso cref="ChangePasswordResult"/> indicating the password change status
        /// </returns>
        ChangePasswordResult ChangePassword(IClaimsPrincipal principal, string oldPassword,
                                            string newPassword);
    }
}

The ExplicitSettings object has the following definition:

namespace Citrix.DeliveryServices.Authentication.Explicit.Core.Configuration
{
    /// <summary>
    /// A data class to hold the Explicit business logic settings
    /// </summary>
    public class ExplicitSettings
    {
        /// <summary>
        /// Gets or sets service locator name for the authenticator to use
        /// </summary>
        public string AuthenticatorName { get; set; }

        /// <summary>
        /// Gets or sets the require account security identifiers property
        /// </summary>
        public bool RequireAccountSIDs { get; set; }

        /// <summary>
        /// Gets or sets the hide domain field property
        /// </summary>
        public bool HideDomainField { get; set; }

        /// <summary>
        /// Gets or sets the allow user to change their password property
        /// </summary>
        public AllowPasswordChangeValue AllowUserPasswordChange { get; set; }

        /// <summary>
        /// Gets or sets the password expiry warning mode
        /// </summary>
        public PasswordExpiryWarningValue ShowPasswordExpiryWarning { get; set; }

        /// <summary>
        /// Gets or sets the override for the password expiry warning period in days
        /// </summary>
        public int PasswordExpiryWarningPeriod { get; set; }

        /// <summary>
        /// Gets the list of trusted domains
        /// </summary>
        public IList<string> Domains { get; set; }

        /// <summary>
        /// Gets the default trusted domain
        /// </summary>
        /// <remarks>
        /// This will be null, if no trusted domains are specified,
        /// otherwise it must be a member of the trusted domains.
        /// </remarks>
        public string DefaultDomain { get; set; }

        /// <summary>
        /// Gets the allow zero length password property
        /// </summary>
        public bool AllowZeroLengthPassword { get; set; }
    }
}

The settings are built from the following configuration section of the Authentication Service and stored in the IoC container:

<configuration>
  <citrix.deliveryservices>
    <explicitBL authenticator="defaultDelegatedAuthenticator" hideDomainField="true"
                allowUserPasswordChange="Never" allowZeroLengthPassword="false" 
                showPasswordExpiryWarning="Windows" 
                passwordExpiryWarningPeriod="10" requireAccountSIDs="true">
      <domainSelection default="">
        <clear />
      </domainSelection>
    </explicitBL>
  </citrix.deliveryservices>
</configuration>

The explicitBL section has the following configuration items that control the expected user interface in addition to authentication policy:

  • authenticator: This value must not be changed
  • hideDomainField:
    • If this value is true, then forms should not display a separate user interface element for the user to supply the domain where their credential is stored. Instead they will be asked for their user name in the form of domain\user or user@domain.
    • If this value is false, then a domain user interface element will be displayed. If the <domainSelection> element is empty, then a text box for the user to type their domain should be displayed, otherwise a combo box containing all the values from the <domainSelection> element should be displayed.
  • allowUserPasswordChange: This value controls when a user is allowed to change their password and can have the following values:
    • Never: The user is not allowed to change their password through StoreFront. A user attempting to authenticate with an expired password will fail to gain access to the system, and will be directed to change their password elsewhere.
    • ExpiredOnly: The user is allowed to change an expired password during the authentication process.
    • Always: The user can change an expired password during authentication, but will also receive a warning when their password is about to expire, and will be able to choose to change their password at any time.
  • allowZeroLengthPassword: This is usually false and prevents users with zero length passwords from gaining access to the system or changing their password to a zero length.
  • showPasswordExpiryWarning: This setting controls when a password expiry warning notice is presented to the user, if allowUserChangePassword=Always, and this setting can have the following values:
  • passwordExpiryWarningPeriod: This setting, in days, determines when to display a password expiry warning notice to the user, if allowUserChangePassword=Always and showPasswordExpiryWarning=Custom
  • requireAccountSIDs: This setting determines whether or not to include the Windows Group information with the authentication data. This value should be left as true, as other StoreFront components rely on the information being present.
  • domainSelection: This element allows the specification of a set of domains. These can be specified as either short domain or fully qualified domain names. The user is then restricted to either selecting or typing a valid domain. It is possible to specify a default.
    • Note: The comparison is a string comparison only.

Kerberos Support

StoreFront also allows the use of Kerberos Service-For-User (S4U) for partial account validation and to obtain information about the account, including group information. The IoC container is populated with an object that implements the following interface that can be used for this purpose:

namespace Citrix.DeliveryServices.Authentication.Kerberos
{
    /// <summary>
    /// Interface for attempting kerberos authentication
    /// </summary>
    public interface IKerberosAuthenticator
    {
        /// <summary>
        /// Authenticate the user specified by their user principal name
        /// </summary>
        /// <param name="userPrincipalName">The user principal name</param>
        /// <param name="clientRealm">The optional client realm </param>
        /// <returns>The <seealso cref="AuthenticationResult"/></returns>
        AuthenticationResult Authenticate(string userPrincipalName, string clientRealm = null);

        /// <summary>
        /// Authenticate the user specified by a user certificate
        /// </summary>
        /// <param name="userCertificate">The user certificate</param>
        /// <param name="domainHint">The optional domain hint </param>
        /// <returns>The <seealso cref="AuthenticationResult"/></returns>
        AuthenticationResult Authenticate(X509Certificate2 userCertificate,
                                          string domainHint = null);

        /// <summary>
        /// Authenticate the user specified by a user certificate
        /// </summary>
        /// <param name="userCertificate">The user certificate</param>
        /// <param name="domainHint">The optional domain hint </param>
        /// <returns>The <seealso cref="AuthenticationResult"/></returns>
        AuthenticationResult Authenticate(HttpClientCertificate userCertificate,
                                          string domainHint = null);
    }
}

Forms

The Authentication SDK provides the following built-in forms:

CancelledForm

This class creates the required form in response to a cancel request.

Constructor:

Citrix.DeliveryServices.Authentication.FormsProtocol.Forms.CancelledForm(
                  FormsTemplateEngine templateEngine, IEnumerable<CultureInfo> cultures)

Parameters:

  • templateEngine: The template engine. This is a required parameter.
  • cultures: The currently available cultures. This is a required parameter.

Form:

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
  <Status>success</Status>
  <Result>cancelled</Result>
  <StateContext />
</AuthenticateResponse>

Template:

Cancelled.tfrm

FailureMessageForm

This class creates a mini-conversation of a message form, to inform the user of the reason for the error, followed by a failure form.

Constructor:

Citrix.DeliveryServices.Authentication.FormsProtocol.Forms.FailureMessageForm(
              FormsTemplateEngine templateEngine, IEnumerable<CultureInfo> cultures,
               IEnumerable<FormsMessage> messages, string postbackUrl, string cancelUrl)

Parameters:

  • templateEngine: The template engine.

  • cultures: The currently available cultures.

  • messages: The messages to display.

  • postbackUrl: The host relative URL to POST the form response to.

  • cancelUrl: The host relative URL to POST a cancel form response to.

Template: Messages.tfrm & Failed.tfrm

ChangeExpiredPasswordForm

This class creates a mini-conversation to change a user’s expired password.

Constructor:

Citrix.DeliveryServices.Authentication.FormsProtocol.Forms.ChangePassword.ChangeExpiredPasswordForm
(
    FormsTemplateEngine templateEngine, IEnumerable<CultureInfo> cultures, IExplicit explicitBL,
    string postbackUrl, string cancelUrl
)

Parameters:

  • templateEngine: The template engine.
  • cultures: The currently available cultures.
  • explicitBL: The explicit credential business object, described above.
  • postbackUrl: The host relative URL to POST the form response to.
  • cancelUrl: The host relative URL to POST a cancel form response to.

Template: ChangeExpiredPassword.tfrm and ConfirmChangePassword.tfrm

Notes: - The explicitBL object must have previously been used in the conversation to attempt to authenticate the user and discover that their password has expired. If this is not done, then an error form will be generated. - The settings for explicitBL object must have the AllowUserPasswordChange value set to either ExpiredOnly or Always. If the value is Never, then an error form will be issued.

UsernamePasswordForm

This class creates a mini-conversation to allow a user to authenticate by explicitly providing their user name and password in the form. If configured it will also allow the user to change their expired password as part of this form.

Constructor:

Citrix.DeliveryServices.Authentication.FormsProtocol.Forms.UsernamePasswordForm
(
    FormsTemplateEngine templateEngine, IEnumerable<CultureInfo> cultures, IExplicit explicitBL,
    string postbackUrl, string cancelUrl, bool allowChangeExpiredPasswordIfConfigured=true
)

Parameters: - templateEngine: The template engine. - cultures: The currently available cultures. - explicitBL: The explicit credential business object, described above. - postbackUrl: The host relative URL to POST the form response to. - cancelUrl: The host relative URL to POST a cancel form response to. - allowChangeExpiredPasswordIfConfigured: This optional parameter defines whether or not the the user is allowed to change their expired password as part of this form. - true: This is the default value, and allows the user to change their expired password, if the explicitBL object has been configured to allow it. - false:The user is never allowed to change their password as part of this form.

Template: UsernamePassword.tfrm and optionally ChangeExpiredPassword.tfrm and ConfirmChangePassword.tfrm

ChangePasswordController

The ChangePasswordController is a built-in ASP.Net MVC Controller for handling requests for a user to choose to change their password before it has expired. This Controller is deployed by default and an MVC route to it is defined in the Authentication Service web.config as shown below:

<commonExplicitEndpoints>
  <routeTable order="10">
    <routes>
      <route name="StartChangePassword" url="ChangePassword/Start">
        <defaults>
          <add param="controller" value="ChangePassword" />
          <add param="action" value="StartChangePassword" />
          <add param="postbackAction" value="ChangePassword" />
          <add param="cancelAction" value="CancelChangePassword" />
          <add param="conversationFactory" value="ChangePassword" />
          <add param="changePasswordAction" value="StartChangePassword" />
          <add param="changePasswordController" value="ChangePassword" />
          <add param="claimsFactoryName" value="standardClaimsFactory" />
        </defaults>
        <data />
      </route>
      <route name="ChangePassword" url="ChangePassword">
        <defaults>
          <add param="controller" value="ChangePassword" />
          <add param="action" value="ChangePassword" />
        </defaults>
        <data />
      </route>
      <route name="CancelChangePassword" url="ChangePassword/Cancel">
        <defaults>
          <add param="controller" value="ChangePassword" />
          <add param="action" value="CancelChangePassword" />
        </defaults>
        <data />
      </route>
    </routes>
  </routeTable>
</commonExplicitEndpoints>

In order for the client to discover this route, the CredentialUpdateService URL information must be present in the Request Security Token Response, as illustrated below:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<requesttokenresponse xmlns="http://citrix.com/delivery-services/1-0/auth/requesttokenresponse"
                      xmlns:ar="http://citrix.com/authentication/response/1">
  <for-service>a service identifier</for-service>
  <issued>2009-05-12T15:27:28.1550000Z</issued>
  <expiry>2009-05-12T16:29:31.1550000Z</expiry>
  <lifetime>0.01:02:03</lifetime>
  <token-template>abcdef</token-template>
  <token>ABCDEF123===</token>
  <ar:CredentialUpdateInformationList>
    <ar:CredentialUpdateInformation>
      <ar:CredentialDisplayName>DomainName</ar:CredentialDisplayName>
      <ar:AuthenticationInfoId>1</ar:AuthenticationInfoId>
      <ar:ExpiryTime>2010-05-12T15:27:28.155Z</ar:ExpiryTime>
      <ar:TimeRemaining>10.05:33:09</ar:TimeRemaining>
      <ar:CredentialUpdateService>ChangePassword/Start</ar:CredentialUpdateService>
    </ar:CredentialUpdateInformation>
  </ar:CredentialUpdateInformationList>
</requesttokenresponse>

A custom conversation can use the built-in functionality by setting the change password controller name and action name in the route data of the route associated with the conversation, as shown below:

<route name="StartExplicitAuthentication" url="ExplicitForms/Start">
  <defaults>
    <add param="controller" value="ExplicitFormsAuthentication" />
    <add param="action" value="AuthenticateStart" />
    <add param="postbackAction" value="Authenticate" />
    <add param="cancelAction" value="CancelAuthenticate" />
    <add param="conversationFactory" value="ExplicitAuthentication" />
    <add param="changePasswordAction" value="StartChangePassword" />
    <add param="changePasswordController" value="ChangePassword" />
    <add param="protocol" value="ExplicitForms" />
    <add param="claimsFactoryName" value="standardClaimsFactory" />
  </defaults>
  <data />
</route>

This data is resolved by the built-in code to find the change password start URL . The built-in code also ensures that the correct RSTR is returned so the client can discover the change password end-point.

Templates: ChangeExpiredPassword.tfrm and ConfirmChangePassword.tfrm

Notes: - The user must be authenticated in order to change their password. The authenticated user must have authenticated using a password, otherwise an error form is issued. The password originally supplied during authentication is verified against a value supplied by the user in the form, and changing the password is not allowed unless the passwords match. - The settings for explicitBL object must have the AllowUserPasswordChange value set to Always. If the value is Never or ExpiredOnly, then an error form will be issued. 

Forms Template Engine

Introduction

The forms template engine is the StoreFront component responsible for generating the Receiver Common Forms from templates. The templates are based on the ASP.NET Razor View Engine (see http://www.asp.net/web-pages/tutorials/basics/2-introduction-to-asp-net-web-programming-using-the-razor-syntax) with methods specific to the Receiver Common Forms domain that comprises the Forms Template Language described below.

The StoreFront Authentication SDK samples contain examples of how to define new templates, use the API, generate localized textual content, and use the Forms Template Language. Further details of the APIs can also be found in the XML documentation file at:

AuthSDK.zip\Assemblies\StoreFront\Citrix.DeliveryServices.Authentication.Forms.Template.XML

If there are issues with the template processing, the Forms Template Engine adds events to the Event log as described in Appendix A.

Creation

Template Engine Creation

For a particular web application, there is one and only one instance of the Forms Template Engine. It is stored in the StoreFront IoC container by the Forms Protocol start-up module during the application start-up and is accessed through the service locator.

First a Razor engine host is created and configured to generate C# code. The base class is configured to be:

Citrix.DeliveryServices.Authentication.Forms.Template.FormsTemplateBase

This base class contains the domain-specific business rules relating to the Citrix Receiver Common Forms.

The Razor host imports the following namespaces:

  • System
  • Citrix.DeliveryServices.Authentication.Forms.Template.Requirements

Notes: A limitation of this version is that it is not possible to add other namespaces or references to custom assemblies.

The Forms Template Engine is then constructed from a Razor template engine, wrapping the razor host and the Citrix localization factory, before loading and compiling the templates.

The templates are files with the file extension tfrm that are stored in the directory ~/App_Data/Templates. The template engine also finds and loads images with the following file extensions: gif, jpg, jpeg, png, bmp. The Forms Template Engine also watches for changes to the templates directory and automatically reloads the templates when a change is made. When the templates are reloaded, an event is added to the Citrix Delivery Services Event log, as described in Appendix A, with Event Id: 5.

The Forms Template Engine is finally stored in the IoC.

Template Compilation

The compilation of a template, named CustomTemplate.tfrm for example, has the following steps:

  1. The Razor engine takes the template contents and generates code that represents the class Citrix.CompiledForms.CustomTemplate as C# code with FormsTemplateBase as its base class.
  2. The C# is then compiled into a code DOM using the CSharpCodeProvider.
  3. The code DOM is then compiled into an in-memory assembly that is loaded into the process.
  4. The loaded assembly is then stored in the Forms Template Engine, indexed as: CustomTemplate.

Notes: The Razor template processing requires that the process has write access to the %TEMP% directory

Template Rendering

Images

The Receiver Common Authentication Forms Language allows the use of images as part of the authentication process, for example Captcha images. In addition to the templates, the Forms Template Engine will also search the template folder for files with extensions: gif, jpg, jpeg, png, and bmp. It will load all the files found and convert them to appropriate RFC 2397 data URIs, ready for use in the required templates. Again, adding, removing, or modifying an image will result in the images being reloaded automatically.

Images can also be dynamically generated, with support built-in for converting raw image data to data URIs for use in forms.

Localization

The forms Template Engine supports the generation of localized textual content from a single template. In order for localized content to be available to the Forms Template Engine, the forms author should create localized content as standard Microsoft resx files (see http://msdn.microsoft.com/en-us/library/ekyft91f(v=vs.90).aspx). These resource files should be placed in the directory:

~/App_Data/resources.

StoreFront Services needs to be configured to become aware of new localization files, as described below.

APIs that accept localization keys can also accept either strings that do not require localization, or pre-localized strings. The processing of a localization key has the following steps.

A key is expected to be an indexed resource key of form: ResourceSetPrefix:Key. The ResourceSetPrefix is configured in the Authentication Service web.config, and maps a prefix to a set of resources. For example, the default prefixes are defined as:

<configuration>  
  <citrix.deliveryservices>
    <localisation>
      <resourceSets>
        <clear />
        <add prefix="FormsProtocol" resourcePath="App_Data/resources/FormsProtocol" />
        <add prefix="ExplicitCore" resourcePath="App_Data/resources/ExplicitCore" />
        <add prefix="ExplicitFormsCommon" resourcePath="App_Data/resources/ExplicitFormsCommon" />
        <add prefix="ExplicitAuth" resourcePath="App_Data/resources/ExplicitAuth" />
      </resourceSets>
    </localisation>
  </citrix.deliveryservices>
</configuration>

So, for example, the resource named: “test” defined in the FormsProtocol.resx file would be accessed by the localization key:

FormsProtocol:test

Customizations are expected to update the above configuration with their resource sets.

If a localization key is supplied that does not correspond to this pattern, or that specifies a resource set prefix not mapped in the above configuration, then the supplied key is just returned as the localized string. Examples include:

  • “String not to be localized” would be localized as: “String not to be localized”
  • “Pre-localized” would be localized as: “Pre-localized”
  • “UnknownReseourceSetPrefix:Key” would be localized as: “UnknownReseourceSetPrefix:Key”

If a valid resource set prefix is found, then the associated resource provider is used to locate the appropriate localized string into the requested language. If the localization key exists, but the request is to localize to a language not provided, then the string will be localized into the default language, which is English.

Template Rendering

The Forms Template Engine is used by Forms objects to generate the Forms through the following methods on the Forms Template Engine:

        /// <summary>
        /// Render the specified template using the specified anonymous object
        /// as the model.
        /// </summary>
        /// <param name="templateName">The name of the template to render</param>
        /// <param name="currentCultures">The current <seealso cref="CultureInfo"/> to use to 
                                          localize text items</param>
        /// <param name="model">The anonymous model</param>
        /// <param name="messages">The <seealso cref="FormsMessage"/> to render</param>
        /// <returns>The rendered Xml</returns>
        public XmlDocument RenderTemplate(string templateName,
                                          IEnumerable<CultureInfo> currentCultures,
                                          object model,
                                          IEnumerable<FormsMessage> messages = null);

        /// <summary>
        /// Render the specified template using the specified dictionary of named objects
        /// as the values.
        /// </summary>
        /// <param name="templateName">The name of the template to render</param>
        /// <param name="currentCultures">The current <seealso cref="CultureInfo"/> to use to 
                                          localize text items</param>
        /// <param name="renderValues">The dictionary of named objects to use as values</param>
        /// <param name="messages">The <seealso cref="FormsMessage"/> to render</param>
        /// <returns>The rendered Xml</returns>
        public XmlDocument RenderTemplate(string templateName,
                                          IEnumerable<CultureInfo> currentCultures,
                                          IDictionary<string, object> renderValues,
                                          IEnumerable<FormsMessage> messages=null);

In the above, the template name is the index of the compiled template, as described above; for example, the name to be used for test.tfrm is: test. The data model, whether an anonymous data class or a dictionary, allows the form to define named data items referenced in the associated template. Examples of data models can be seen in the Forms Template Language section below.

The FormsMessage object has the following API, and is used for associating messages with credentials:

namespace Citrix.DeliveryServices.Authentication.Forms.Template.RequirementAPI
{
    /// <summary>
    /// This class represents a message to be displayed on a form.
    /// The message is one of the following categories:
    ///     information, warning or error.
    /// </summary>
    public class FormsMessage
    {
        /// <summary>
        /// Gets or sets the localization key
        /// </summary>
        public string LocalisationKey { get; set; }

        /// <summary>
        /// Gets or sets the localized message
        /// </summary>
        internal string Message { get; set; }

        /// <summary>
        /// Gets or sets the collection of filed identifiers that this
        /// message relates to.
        /// </summary>
        public IEnumerable<string> FieldIds { get; set; }

        /// <summary>
        /// Gets or sets the form message type
        /// This must be: information, warning or error
        /// </summary>
        public Label.LabelType MessageType { get; set; }

        /// <summary>
        /// Render this message into the specified buffer
        /// </summary>
        /// <param name="sb">
        /// The <seealso cref="StringBuilder"/> to render this message into
        /// </param>
        /// <returns>The string of the the rendered message</returns>
        public void RenderMessageToBuffer(StringBuilder sb);
    }
}

For example, consider a change password form with a requirement for the user to re-enter their existing password:

<Requirement>
  <Credential>
    <ID>oldPassword</ID>
    <Type>password</Type>
  </Credential>
  <Label>
    <Text>Old password:</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <Text>
      <Secret>true</Secret>
      <ReadOnly>false</ReadOnly>
      <InitialValue></InitialValue>
      <Constraint>.+</Constraint>
    </Text>
  </Input>
</Requirement>

If the user enters an invalid value, then an error label is returned that specifies the oldPassword requirement to be highlighted. This would be done by creating a FormsMessage as follows:

new FormsMessage
    {
        MessageType = Label.LabelType.error,
        LocalisationKey = "ExplicitCore:OldInvalid",
        FieldIds = new[] { "oldPassword" }
    }
This would result in the following XML:
<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <HighlightFields>
      <CredentialID>oldPassword</CredentialID>
    </HighlightFields>
    <Text>Old password incorrect</Text>
    <Type>error</Type>
  </Label>
  <Input />
</Requirement>

The generated form XML is returned to the client by the conversation engine and the MVC Controller in an appropriately formatted message.

The Authentication SDK samples contain detailed examples of how to use the Template Rendering API.

Forms Template Language

The FormsTemplateBase C# class has a collection of methods that can be accessed in the template. These form the domain- specific forms template language. The language is divided into three APIs: low-level, requirements and high-level, which allow different levels of control of the final Receiver Common Forms XML. Appendix B illustrates a single form generated using the three APIs, so that they can be compared and contrasted.

The following description expects a basic understanding of the layout of the Receiver Common Forms XML as described in [2]. The APIs also expect an understanding of C# optional and named Parameters, as described here: http://msdn.microsoft.com/en-gb/library/dd264739.aspx

In addition to these domain-specific methods, many general Razor programming constructs may be used.

Low Level API

This API is the lowest level API and is based around the generation of strings for XML documents.

For the low-level methods, all the Parameters are required.

ContainsValue

bool ContainsValue(string valueName)

This method returns true if the specified named index is associated with an object in the data model.

Parameters:

  • valueName: The name of an object in the data model.

Returns: True if the specified named index is valid

Example:

Model: new { AssistiveText = "Help Text"};

Template:

<Input>
@if (ContainsValue("AssistiveText"))
{
@: <AssistiveText>@GetValue("AssistiveText")</AssistiveText >
}
  <Text>
    <Constraint>.+</Constraint>
  </Text>
</Input>

Output:

<Input>
  <AssistiveText>Help Text</AssistiveText>
  <Text>
    <Constraint>.+</Constraint>
  </Text>
</Input>

GetObject

object GetObject(string valueName)

This method returns the object in the data model with the specified named index.

Parameters::

  • valueName: The name of an object in the data model.

Returns: The specified object from the data model, or null if the index is not valid.

Example:

Model:

new
{
    PhoneNumbers = new []
                   {
                       new Tuple<string, string> ("Home Number", "01954 283 710"),
                       new Tuple<string, string> ("Fax Number", "01954 283 601")
                   }
}

Template:

@foreach(Tuple<string, string> phoneNumber in (IEnumerable)GetObject("PhoneNumbers"))
{
@:      <Requirement>
@Credential(credentialType: "none")
@TextLabel(labelKey: @phoneNumber.Item1)
@:        <Input>
@:          <Text>
@:            <InitialValue>@phoneNumber.Item2</InitialValue>
@:            <Constraint>.+</Constraint>
@:          </Text>
@:        </Input>
@:      </Requirement>
}

Output:

<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <Text>Home Number</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <Text>
      <InitialValue>01954 283 710</InitialValue>
      <Constraint>.+</Constraint>
    </Text>
  </Input>
</Requirement>
<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <Text>Fax Number</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <Text>
      <InitialValue>01954 283 601</InitialValue>
      <Constraint>.+</Constraint>
    </Text>
  </Input>
</Requirement>

GetValue

string GetValue(string valueName)

This method returns the object in the data model with the specified named index, as a string. This method will convert the specified object to a string, if required.

Parameters:

  • valueName: The name of an object in the data model.

Returns: The specified string from the data model, or the empty string if the index is not valid

Example:

Model: new { Date = DateTime.Now() };

Template: @GetValue("Date")

Output: 5/6/2005 02:34:42 PM

ControlValue

bool ControlValue(string valueName)

This method returns the object in the data model with the specified named index, as a Boolean value. This method expects that the stored object is a Boolean and will not attempt to convert from other types.

Parameters:

  • valueName: The name of an object in the data model.

Returns: The specified Boolean value from the data model, or false if the index is not valid

Example:

Model: new { Display = true };

Template:

@if (ControlValue("Display"))
{
@: <AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
}

Output:

<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">

GetAttributeValue

string GetAttributeValue(string valueName)

This method returns the object in the data model with the specified named index, as a string encoded for using as an XML attribute. This method will convert the specified object to a string, if required.

Parameters::

  • valueName: The name of an object in the data model.

Returns: The specified string from the data model encoded to be used as an XML attribute, or the empty string if the index is not valid

Example:

Model: new { Namespace = "http://citrix.com/authentication/response/1"};

Template:

<AuthenticateResponse xmlns="@GetAttributeValue("Namespace")">

Output:

<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">

GetElementValue

string GetElementValue(string valueName)

This method returns the object in the data model with the specified named index, as a string encoded for using as an XML element. This method will convert the specified object to a string, if required.

Parameters: - valueName: The name of an object in the data model.

Returns: The specified string from the data model encoded to be used as an XML element, or the empty string if the index is not valid

Example:

Model: new { "StateContextElementName", "StateContext"};

Template:

<@GetElementValue("StateContextElementName")>

Output:

<StateContext>

GetTextValue

string GetTextValue(string valueName)

This method returns the object in the data model with the specified named index, as a string encoded for using as XML text. This method will convert the specified object to a string, if required.

Parameters:

  • valueName: The name of an object in the data model.

Returns:

The specified string from the data model encoded to be used as XML text, or the empty string if the index is not valid

Example:

Model: new { TestText = "This is non-localized & unencoded text"};

Template:

@GetTextValue("TestText")

Output:

This is non-localized & unencoded text.

GetLocalisedTextValue

string GetLocalisedTextValue(string valueName)

This method will use the localization key to provide a localized string encoded as XML text.

Parameters:

  • valueName: The name of an object in the data model containing a localization key

Returns: The localized string, encoded as XML text, or the empty string if the index is not valid

Example:

Template: @GetLocalisedTextValue("Test:TestKey")

Output:

This is a localized test string

GetLocalisedFormattedValue

string GetLocalisedFormattedValue(string formatName, params object[] Parameters)

This method will use the localization key to obtain a format string that is used with the Parameters to generate a localized string, encoded as XML text.

Parameters:

  • valueName: The name of an object in the data model containing a localization key

Returns:

The localized string, encoded as XML text, or the empty string if the index is not valid

Example:

Template: @GetLocalisedTextValue("Test:FormatKey", DateTime.Now)

Output:

Called at: 5/6/2005 02:34:42 PM

Requirements

A Receiver Common Form contains a list of authentication requirements. Each authentication requirement contains the following elements: <Credential>, <Label>, and <Input>. This API is focused around the generation of these three elements. In cases where there are many arguments, defaults are provided where possible, and it is expected that Parameters are specified by name for ease of use, as demonstrated in the examples.

Credential

string Credential(string credentialType, string id = null, string saveId = null)

Create a <Credential> authentication requirement fragment with the specified values.

Parameters:

  • credentialType: The credential type (see the Receiver Common Authentication Forms Language document for more information). This parameter is required.
  • id: The optional credential identifier. The default is to have no identifier.
  • saveId: The optional identifier to be used to save this credential on a client. The default is to have no save identifier.

Returns:

The <Credential> xml fragment

Example:

Template:

@Credential(credentialType: "username", id: "UsernameID", saveId: "SaveUsernameID")

Output:

<Credential>
  <ID>UsernameID</ID>
  <SaveID>SaveUsernameID</SaveID>
  <Type>username</Type>
</Credential>

TextLabel

string TextLabel(string labelKey, Label.LabelType labelType = Label.LabelType.plain)

Create a text-based <Label> authentication requirement fragment from the specified values.

Parameters:

  • labelKey: The localization key for the label. This parameter is required.
  • labelType: The optional label type (see the Receiver Common Authentication Forms Language document for more information). The default label type is: plain

Returns: The text-based <Label> xml fragment

Example:

Template: @TextLabel(labelKey: "Test:UsernameLabelKey")

Output:

<Label>
  <Text>User name:</Text>
  <Type>plain</Type>
</Label>

ImageLabel

string ImageLabel(string imageKey)

Create an image-based <Label> authentication requirement fragment from an image stored in the Template folder.

Parameters:

  • imageKey: The name of the image file in the Template directory. This is a required parameter.

Returns: The image-based <Label> xml fragment

Example:

Template:

@ImageLabel(imageKey: "captcha.png")

Output:

<Label>
  <Binary>data:image/png;base64,iVBORw0KGgoAAAANSUh…/sAAAAASUVORK5CYII=</Binary>
  <Type>image</Type>
</Label>

Note: It is possible to dynamically create images and display them, for example:

Form:

byte[] dynamicImage; 
var converted = FormsTemplateEngine.GetImageDataUri(dynamicImage, @"image/gif");      
TemplateEngine.RenderTemplate(FormName, Cultures, Values,
            new { DynamicImage = converted});

Template:

<Label>
  <Binary>@GetTextValue("DynamicImage")</Binary>
  <Type>image</Type>
</Label>

Output:

<Label>
  <Binary>data:image/png;base64,iVBORw0KGgoAAAANSUh…/sAAAAASUVORK5CYII=</Binary>
  <Type>image</Type>
</Label>

InputButton

string InputButton(string buttonKey, string assistiveTextKey=null)

Create a button <Input> authentication requirement fragment.

Parameters:

  • buttonKey: The localization key for the button label. This is a required parameter.

  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.

Returns: The button <Label> xml fragment

Example:

Template:

@InputButton(buttonKey: "Test:LogonButtonKey")

Output:

<Input>
  <Button>Log On</Button>
</Input>

InputComboBox

string InputComboBox(string displayItemsValueName, string initialSelectionValueName = null,
                     string assistiveTextKey = null)

Create a combo-box <Input> authentication requirement fragment.

Parameters:

  • displayItemsValueName: The name of the dictionary of display-value pairs in the model. This is a required parameter.
  • initialSelectionValueName: The optional initially selected display value. The default is to select the first value.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.

Returns:

The combo-box <Input> xml fragment

Example:

Model:

new
{
    Domains = new Dictionary<string, string>
              {
                  { @"citrix", @"citrix.com"},
                  { @"citrite", @"citrite.local"}
              },
    InitialDomain = @"citrite.local"
}

Template:

@InputComboBox(displayItemsValueName: "Domains",
                       initialSelectionValueName: "InitialDomain",
                       assistiveTextKey: "DomainAssistiveTextKey")

Output:

<Input>
  <AssistiveText>Please select your domain</AssistiveText>
  <ComboBox>
    <InitialSelection>citrite.local</InitialSelection>
    <DisplayValues>
      <DisplayValue>
        <Display>citrix</Display>
        <Value>citrix.com</Value>
      </DisplayValue>
      <DisplayValue>
        <Display>citrite</Display>
        <Value>citrite.local</Value>
      </DisplayValue>
    </DisplayValues>
  </ComboBox>
</Input>

Note:

The display values for combo box, radio button, and multi-combo box, can be localized, for example, the following model is taken from the Test Forms sample:

new
{
    multiComboValues = new Dictionary<string, string>
            {
                {@"TestForms:ComboDisplayTextOne", @"Value1"},
                {@"TestForms:ComboDisplayTextTwo", @"Value2"},
                {@"TestForms:ComboDisplayTextThree", @"Value3"}
            }
}

InputRadioButton

string InputRadioButton(string displayItemsValueName, string initialSelectionValueName = null,
                        string assistiveTextKey = null)

Create a combo-box <Input> authentication requirement fragment.

Parameters:

  • displayItemsValueName: The name of the dictionary of display-value pairs in the model. This is a required parameter.
  • initialSelectionValueName: The optional initially selected display value. The default is is to select the first value.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.

Returns:

The radio button <Input> xml fragment

Example:

Model:

new
{
    Domains = new Dictionary<string, string>
              {
                  { @"citrix", @"citrix.com"},
                  { @"citrite", @"citrite.local"}
              },
    InitialDomain = @"citrite.local"
}

Template:

@InputRadioButton(displayItemsValueName: "Domains",
                          initialSelectionValueName: "InitialDomain",
                          assistiveTextKey: "DomainAssistiveTextKey")

Output:

<Input>
  <AssistiveText>Please select your domain</AssistiveText>
  <RadioButton>
    <InitialSelection>citrite.local</InitialSelection>
    <DisplayValues>
      <DisplayValue>
        <Display>animaniacs</Display>
        <Value>animaniacs.net</Value>
      </DisplayValue>
      <DisplayValue>
        <Display>citrite</Display>
        <Value>citrite.local</Value>
      </DisplayValue>
    </DisplayValues>
  </RadioButton>
</Input>

InputMultiComboBox

string InputMultiComboBox(string displayItemsValueName, string assistiveTextKey = null)

Create a multi-select combo-box <Input> authentication requirement fragment.

Parameters:

  • displayItemsValueName: The name of the dictionary of display, value and selection flag tuples in the model. This is a required parameter.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.

Returns:

The multi-selection combo-box <Input> xml fragment

Example:

Model:

new
{
    Users = new Dictionary<string, Tuple<string, bool?>>
                {
                    { @"Alice", new Tuple<string, bool?>("Alice", null)},
                    { @"Bob",  new Tuple<string, bool?>("Bob", false)},
                    { @"Eve",  new Tuple<string, bool?>("Eve", true)}
                }
}

Template:

@InputMultiComboBox(displayItemsValueName: "Users",
                            assistiveTextKey: "MultiComboBoxAssistiveTextKey")

Output:

<Input>
  <AssistiveText>Please select who is a hacker</AssistiveText>
  <MultiComboBox>
    <DisplayValues>
      <DisplayValue>
        <Display>Alice</Display>
        <Value>Alice</Value>
      </DisplayValue>
      <DisplayValue>
        <Display>Bob</Display>
        <Value>Bob</Value>
        <Select>false</Select>
      </DisplayValue>
      <DisplayValue>
        <Display>Eve</Display>
        <Value>Eve</Value>
        <Select>true</Select>
      </DisplayValue>
    </DisplayValues>
  </MultiComboBox>
</Input>

InputCheckBox

string InputCheckBox(bool? initiallyChecked=null, string assistiveTextKey = null)

Create a check box <Input> authentication requirement fragment.

Parameters:

  • initiallyChecked: The optional value to set whether the check box is initially checked. If the value is null, which is the default, then the optional < InitialValue > element is not included.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.

Returns:

The check box <Input> xml fragment

Example:

Template:

@InputCheckBox(initiallyChecked: true,
                       assistiveTextKey: "CheckBoxAssistiveTextKey")

Output:

<Input>
  <AssistiveText>Check box assistive text</AssistiveText>
  <CheckBox>
    <InitialValue>true</InitialValue>
  </ CheckBox >
</Input>

InputTextBox

string InputTextBox(string assistiveTextKey = null, string initialValueName = null,
                    string constraint = TextBox.DefaultConstraint, bool? secret = null,
                    bool? readOnly = null)

Create a text-based <Input> authentication requirement fragment.

Parameters:

  • initialValueName: The optional name of the model value that contains the initial value of the text box. Note that this is a raw string not a key to be localized. The default is not to have an initial value.
  • constraint: The optional, and unused, regular expression for the text to be entered.
  • secret: If true then the text typed by the user should be obscured. This parameter is optional. If the value is null, which is the default, then the optional <Secret> element is not included.
  • readOnly: This optional parameter makes the initial text value read-only. If the value is null, which is the default, then the optional <ReadOnly> element is not included.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.

Returns:

The text <Input> xml fragment

Example:

Template: @InputTextBox(secret: true,
                      assistiveTextKey: "PasswordAssistiveTextKey")

Output:

<Input>
  <AssistiveText>Please enter your password</AssistiveText>
  <Text>
    <Secret>true</Secret>
    <Constraint>.+</Constraint>
  </Text>
</Input>

High Level API

The high level API is also focused on the authentication requirements, but creates a complete requirement, including the <Credential>, <Label>, and <Input> values. These methods provide a higher-level abstraction and ensure that the authentication requirements produced are internally consistent. For this reason, it is recommended that these methods should be used where possible.

In cases where there are many arguments, defaults are provided where possible, and it is expected that Parameters are specified by name for ease of use.

ControlHeaders

string ControlHeaders(string status, string result, string stateContext = null,
                      string logMessageKey = null)

Create the control header fragment.

Parameters: - status: The authentication status (see the Receiver Common Authentication Forms Language document). This parameter is required. - result: The authentication result (see the Receiver Common Authentication Forms Language document). This parameter is required. - stateContext: The optional state context. The default is an empty state context. - logMessage: This optional parameter is a key to a localized message to be logged by the client. The default is to not include the optional <LogMessage> element.

Returns: The control xml fragment

Example:

Template: @ControlHeaders(status: StatusSuccess, result: ResultMoreInfo, stateContext: "0x12345678")

Output:

<Status>success</Status>
<Result>more-info</Result>
<StateContext>0x12345678</StateContext>

Textbox

string Textbox(string type, string id = null, string saveId = null, string assistiveTextKey = null,
               string labelKey = null, Label.LabelType labelType = Label.LabelType.plain,
               string initialValueName = null, string constraint = TextBox.DefaultConstraint,
               bool? secret = null, bool? readOnly = null)

Create a general text box authentication requirement fragment.

Parameters:

  • type: The type of the associated credential (see the Receiver Common Authentication Forms Language document). This parameter is required.
  • id: The optional credential identifier. If not specified, then the option <ID> element is not included.
  • saveId: The optional identifier to be used to save this credential on a client. The default is to have no save identifier.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.
  • labelKey: The localization key for the label. This parameter is required.
  • labelType: The optional label type (see the Receiver Common Authentication Forms Language document for more information). The default label type is: plain
  • initialValueName: The optional name of a model value that contains the initial value of the text box. Note that this is a raw string not a key to be localized. The default is not to have an initial value.
  • constraint: The optional, and unused, regular expression for the text to be entered.
  • secret: If true then the text typed by the user should be obscured. This parameter is optional. If the value is null, which is the default, then the optional <Secret> element is not included.
  • readOnly: This optional parameter makes the initial text value read-only. If the value is null, which is the default, then the optional <ReadOnly> element is not included.

Returns:

The text box authentication requirement XML fragment

Example:

Template:

@Textbox(type: "username", id:"UsernameID", saveId: "SaveUsernameID",
         labelKey: "Test:UsernameLabelKey",
         assistiveTextKey: "Test:UsernameAssistiveTextKey")

Output:

<Requirement>
  <Credential>
    <ID>UsernameID</ID>
    <SaveID>SaveUsernameID</SaveID>
    <Type>username</Type>
  </Credential>
  <Label>
    <Text>User name:</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <AssistiveText>domain\user or user@domain.com</AssistiveText>
    <Text>
      <Constraint>.+</Constraint>
    </Text>
  </Input>
</Requirement>

PasswordTextBox

string PasswordTextBox(string id = null, string saveId = null, string assistiveTextKey = null,
                       string labelKey = null, Label.LabelType labelType = Label.LabelType.plain,
                       string initialValueName = null,
                       string constraint = TextBox.DefaultConstraint, bool? readOnly = false)

Create a password text box authentication requirement fragment.

Parameters:

  • id: The optional credential identifier. If not specified, then the option <ID> element is not included.
  • saveId: The optional identifier to be used to save this credential on a client. The default is to have no save identifier.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.
  • labelKey: The localization key for the label. This parameter is required.
  • labelType: The optional label type (see the Receiver Common Authentication Forms Language document for more information). The default label type is: plain
  • initialValueName: The optional name of a model value that contains the initial value of the text box. Note that this is a raw string not a key to be localized. The default is not to have an initial value.
  • constraint: The optional, and unused, regular expression for the text to be entered.
  • readOnly: This optional parameter makes the initial text value read-only. If the value is null, which is the default, then the optional <ReadOnly> element is not included.

Returns:

The password text box authentication requirement XML fragment

Example:

Template:

@PasswordTextBox(id:"PasswordID", saveId: "SavePasswordID", labelKey: "Test:PasswordLabelKey")

Output:

<Requirement>
  <Credential>
    <ID>PasswordID</ID>
    <SaveID>SavePasswordID</SaveID>
    <Type>password</Type>
  </Credential>
  <Label>
    <Text>Password:</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <Text>
      <Secret>true</Secret>
      <ReadOnly>false</ReadOnly>
      <Constraint>.+</Constraint>
    </Text>
  </Input>
</Requirement>

UsernameTextBox

string UsernameTextBox(string id = null, string saveId = null, string assistiveTextKey = null,
                       string labelKey = null, Label.LabelType labelType = Label.LabelType.plain,
                       string initialValueName = null,
                       string constraint = TextBox.DefaultConstraint, bool? secret = null,
                       bool? readOnly = null)

Create a user name text box authentication requirement fragment.

Parameters:

  • id: The optional credential identifier. If not specified, then the option <ID> element is not included.
  • saveId: The optional identifier to be used to save this credential on a client. The default is to have no save identifier.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.
  • labelKey: The localization key for the label. This parameter is required.
  • labelType: The optional label type (see the Receiver Common Authentication Forms Language document for more information). The default label type is: plain
  • initialValueName: The optional name of a model value that contains the initial value of the text box. Note that this is a raw string not a key to be localized. The default is not to have an initial value.
  • constraint: The optional, and unused, regular expression for the text to be entered.
  • secret: If true then the text typed by the user should be obscured. This parameter is optional. If the value is null, which is the default, then the optional <Secret> element is not included.
  • readOnly: This optional parameter makes the initial text value read-only. If the value is null, which is the default, then the optional <ReadOnly> element is not included.

Returns: The user name text box authentication requirement XML fragment

Example:

Template:

@UsernameTextBox(id:"UsernameID", saveId: "SaveUsernameID", labelKey: "Test:UsernameLabelKey",
                 assistiveTextKey: "Test:UsernameAssistiveTextKey")

Output:

<Requirement>
  <Credential>
    <ID>UsernameID</ID>
    <SaveID>SaveUsernameID</SaveID>
    <Type>username</Type>
  </Credential>
  <Label>
    <Text>User name:</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <AssistiveText>domain\user or user@domain.com</AssistiveText>
    <Text>
      <Constraint>.+</Constraint>
    </Text>
  </Input>
</Requirement>

NewPasswordTextBox

string NewPasswordTextBox(string id = null, string saveId = null, string assistiveTextKey = null,
                          string labelKey = null,Label.LabelType labelType = Label.LabelType.plain,
                          string initialValueName = null,
                          string constraint = TextBox.DefaultConstraint, bool? secret = null,
                          bool? readOnly = null)

Create a new password text box authentication requirement fragment.

Parameters:

  • id: The optional credential identifier. If not specified, then the option <ID> element is not included.
  • saveId: The optional identifier to be used to save this credential on a client. The default is to have no save identifier.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.
  • labelKey: The localization key for the label. This parameter is required.
  • labelType: The optional label type (see the Receiver Common Authentication Forms Language document for more information). The default label type is: plain
  • initialValueName: The optional name of a model value that contains the initial value of the text box. Note that this is a raw string not a key to be localized. The default is not to have an initial value.
  • constraint: The optional, and unused, regular expression for the text to be entered.
  • secret: If true then the text typed by the user should be obscured. This parameter is optional. If the value is null, which is the default, then the optional <Secret> element is not included.
  • readOnly: This optional parameter makes the initial text value read-only. If the value is null, which is the default, then the optional <ReadOnly> element is not included.

Returns:

The new password text box authentication requirement xml fragment

Example:

Template:

@NewPasswordTextBox(id:"NewPasswordID", saveId: "SavePasswordID", 
                    labelKey:"Test:NewPasswordLabelKey",
                    assistiveTextKey: "Test:NewPasswordAssisitiveTextKey")

Output:

<Requirement>
  <Credential>
    <ID>NewPasswordID</ID>
    <SaveID>SavePasswordID</SaveID>
    <Type>newpassword</Type>
  </Credential>
  <Label>
    <Text>Password:</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <AssistiveText>Must meet complexity requirements</AssistiveText>
    <Text>
      <Secret>true</Secret>
      <Constraint>.+</Constraint>
    </Text>
  </Input>
</Requirement>

PasscodeTextBox

string PasscodeTextBox(string id = null, string assistiveTextKey = null, string labelKey = null,
                       Label.LabelType labelType = Label.LabelType.plain,
                       string constraint = TextBox.DefaultConstraint)

Create a passcode text box authentication requirement fragment.

Parameters:

  • id: The optional credential identifier. If not specified, then the option <ID> element is not included.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.
  • labelKey: The localization key for the label. This parameter is required.
  • labelType: The optional label type (see the Receiver Common Authentication Forms Language document for more information). The default label type is: plain
  • constraint: The optional, and unused, regular expression for the text to be entered.

Returns:

The passcode text box authentication requirement xml fragment

Example:

Template:

@PasscodeTextBox(id:"PasscodeID", labelKey: "Test:PasscodeLabelKey",
                 assistiveTextKey: "Test:PasscodeAssisitiveTextKey")

Output:

<Requirement>
  <Credential>
    <ID>PasscodeID</ID>
    <Type>passcode</Type>
  </Credential>
  <Label>
    <Text>Passcode:</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <AssistiveText>PIN and token</AssistiveText>
    <Text>
      <Secret>true</Secret>
      <Constraint>.+</Constraint>
    </Text>
  </Input>
</Requirement>

Image

string Image(string imageKey)

Create an image label authentication requirement fragment with no associated input.

Parameters:

  • imageKey: The image key, i.e. the file name in the Templates directory. This is a required parameter.

Returns:

The image label authentication requirement XML fragment

Example:

Template:

@Image("citrix_logo.gif")
Output:
<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <Binary>data:image/gif;base64,R0lGODlhXAAkAEAAACH5BAAAAA…+l105A7NZ1PAQEAOw==</Binary>
    <Type>image</Type>
  </Label>
  <Input />
</Requirement>

Text

string Text(string textKey)

Create a text label authentication requirement fragment with no associated input, in the plain style.

Parameters: - textKey: The text localization key. This is a required parameter.

Returns:

The text label authentication requirement xml fragment

Example:

Template:

@Text("Test:PlainKey")
Output:
<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <Text>This is plain text.</Text>
    <Type>plain</Type>
  </Label>
  <Input />
</Requirement>

Heading

string Heading(string headingKey)

Create a heading text label authentication requirement fragment with no associated input.

Parameters:

  • headingKey: The text localization key. This is a required parameter.

Returns:

The heading text label authentication requirement XML fragment

Example:

Template:

@Heading("Test:HeadingKey")

Output:

<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <Text>This is a heading.</Text>
    <Type>heading</Type>
  </Label>
  <Input />
</Requirement>

Information

string Information(string informationKey)

Create an information text label authentication requirement fragment with no associated input.

Parameters:

  • informationKey: The text localization key. This is a required parameter.

Returns:

The information text label authentication requirement XML fragment

Example:

Template:

@Information("Test:InformationKey")
Output:
<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <Text>This is an information item.</Text>
    <Type>information</Type>
  </Label>
  <Input />
</Requirement>

Warning

string Warning(string warningKey)

Create a warning text label authentication requirement fragment with no associated input.

Parameters:

  • warningKey: The text localization key. This is a required parameter.

Returns:

The warning text label authentication requirement XML fragment

Example:

Template:

@Warning("Test:WarningKey")
Output:
<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <Text>This is a warning.</Text>
    <Type>warning</Type>
  </Label>
  <Input />
</Requirement>

Error

string Error(string errorKey)

Create an error text label authentication requirement fragment with no associated input.

Parameters:

  • errorKey: The text localization key. This is a required parameter.

Returns:

The error text label authentication requirement XML fragment

Example:

Template:

@Error("Test:ErrorKey")
Output:
<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <Text>This is an error.</Text>
    <Type>error</Type>
  </Label>
  <Input />
</Requirement>

Confirmation

string Confirmation(string confirmationKey)

Create a confirmation text label authentication requirement fragment with no associated input.

Parameters: - confirmationKey: The text localization key. This is a required parameter.

Returns: The confirmation text label authentication requirement XML fragment

Example:

Template:

@Confirmation("Test:ConfirmationKey")
Output:
<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <Text>This is a confirmation.</Text>
    <Type>confirmation</Type>
  </Label>
  <Input />
</Requirement>

InfoMessages

string InfoMessages()

Create a fragment that represents the list of all information label authentication requirements generated in the form.

Parameters:

None

Returns:

All the information label authentication requirements as an XML fragment

Example:

Form:

TemplateEngine.RenderTemplate(FormName, Cultures, Values,
            new[]
            {
                new FormsMessage
                    {
                        LocalisationKey = PasswordExpiredLocalisationKey,
                        MessageType = Label.LabelType.information
                    }
            })

Template:

@InfoMessages()
Output:
<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <Text>Your password has expired and must be changed.</Text>
    <Type>information</Type>
  </Label>
  <Input />
 </Requirement>

WarningMessages

string WarningMessages()

Create a fragment that represents the list of all warning label authentication requirements generated in the form.

Parameters:

None

Returns:

All the warning label authentication requirements as an XML fragment

Example:

Form:

TemplateEngine.RenderTemplate(FormName, Cultures, Values,
            new[]
            {
                new FormsMessage
                    {
                        LocalisationKey = WarningLocalizationKey,
                        MessageType = Label.LabelType.warning
                    }
            })

Template:

@WarningMessages()
Output:
<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <Text>This is a warning message.</Text>
    <Type>warning</Type>
  </Label>
  <Input />
 </Requirement>

ErrorMessages

string ErrorMessages()

Create a fragment that represents the list of all error label authentication requirements generated in the form.

Parameters:

None

Returns:

All the error label authentication requirements as an XML fragment

Example:

Form:

TemplateEngine.RenderTemplate(FormName, Cultures, Values,
            new[]
            {
                new FormsMessage
                    {
                        LocalisationKey = ErrorLocalizationKey,
                        MessageType = Label.LabelType.error
                    }
            })

Template:

@ErrorMessages()
Output:
<Requirement>
  <Credential>
    <Type>none</Type>
  </Credential>
  <Label>
    <Text>This is an error message.</Text>
    <Type>error</Type>
  </Label>
  <Input />
 </Requirement>

Button

string Button(string id, string buttonKey, string assistiveTextKey = null)

Create a button authentication requirement fragment.

Parameters:

  • id: The button identifier. This is a required parameter.
  • buttonKey: The localization key for the button text. This is a required parameter.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.

Returns: The button authentication requirement XML fragment

Example:

Template:

@Button(id:"loginBtn", buttonKey: "LogonButtonKey")

Output:

<Requirement>
  <Credential>
    <ID>loginBtn</ID>
    <Type>none</Type>
  </Credential>
  <Label>
    <Type>none</Type>
  </Label>
  <Input>
    <Button>Log On</Button>
  </Input>
</Requirement>

SaveCredential

string SaveCredential(string id, string assistiveTextKey = null, string labelKey = null,
                      Label.LabelType labelType = Label.LabelType.plain,
                      bool? initiallyChecked = null)

Create a save credential requirement fragment.

Parameters:

  • id: The optional credential identifier. If not specified, then the option <ID> element is not included.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.
  • labelKey: The localization key for the label. This parameter is required.
  • labelType: The optional label type (see the Receiver Common Authentication Forms Language document for more information). The default label type is: plain
  • initiallyChecked: The optional value to set whether the associated check box is initially checked. If the value is null, which is the default, then the optional < InitialValue > element is not included.

Returns:

The save credential authentication requirement XML fragment

Example:

Template:

@SaveCredential(id:"SaveCredentialID", labelKey: "Test:SaveCredentialKey",
                assistiveTextKey: "SaveCredentialAssistiveTextKey", initiallyChecked: true)

Output:

<Requirement>
  <Credential>
    <ID>SaveCredentialID</ID>
    <Type>savecredentials</Type>
  </Credential>
  <Label>
    <Text>Remember my password</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <AssistiveText>Save your credentials locally</AssistiveText>
    <CheckBox>
      <InitialValue>true</InitialValue>
    </CheckBox>
  </Input>
</Requirement>

DomainComboBox

string DomainComboBox(string id, string displayItemsValueName,
                      string initialSelectionValueName = null, string saveId = null,
                      string assistiveTextKey = null, string labelKey = null,
                      Label.LabelType labelType = Label.LabelType.plain)

Create a combo box authentication requirement fragment for domains.

Parameters:

  • id: The optional credential identifier. If not specified, then the option <ID> element is not included.
  • displayItemsValueName: The name of the dictionary of display-value pairs in the model. This is a required parameter.
  • initialSelectionValueName: The optional initially selected display value. The default is to select the first value.
  • saveId: The optional identifier to be used to save this credential on a client. The default is to have no save identifier.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.
  • labelKey: The localization key for the label. This parameter is required.
  • labelType: The optional label type (see the Receiver Common Authentication Forms Language document for more information). The default label type is: plain

Returns:

The domain combo box authentication requirement XML fragment

Example:

Model:

new
{
    Domains = new Dictionary<string, string>
              {
                  { @"citrix", @"citrix.com"},
                  { @"citrite", @"citrite.local"}
              },
    InitialDomain = @"citrite.local"
}

Template:

@DomainComboBox(id:"DomainID", displayItemsValueName: "Domains",
                initialSelectionValueName: "InitialDomain", saveId: "SaveDomainID",
                labelKey: "Test:DomainKey",
                assistiveTextKey: "Test:DomainComboBoxAssistiveTextKey")

Output:

<Requirement>
  <Credential>
    <ID>DomainID</ID>
    <SaveID>SaveDomainID</SaveID>
    <Type>domain</Type>
  </Credential>
  <Label>
    <Text>Domain:</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <AssistiveText>Please select your domain</AssistiveText>
    <ComboBox>
      <InitialSelection>citrite.local</InitialSelection>
      <DisplayValues>
        <DisplayValue>
          <Display>citrix</Display>
          <Value>citrix.com</Value>
        </DisplayValue>
        <DisplayValue>
          <Display>citrite</Display>
          <Value>citrite.local</Value>
        </DisplayValue>
      </DisplayValues>
    </ComboBox>
  </Input>
</Requirement>

DomainTextBox

string DomainTextBox(string id = null, string saveId = null, string assistiveTextKey = null,
                     string labelKey = null, Label.LabelType labelType = Label.LabelType.plain,
                     string initialValueName = null,
                     string constraint = TextBox.DefaultConstraint, bool? secret = null,
                     bool? readOnly = null)

Create a text box authentication requirement fragment for domains.

Parameters:

  • id: The optional credential identifier. If not specified, then the option <ID> element is not included.
  • saveId: The optional identifier to be used to save this credential on a client. The default is to have no save identifier.
  • assistiveTextKey: The optional localization key for the assistive text. The default is to have no assistive text.
  • labelKey: The localization key for the label. This parameter is required.
  • labelType: The optional label type (see the Receiver Common Authentication Forms Language document for more information). The default label type is: plain
  • initialValueName: The optional name of a model value that contains the initial value of the text box. Note that this is a raw string not a key to be localized. The default is not to have an initial value.
  • constraint: The optional, and unused, regular expression for the text to be entered.
  • secret: If true then the text typed by the user should be obscured. This parameter is optional. If the value is null, which is the default, then the optional <Secret> element is not included.
  • readOnly: This optional parameter makes the initial text value read-only. If the value is null, which is the default, then the optional <ReadOnly> element is not included.

Returns:

The domain text box authentication requirement XML fragment

Example:

Template:

@DomainTextBox(id:"DomainID", saveId: "SaveDomainID", labelKey: "Test:DomainKey",
               assistiveTextKey: "Test:DomainTextBoxAssistiveTextKey")

Output:

<Requirement>
  <Credential>
    <ID>DomainID</ID>
    <SaveID>SaveDomainID</SaveID>
    <Type>domain</Type>
  </Credential>
  <Label>
    <Text>Domain:</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <AssistiveText>Please supply your domain</AssistiveText>
      <Text>
        <InitialValue></InitialValue>
        <Constraint>.+</Constraint>
      </Text>
  </Input>
</Requirement>

Appendix A: Event Log Entries

If there are issues with the template processing, the Forms Template Engine adds events to the event log at the following location, with Task Category 1170:

Event Viewer > Applications and Services Logs > Citrix Delivery Services

The following examples illustrate the errors that can be produced.

Razor Template Error: Event Id 1

There was a Razor compilation error of a template named UsernamePassword with contents:

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
@* ©2014 Citrix Systems, Inc.   All rights reserved. *@
@foreach
@ControlHeaders(status: StatusSuccess, result: ResultMoreInfo, stateContext: GetValue("StateContext"))
  <AuthenticationRequirements>
    <PostBack>@GetTextValue("PostBackUrl")</PostBack>
    <CancelPostBack>@GetTextValue("CancelPostBackUrl")</CancelPostBack>
    <CancelButtonText>@GetLocalisedTextValue("ExplicitFormsCommon:CancelButtonText")</CancelButtonText>
    <Requirements>
      @* @Heading("ExplicitAuth:AuthenticateHeadingText") *@
      @InfoMessages()
      @WarningMessages()
      @UsernameTextBox(id: @GetTextValue("usernameId"), saveId: @GetTextValue("usernameSaveId"), labelKey: "ExplicitFormsCommon:UsernameLabel", initialValueName: "InitialUsername", assistiveTextKey: @GetTextValue("AssistiveTextValue"), secret: false, readOnly: false)
      @PasswordTextBox(id: @GetTextValue("passwordId"), saveId: @GetTextValue("passwordSaveId"), labelKey: "ExplicitFormsCommon:PasswordLabel", initialValueName: "dummy", readOnly: false)
      @if (ControlValue("DisplayDomainText")) {
      @DomainTextBox(id: @GetTextValue("domainId"), saveId: @GetTextValue("domainSaveId"), labelKey: "ExplicitFormsCommon:DomainLabel", readOnly: false)
      } else if (ControlValue("DisplayDomainCombo")) {
      @DomainComboBox(id: @GetTextValue("domainId"), saveId: @GetTextValue("domainSaveId"), displayItemsValueName: "DomainValues", initialSelectionValueName: "InitialDomain", labelKey: "ExplicitFormsCommon:DomainLabel")
      }
      @SaveCredential(id: @GetTextValue("saveCredentialsId"), labelKey: "ExplicitFormsCommon:SaveCredentialsLabel", initiallyChecked: ControlValue("SaveCredentials"))
      @ErrorMessages()
      @Button(id: @GetTextValue("loginButtonId"), buttonKey: "ExplicitFormsCommon:LoginButtonText")
    </Requirements>
  </AuthenticationRequirements>
</AuthenticateResponse>

Errors:

Expected a "{" but found a "@".  Block statements must be enclosed in "{" and "}".  You cannot use single-statement control-flow statements in CSHTML pages. For example, the following is not allowed:

@if(isLoggedIn)
    <p>Hello, @user</p>

Instead, wrap the contents of the block in "{}":

@if(isLoggedIn) {
    <p>Hello, @user</p>
} at line:(183:4,0)

C# Compilation Error: Event Id 2

There was a C# compilation error of a template named UsernamePassword with contents:

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
@* ©2014 Citrix Systems, Inc.   All rights reserved. *@
@ControlHeaders(status: StatusSuccess, result: ResultMoreInfo, stateContext: GetValue("StateContext"))
  <AuthenticationRequirements>
    <PostBack>@GetTextValue2("PostBackUrl")</PostBack>
    <CancelPostBack>@GetTextValue("CancelPostBackUrl")</CancelPostBack>
    <CancelButtonText>@GetLocalisedTextValue("ExplicitFormsCommon:CancelButtonText")</CancelButtonText>
    <Requirements>
      @* @Heading("ExplicitAuth:AuthenticateHeadingText") *@
      @InfoMessages()
      @WarningMessages()
      @UsernameTextBox(id: @GetTextValue("usernameId"), saveId: @GetTextValue("usernameSaveId"), labelKey: "ExplicitFormsCommon:UsernameLabel", initialValueName: "InitialUsername", assistiveTextKey: @GetTextValue("AssistiveTextValue"), secret: false, readOnly: false)
      @PasswordTextBox(id: @GetTextValue("passwordId"), saveId: @GetTextValue("passwordSaveId"), labelKey: "ExplicitFormsCommon:PasswordLabel", initialValueName: "dummy", readOnly: false)
      @if (ControlValue("DisplayDomainText")) {
      @DomainTextBox(id: @GetTextValue("domainId"), saveId: @GetTextValue("domainSaveId"), labelKey: "ExplicitFormsCommon:DomainLabel", readOnly: false)
      } else if (ControlValue("DisplayDomainCombo")) {
      @DomainComboBox(id: @GetTextValue("domainId"), saveId: @GetTextValue("domainSaveId"), displayItemsValueName: "DomainValues", initialSelectionValueName: "InitialDomain", labelKey: "ExplicitFormsCommon:DomainLabel")
      }
      @SaveCredential(id: @GetTextValue("saveCredentialsId"), labelKey: "ExplicitFormsCommon:SaveCredentialsLabel", initiallyChecked: ControlValue("SaveCredentials"))
      @ErrorMessages()
      @Button(id: @GetTextValue("loginButtonId"), buttonKey: "ExplicitFormsCommon:LoginButtonText")
    </Requirements>
  </AuthenticationRequirements>
</AuthenticateResponse>

Source Code:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.18449
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Citrix.CompiledForms {
    using System;
    using Citrix.DeliveryServices.Authentication.Forms.Template.Requirements;
    using Citrix.DeliveryServices.Authentication.Forms.Template;    

    public class UsernamePassword : Citrix.DeliveryServices.Authentication.Forms.Template.FormsTemplateBase {

#line hidden

        public UsernamePassword() {
        }

        public override void Execute() {
WriteLiteral("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<AuthenticateResponse");

WriteLiteral(" xmlns=\"http://citrix.com/authentication/response/1\"");
WriteLiteral(">\r\n");
WriteLiteral("\r\n");

            #line 4 "UsernamePassword.tfrm"
Write(ControlHeaders(status: StatusSuccess, result: ResultMoreInfo, stateContext: GetValue("StateContext")));

            #line default
            #line hidden
WriteLiteral("\r\n  <AuthenticationRequirements>\r\n    <PostBack>");

            #line 6 "UsernamePassword.tfrm"
         Write(GetTextValue2("PostBackUrl"));

            #line default
            #line hidden
WriteLiteral("</PostBack>\r\n    <CancelPostBack>");

            #line 7 "UsernamePassword.tfrm"
               Write(GetTextValue("CancelPostBackUrl"));

            #line default
            #line hidden
WriteLiteral("</CancelPostBack>\r\n    <CancelButtonText>");

            #line 8 "UsernamePassword.tfrm"
                 Write(GetLocalisedTextValue("ExplicitFormsCommon:CancelButtonText"));

            #line default
            #line hidden
WriteLiteral("</CancelButtonText>\r\n    <Requirements>\r\n      ");
WriteLiteral("\r\n");
WriteLiteral("      ");

            #line 11 "UsernamePassword.tfrm"
 Write(InfoMessages());


            #line default
            #line hidden
WriteLiteral("\r\n");
WriteLiteral("      ");

            #line 12 "UsernamePassword.tfrm"
Write(WarningMessages());

            #line default
            #line hidden
WriteLiteral("\r\n");
WriteLiteral("      ");

            #line 13 "UsernamePassword.tfrm"
Write(UsernameTextBox(id: @GetTextValue("usernameId"), saveId: @GetTextValue("usernameSaveId"), labelKey: "ExplicitFormsCommon:UsernameLabel", initialValueName: "InitialUsername", assistiveTextKey: @GetTextValue("AssistiveTextValue"), secret: false, readOnly: false));

            #line default
            #line hidden
WriteLiteral("\r\n");
WriteLiteral("      ");

            #line 14 "UsernamePassword.tfrm"
Write(PasswordTextBox(id: @GetTextValue("passwordId"), saveId: @GetTextValue("passwordSaveId"), labelKey: "ExplicitFormsCommon:PasswordLabel", initialValueName: "dummy", readOnly: false));

            #line default
            #line hidden
WriteLiteral("\r\n");

            #line 15 "UsernamePassword.tfrm"

            #line default
            #line hidden

            #line 15 "UsernamePassword.tfrm"
       if (ControlValue("DisplayDomainText")) {

            #line default
            #line hidden

            #line 16 "UsernamePassword.tfrm"
Write(DomainTextBox(id: @GetTextValue("domainId"), saveId: @GetTextValue("domainSaveId"), labelKey: "ExplicitFormsCommon:DomainLabel", readOnly: false));

            #line default
            #line hidden

            #line 16 "UsernamePassword.tfrm"

      } else if (ControlValue("DisplayDomainCombo")) {    

            #line default
            #line hidden

            #line 18 "UsernamePassword.tfrm"
Write(DomainComboBox(id: @GetTextValue("domainId"), saveId: @GetTextValue("domainSaveId"), displayItemsValueName: "DomainValues", initialSelectionValueName: "InitialDomain", labelKey: "ExplicitFormsCommon:DomainLabel"));

            #line default
            #line hidden

            #line 18 "UsernamePassword.tfrm"

      }

            #line default
            #line hidden
WriteLiteral("      ");

            #line 20 "UsernamePassword.tfrm"
Write(SaveCredential(id: @GetTextValue("saveCredentialsId"), labelKey: "ExplicitFormsCommon:SaveCredentialsLabel", initiallyChecked: ControlValue("SaveCredentials")));

            #line default
            #line hidden
WriteLiteral("\r\n");
WriteLiteral("      ");

            #line 21 "UsernamePassword.tfrm"
Write(ErrorMessages());

            #line default
            #line hidden
WriteLiteral("\r\n");
WriteLiteral("      ");


            #line 22 "UsernamePassword.tfrm"
Write(Button(id: @GetTextValue("loginButtonId"), buttonKey: "ExplicitFormsCommon:LoginButtonText"));

            #line default
            #line hidden
WriteLiteral("\r\n    </Requirements>\r\n  </AuthenticationRequirements>\r\n</AuthenticateResponse>");

        }
    }
}

Errors:

The name 'GetTextValue2' does not exist in the current context at line:6

Invalid Reciever Common Form Xml: Event Id 4

The Forms template generated invalid Xml:

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">



            <Requirement>
        <Credential>
          <ID>username</ID>
          <SaveID>UsernamePasswordForm-Username</SaveID>
          <Type>username</Type>
        </Credential>
        <Label>
          <Text>User name:</Text>
          <Type>plain</Type>
        </Label>
        <Input>
          <AssistiveText>domain\user or user@domain.com</AssistiveText>
          <Text>
            <Secret>false</Secret>
            <ReadOnly>false</ReadOnly>
            <InitialValue></InitialValue>
            <Constraint>.+</Constraint>
          </Text>
        </Input>
      </Requirement>
            <Requirement>
        <Credential>
          <ID>password</ID>
          <SaveID>UsernamePasswordForm-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>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>
System.Xml.XmlException, System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
The 'AuthenticateResponse' start tag on line 2 position 2 does not match the end tag of 'Requirements'. Line 73, position 7.
   at System.Xml.XmlTextReaderImpl.Throw(String res, String[] args)
   at System.Xml.XmlTextReaderImpl.ThrowTagMismatch(NodeData startTag)
   at System.Xml.XmlTextReaderImpl.ParseEndElement()
   at System.Xml.XmlTextReaderImpl.ParseElementContent()
   at System.Xml.XmlLoader.LoadNode(Boolean skipOverWhitespace)
   at System.Xml.XmlLoader.LoadDocSequence(XmlDocument parentDoc)
   at System.Xml.XmlDocument.Load(XmlReader reader)
   at System.Xml.XmlDocument.LoadXml(String xml)
   at Citrix.DeliveryServices.Authentication.Forms.Template.FormsTemplateBase.Render(IDictionary`2 renderValues, ILocaliser localiser, IEnumerable`1 messages)

Appendix B: Form Template Language Examples

The following templates all generate the same Receiver Common Forms XML:

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
  <Status>success</Status>
  <Result>more-info</Result>
  <StateContext>0x12345678</StateContext>
  <AuthenticationRequirements>
    <PostBack>/Postback</PostBack>
    <CancelPostBack>/Cancel</CancelPostBack>
    <CancelButtonText>Cancel</CancelButtonText>
    <Requirements>
      <Requirement>
        <Credential>
          <ID>UsernameID</ID>
          <SaveID>SaveUsernameID</SaveID>
          <Type>username</Type>
        </Credential>
        <Label>
          <Text>User name:</Text>
          <Type>plain</Type>
        </Label>
        <Input>
          <AssistiveText>domain\user or user@domain.com</AssistiveText>
          <Text>
            <Constraint>.+</Constraint>
          </Text>
        </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>

Low Level API

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
  <Status>success</Status>
  <Result>more-info</Result>
  <StateContext>0x12345678</StateContext>
  <AuthenticationRequirements>
    <PostBack>/Postback</PostBack>
    <CancelPostBack>/Cancel</CancelPostBack>
    <CancelButtonText>@GetLocalisedTextValue("CancelButtonText")</CancelButtonText>
    <Requirements>
      <Requirement>
        <Credential>
          <ID>UsernameID</ID>
          <SaveID>SaveUsernameID</SaveID>
          <Type>username</Type>
        </Credential>
        <Label>
          <Text>@GetLocalisedTextValue("UsernameLabelKey")</Text>
          <Type>plain</Type>
        </Label>
        <Input>
          <AssistiveText>@GetLocalisedTextValue("UsernameAssistiveTextKey")</AssistiveText>
          <Text>
            <Constraint>.+</Constraint>
          </Text>
        </Input>
      </Requirement>
      <Requirement>
        <Credential>
          <ID>loginBtn</ID>
          <Type>none</Type>
        </Credential>
        <Label>
          <Type>none</Type>
        </Label>
        <Input>
          <Button>@GetLocalisedTextValue("LogonButtonKey")</Button>
        </Input>
      </Requirement>
    </Requirements>
  </AuthenticationRequirements>
</AuthenticateResponse>

Requirements API

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
@ControlHeaders(status: StatusSuccess, result: ResultMoreInfo, stateContext: "0x12345678")
  <AuthenticationRequirements>
    <PostBack>/Postback</PostBack>
    <CancelPostBack>/Cancel</CancelPostBack>
    <CancelButtonText>Cancel</CancelButtonText>
    <Requirements>
      <Requirement>
@Credential(credentialType: "username", id: "UsernameID", saveId: "SaveUsernameID")
@TextLabel(labelKey: "UsernameLabelKey")
@InputTextBox(assistiveTextKey: "UsernameAssistiveTextKey")
      </Requirement>
      <Requirement>
@Credential(credentialType: "none", id: "loginBtn")
@TextLabel(labelKey: null, labelType: Label.LabelType.none)
@InputButton(buttonKey: "LogonButtonKey")
      </Requirement>
    </Requirements>
  </AuthenticationRequirements>
</AuthenticateResponse>

High Level API

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
@ControlHeaders(status: StatusSuccess, result: ResultMoreInfo, stateContext: "0x12345678")
  <AuthenticationRequirements>
    <PostBack>/Postback</PostBack>
    <CancelPostBack>/Cancel</CancelPostBack>
    <CancelButtonText>Cancel</CancelButtonText>
    <Requirements>
@UsernameTextBox(id:"UsernameID", saveId: "SaveUsernameID", labelKey: "UsernameLabelKey",
                 assistiveTextKey: "UsernameAssistiveTextKey")
@Button(id:"loginBtn", buttonKey: "LogonButtonKey")
    </Requirements>
  </AuthenticationRequirements>
</AuthenticateResponse>