Citrix Common forms authentication API

Scope

The purpose of this document is to describe the StoreFront Common Forms Authentication Application Programming Interface.

Background

Citrix has devised a common authentication protocol that is implemented by StoreFront NetScaler Gateway. Citrix has implemented this in Citrix Workspace app for the major device platforms, notably Windows, Mac, iOS, Android, Linux, and when accessing them through a web browser. See StoreFront Token Services for more details.

An important part of the common authentication protocol is a generic forms language, 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 and 2388 for more details.

Third parties are able to extend the authentication methods supported by StoreFront Services and NetScaler Gateway by implementing server logic that emits and consumes the Common Forms language; see the StoreFront Services Authentication SDK Overview for more details.

The Common Forms protocol represents a secure conversation between the server and the user agent on the client, where forms are generated by the server, rendered by the useragent, completed by the user, and returned to the server by the user agent. Whilst the forms generated may be the same, the start and end of the conversations differ between StoreFront Service and NetScaler Gateway, representing their different needs.

The form generation and handling can be delegated from NetScaler Gateway to a StoreFront server by means of the Delegated Forms Authentication Protocol. This allows a third party to create one customization that can be used in both StoreFront and NetScaler Gateway.

This document details the StoreFront Services conversation, specifying the messages at the HTTP level.

StoreFront conversation

For maximum interoperability, the protocol is a REST API, that is, XML forms transmitted over HTTPS. The flow of forms and responses constitutes a conversation that can have state associated with the overall conversation through the use of HTTP sessions.

Note:

  • The conversations require HTTP state management, as described by RFC 6265, in particular correct handling of cookies.
  • In the following sections, the definition of HTTP headers uses the notation defined in RFC 2616.

The conversation contains sensitive information, such as passwords, so confidentiality and integrity are required. The expectation is that TLS will be used between the client and the server to both maintain confidentiality and also for server identification, to ensure that the user agent sends confidential information only to trusted servers.

For StoreFront, the conversation starts with a StoreFront Token Services (RST) message to an endpoint obtained from the client as part of the Protocol Choices message. A successful conversation ends with a Request Security Token Response (RSTR) message. The conversation can also end with a failure form, an HTTP status code other than 200, or a network failure.

For the rest of the conversation the server returns authentication forms described in Citrix Common Authentication Forms Language.

Note to client implementers:

Because a client does not know when a conversation will end, it should always set the HTTP Accept header to include both the Form and RSTR types.

Conversation start

  • URL: Usually obtained from the Protocol Choices data
  • HTTP Method: POST
  • Authentication: None
  • Request Content-Type: application/vnd.citrix.requesttoken+xml
  • Accept:
    • application/vnd.citrix.requesttokenresponse+xml,
    • application/vnd.citrix.authenticateresponse-1+xml

Because the conversation persists over several HTTP requests, the initial conversation start may include a Set-Cookie header. The usual HTTP semantics apply to this cookie, as described above. In addition, various aspects of the conversation are negotiated at the start of the conversation through the use of HTTP headers, as discussed below:

Credential type negotiation:

The Common Forms Language contains a ‘soft’ enumeration for the credential types, which means that new credential types can be added without breaking the schema. The behavior of user agents is to reject forms with unknown credential types. Hence credential types need to be negotiated between the client and server using the following HTTP header:

credential-Types-Header = "X-Citrix-AM-CredentialTypes" ":" field-value
field-value = *LWS credential-types-value *LWS
credential-types-value = 1#( credential-type-value )
credential-type-value = *TEXT
<!--NeedCopy-->

The credential types header is a comma-separated list of credential types that the client supports.

Example:

X-Citrix-AM-CredentialTypes: none, username, domain, password, newpassword,
                        passcode, savecredentials, textcredential
<!--NeedCopy-->

New clients will use the above HTTP header to signal the credential types that are known in a comma-separated list. Older clients do not set this header, so if the header is missing then the following credential types will be assumed: none, username, domain, password, newpassword, passcode, savecredentials, textcredential. If a client does not support a required credential type, then the behavior is defined by the form implementation on the server, which may choose to terminate the conversation, or supply a simplified form that the client can process.

Label type negotiation:

The Common Forms Language contains a “soft” enumeration for the label types, which means that new label types can be added without breaking the schema. Label types can be negotiated between the client and server using the following HTTP header:

label-Types-Header = "X-Citrix-AM-LabelTypes" ":" field-value
field-value = *LWS label-types-value *LWS
label-types-value = 1#( label-type )
label-type = *TEXT
<!--NeedCopy-->

The label types header is a comma-separated list of label values that the client supports.

Example:

       X-Citrix-AM-LabelTypes: none, plain, heading, information, warning,
                           error, confirmation
<!--NeedCopy-->

New clients will use the above HTTP header to signal the label types that are known in a comma-separated list. Older clients do not set this header, so if the header is missing then the following label types shall be assumed: none, plain, heading, information, warning, error, confirmation. If a client does not support a required label type, then the behavior is defined by the form implementation on the server, which may choose to terminate the conversation, or supply a simplified form that the client can process.

Notes regarding images:

  • The default list of label types does not contain image. This is because none of the known legacy clients supported rendering images.
  • Images are intended for authentication purposes, such as captcha images, and are not intended for generic branding.
  • The image data supplied in the form is to be interpreted as a Data URI as defined by RFC 2397.

Language negotiation:

The textual data displayed in the form is defined by the StoreFront Server. In order to supply text in the appropriate language for the user, the server supports the standard HTTP language negotiation mechanism through the use of the Accept-Language header as described in RFC 2616.

Accept-Language-Header = "Accept-Language" ":" field-value
field-value = *LWS accept-language-value *LWS
accept-language-value = 1#( language-range [ ";" "q" "=" qvalue ] )
language-range  = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
<!--NeedCopy-->

The header value may include a comma-separated list of language ranges. Each language range may be given an associated quality value that represents an estimate of the user’s preference for the languages specified by that range. The quality value defaults to ‘q=1’.

Example:

       Accept-Language: da, en-gb;q=0.8, en;q=0.7
<!--NeedCopy-->

Should be interpreted as: ‘I prefer Danish, but will accept British English and other types of English.’

If a language cannot be negotiated, then a default language will be used, typically English.

Client response to a form

  • URL: Obtained from the current form
  • HTTP Method: POST
  • Authentication: None
  • Request Content-Type: application/x-www-form-urlencoded
  • Accept:
    • application/vnd.citrix.requesttokenresponse+xml,
    • application/vnd.citrix.authenticateresponse-1+xml

In response to a form the client will post back the requested information in the same format as for HTML forms, and must be appropriately encoded.

Note to server implementors:

Existing clients may encode incorrectly, using ‘%20’ instead of ‘+’ to encode spaces, and may use UTF-8 encoding for extended multilingual characters that server implementations must be able to cope with.

A detailed discussion of how to form the client response for each user input type is discussed in Client responses for form input.

Wire-level example

Form from StoreFront:

HTTP/1.1 200 OK
Cache-Control: no-cache; private; no-store; must-revalidate; max-stale=0; post-check=0; pre-check=0; max-age=0
Pragma: no-cache
Content-Type: application/vnd.citrix.authenticateresponse-1+xml; charset=utf-8
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Set-Cookie: ASP.NET_SessionId=ixepmqu3dh1kxu30mv1una3y; path=/; HttpOnly
Date: Mon, 07 Apr 2014 13:08:47 GMT
Content-Length: 1552

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
  <Status>success</Status>
  <Result>more-info</Result>
  <StateContext />
  <AuthenticationRequirements>
    <PostBack>/Citrix/Authentication/ExplicitForms</PostBack>
    <CancelPostBack>/Citrix/Authentication/ExplicitForms/Cancel</CancelPostBack>
    <CancelButtonText>Cancel</CancelButtonText>
    <Requirements>
      <Requirement>
        <Credential>
          <ID>username</ID>
          <SaveID>ExplicitForms-Username</SaveID>
          <Type>username</Type>
        </Credential>
        <Label>
          <Text>User name:</Text>
          <Type>plain</Type>
        </Label>
        <Input>
          <AssistiveText>domain\user or user@domain</AssistiveText>
          <Text>
            <Secret>false</Secret>
            <ReadOnly>false</ReadOnly>
            <InitialValue></InitialValue>
            <Constraint>.+</Constraint>
          </Text>
        </Input>
      </Requirement>
      <Requirement>
        <Credential>
          <ID>password</ID>
          <SaveID>ExplicitForms-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>
<!--NeedCopy-->

Client request:

POST https://storefront.local/Citrix/Authentication/ExplicitForms HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Accept: application/vnd.citrix.requesttokenresponse+xml, application/vnd.citrix.authenticateresponse-1+xml
Host: storefront.local
Cookie: ASP.NET_SessionId=ixepmqu3dh1kxu30mv1una3y
Content-Length: 83
Expect: 100-continue
StateContext=&loginBtn=Log+On&username=animaniacs%5ctestuser0&password=testuser&saveCredentials=false
<!--NeedCopy-->

Conversation cancel

  • URL: Obtained from the current form
  • HTTP Method: POST
  • Authentication: None
  • Request Content-Type: application/x-www-form-urlencoded
  • Accept:
    • application/vnd.citrix.authenticateresponse-1+xml
  • Response Content-Type: application/vnd.citrix.authenticateresponse-1+xml

The client should POST the StateContext back to the server, to the Cancel Url obtained from the current form: e.g. to cancel the form above, the following should be POSTed:

StateContext=

Note to server implementors: The state associated with the conversation should be discarded on the server when a cancel message is received, and any attempt to re-use the existing Http session state should result in a failure form.

Notes to client implementors:

  • Implementors of long-lived clients should ensure that the session cookie is cleared after the conversation has been cancelled.
  • The assumption is that forms shall normally have a cancel button, positioned in the natural location for each client platform, relative to the other buttons. However, the cancel button should only be shown if the element is present at: /AuthenticateResponse/AuthenticationRequirements/CancelButtonText in the supplied form.

Wire-level example

Cancel request from client:

POST https://storefront.local/Citrix/Authentication/ExplicitForms/Cancel HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Accept: application/vnd.citrix.authenticateresponse-1+xml
Host: storefront.local
Cookie: ASP.NET_SessionId=ixepmqu3dh1kxu30mv1una3y
Content-Length: 14

StateContext=
<!--NeedCopy-->

Response from StoreFront:

HTTP/1.1 200 OK
Cache-Control: no-cache; private; no-store; must-revalidate; max-stale=0; post-check=0; pre-check=0; max-age=0
Pragma: no-cache
Content-Type: application/vnd.citrix.authenticateresponse-1+xml; charset=utf-8
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Server: Microsoft-IIS/8.0
Date: Mon, 07 Apr 2014 14:33:50 GMT
Content-Length: 201

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

Conversation end

  • URL: Obtained from the current form
  • HTTP Method: POST
  • Authentication: None
  • Request Content-Type: application/x-www-form-urlencoded
  • Accept:
    • application/vnd.citrix.requesttokenresponse+xml,
    • application/vnd.citrix.authenticateresponse-1+xml
  • Response Content-Type: application/vnd.citrix.requesttokenresponse+xml

Note:

For server implementors: The state associated with the conversation should be discarded on the server when the RSTR message has been sent, and any attempt to re-use the existing HTTP session state should result in a failure form.

For client implementors: Implementors of long-lived clients should ensure that the session cookie is cleared after the conversation has been completed.

Wire-level example

Authentication request from client:

POST https://storefront.local/Citrix/Authentication/ExplicitForms HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Accept: application/vnd.citrix.requesttokenresponse+xml, application/vnd.citrix.authenticateresponse-1+xml
Host: storefront.local
Cookie: ASP.NET_SessionId=ixepmqu3dh1kxu30mv1una3y
Content-Length: 83
Expect: 100-continue

StateContext=&loginBtn=Log+On&username=animaniacs%5ctestuser0&password=testuser&saveCredentials=false
<!--NeedCopy-->

Response from StoreFront:

HTTP/1.1 200 OK
Cache-Control: no-cache; private; no-store; must-revalidate; max-stale=0; post-check=0; pre-check=0; max-age=0
Pragma: no-cache
Content-Type: application/vnd.citrix.requesttokenresponse+xml
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Server: Microsoft-IIS/8.0
Date: Mon, 07 Apr 2014 14:25:52 GMT
Content-Length: 3408

<?xml version="1.0" encoding="utf-8"?>
<requesttokenresponse xmlns="http://citrix.com/delivery-services/1-0/auth/requesttokenresponse">
  <for-service>73003cf4-dc21-4bef-9138-af0ec600dba3</for-service>
  <issued>2014-04-07T14:25:51.7525144Z</issued>
  <expiry>2014-04-08T10:25:51.7525144Z</expiry>
  <lifetime>0.20:00:00</lifetime>
  <token-template />
  <token>H4sIAAAAAAAEAK1XaY+…ANAAA=</token>
  <CredentialUpdateInformationList xmlns="http://citrix.com/authentication/response/1">
    <CredentialUpdateInformation>
      <CredentialDisplayName>animaniacs</CredentialDisplayName>
      <AuthenticationInfoId>Citrix Common Forms 1.0</AuthenticationInfoId>
      <CredentialUpdateService />
    </CredentialUpdateInformation>
  </CredentialUpdateInformationList>
</requesttokenresponse>
<!--NeedCopy-->

Client persistent storage

Client persistent storage is needed by third parties using the Citrix Authentication SDK to implement authentication methods that currently rely on persistent browser cookies, e.g. for assigning a unique ID to user agents.

An authentication service specifies a value for the client to store by sending a header during a forms authentication conversation. Given a value to store, the client adds the same header to all further requests in the current and subsequent Common Forms authentication conversations with that authentication service.

Header syntax and semantics

The same header, defined below, is used in both HTTP requests and responses.

Storage-Header = "X-Citrix-AM-Storage" ":" field-value
field-value = *LWS storage-value *LWS
storage-value = *TEXT
<!--NeedCopy-->

The header value may include leading and trailing whitespace, which should be ignored, and the storage value is a plain text string. The storage value may be omitted entirely, leaving a header with an empty value.

Each X-Citrix-AM-Storage header represents a value stored by the user agent on behalf of the specific authentication service. Each authentication service may store only one value, but a client that interacts with multiple authentication services must be able store one value for each service. An authentication service is identified by the start URL of a forms conversation, including scheme, hostname, port number and path, but excluding query strings and fragments. The URLs of subsequent requests in a forms auth conversation are likely to differ from the start URL so clients must be careful not to simply use the current request URL in making decisions about storage.

Clients must compare URLs in accordance with the URI comparison rules defined in RFC 2616.

Note:

A client may authenticate against a third party authentication service through Delegated Forms Authentication using different start URLs, for example one URL for NetScaler Gateway and one URL for StoreFront. In this case the third authentication service will end up setting two storage values on the client. This is conceptually not ideal but it should be sufficient because the main goal is parity with HTTP cookie-based authentication methods.

The X-Citrix-AM-Storage header is only valid during a forms-based authentication conversation. Clients should not update storage or send a storage value outside the authentication conversation. Clients should update the storage value if the header is received on any valid forms response during the conversation, including: failure forms (for example a ‘fail’ or ‘cancel’ result, or failure status) and Request Token Responses.

There is no mechanism for a client to advertise that it supports persistent storage, other than by returning a previously stored value.

Note:

For server implementors:

  • It is the server implementor’s responsibility to appropriately protect the data stored on the client, for example using HMACs if data origin and integrity are important, or encryption if privacy is also required.
  • The maximum size of the storage header on an HTTP response is 5016 bytes, including the header name and whitespace around the value.

For client implementors:

  • Clients should treat a storage value as an opaque string and should not attempt to interpret the value. The content of a storage value is meaningful only to the originating server.

Server-to-client HTTP responses

An X-Citrix-AM-Storage header on a response is a directive for the client to persistently and securely store the value on behalf of the sending authentication service. Clients should store the received value immediately and not wait until the end of the authentication conversation, such that if an authentication attempt is abandoned the client will be able to send the last seen value on starting the next authentication attempt.

If the X-Citrix-AM-Storage response header has an empty value, then clients should delete the storage value for the sending authentication service; subsequently clients do not need to send the empty X-Citrix-AM-Storage header.

If a forms response does not include an X-Citrix-AM-Storage header, clients must assume that there is no change required, and must keep the current storage value unchanged, and continue to send the last seen value.

If the response is not a valid forms response or is otherwise not understood by the client, the client must ignore any X-Citrix-AM-Storage header and not update the storage value.

Servers should not include more than one X-Citrix-AM-Storage header in the same response. If multiple storage headers are received, clients should process the first one only.

Storage values do not have an explicit or implicit expiry date and potentially may be retained indefinitely. However, clients should delete all stored values if the user uninstalls or otherwise resets the client software, for example using the “Reset” feature in Workspace.

Client-to-server HTTP requests

If an authentication service has previously set a storage value on the client, the client must send the value to the authentication service using the X-Citrix-AM-Storage header on every request during a forms authentication attempt, starting with the request for the initial form.

Clients may omit the header if they have no value stored for the authentication service.

Wire example

Client internal storage table
Origin service start URL Storage value

A first time use scenario, so there are no stored values.

Request
POST https://example.com/Citrix/Authentication/CustomForms/Authenticate HTTP/1.1

Content-Type: application/vnd.citrix.requesttoken+xml

Accept: application/vnd.citrix.requesttokenresponse+xml,application/vnd.citrix.authenticateresponse-1+xml,text/xml

Content-Length: xxx

<?xml version="1.0"?>

<requesttoken xmlns="http://citrix.com/delivery-services/1-0/auth/requesttoken"><for-service>32a290eb-7521-cd92-1394-475927c3a239</for-service><for-service-url>https:// example.com /Citrix/Authentication/auth/v1/token</for-service-url><reqtokentemplate></reqtokentemplate><requested-lifetime>0.08:00:00</requested-lifetime></requesttoken>
<!--NeedCopy-->

There’s no stored value, so no X-Citrix-AM-Storage header needs to be sent.

Response
HTTP/1.1 200 OK

Content-Type: application/vnd.citrix.authenticateresponse-1+xml

X-Citrix-AM-Storage: FTU

Content-Length: xxx

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
  <Status>success</Status>
  <Result>more-info</Result>
  <StateContext>f8d463c0</StateContext>
  <AuthenticationRequirements>
    <PostBack>Citrix/Authentication/CustomForms/SendForm</PostBack>
    <CancelPostBack>Citrix/Authentication/CustomForms /CancelForm</CancelPostBack>
    <CancelButtonText>Cancel</CancelButtonText>
    <Requirements>
      <Requirement>
        <Credential>
          <ID>username</ID>
          <Type>username</Type>
        </Credential>
        <Label>
          <Text>User name:</Text>
          <Type>plain</Type>
        </Label>
        <Input>
          <Text />
        </Input>
      </Requirement>
      <Requirement>
        <Credential>
          <ID>password</ID>
          <Type>password</Type>
        </Credential>
        <Label>
          <Text>Password:</Text>
          <Type>plain</Type>
        </Label>
        <Input>
          <Text>
          <Secret>true</Secret>
          </Text>
        </Input>
      </Requirement>
      <Requirement>
        <Credential>
          <ID>nextBtn</ID>
          <Type>none</Type>
        </Credential>
        <Label>
          <Type>none</Type>
        </Label>
        <Input>
          <Button>Sign On</Button>
        </Input>
      </Requirement>
    </Requirements>
  </AuthenticationRequirements>
</AuthenticateResponse>
<!--NeedCopy-->

The authentication service sets a value for the first time.

Client internal storage table
Origin service start URL Storage value
https://example.com:443/Citrix/Authentication/CustomForms/Authenticate FTU

The client stores the value against the authentication service start URL.

Request
POST https://example.com/Citrix/Authentication/CustomForms/SendForm HTTP/1.1

Accept: application/vnd.citrix.requesttokenresponse+xml,application/vnd.citrix.authenticateresponse-1+xml,text/xml

Content-Type: application/x-www-form-urlencoded

Content-Length: xxx

X-Citrix-AM-Storage: FTU

StateContext=f8d463c0&username=user&password=pass&nextBtn=Sign%20On
<!--NeedCopy-->

The client posts back credentials, also sending the latest storage value. Note that the URL for this request is different from the start URL.

Response
HTTP/1.1 200 OK

Content-Type: application/vnd.citrix.requesttokenresponse+xml

Content-Length: xxx

X-Citrix-AM-Storage: FTUDone

<?xml version="1.0" encoding="utf-8"?>

<requesttokenresponse xmlns="http://citrix.com/delivery-services/1-0/auth/requesttokenresponse">
  <for-service>73003cf4-dc21-4bef-9138-af0ec600dba3</for-service>
  <issued>2014-04-07T14:25:51.7525144Z</issued>
  <expiry>2014-04-08T10:25:51.7525144Z</expiry>
  <lifetime>0.20:00:00</lifetime>
  <token-template />
  <token>H4sIAAAAAAAEAK1XaY+…ANAAA=</token>
  <CredentialUpdateInformationList xmlns="http://citrix.com/authentication/response/1">
    <CredentialUpdateInformation>
      <CredentialDisplayName>user</CredentialDisplayName>
      <AuthenticationInfoId>Citrix Common Forms 1.0</AuthenticationInfoId>
      <CredentialUpdateService />
    </CredentialUpdateInformation>
  </CredentialUpdateInformationList>
</requesttokenresponse>
<!--NeedCopy-->

Authentication completes successfully and the authentication service updates the storage value using the Request Token Response.

Client internal storage table
Origin service start URL Storage value
https://example.com:443/Citrix/Authentication/CustomForms/Authenticate FTUDone

The client stores the updated value against the authentication service forms start URL.

Client responses for form input

General user agent processing

When the user submits a form (for example. by activating a logon button), the user agent on the client should process it as follows:

  1. Identify the “successful” controls.
  2. Build a form data set, which is a sequence of control-id=current-value pairs from successful controls separated by the ‘&’ character. Any value containing a ‘&’ character should be correctly encoded as %26.
  3. Encode the data set to comply with the application/x-www-form-urlencoded content type.
  4. POST the data to the host-relative URL specified by the supplied form.

The form contains a <StateContext> element at XPath: /AuthenticateResponse/StateContext. The text value of this element should be returned to the server in the form set data with control-id: StateContext.

The user agent should identify the button activated by adding the button controlId=localized-button-text to the data set to be returned.

Finally the user agent should append to the data set the credential id and current value for all /AuthenticateResponse/AuthenticationRequirements/Requirements/Requirement elements where the following applies:

  • The ./Credential/ID element is present, and not empty.
  • The requirement is not read-only.
  • The ./Input element has a control associated with it.

Once the data set is complete it should be encoded as forms data as described in 17.13.4 Form content types and then sent to the server using a HTTP POST to the host relative URL specified by the /AuthenticateResponse/AuthenticationRequirements/PostBack element. The POST should also set the appropriate cookie header.

Note to client implementors:

Elements that are marked as read-only should not be returned to the server.

Buttons

Where there are multiple buttons defined, only the button selected by the user should be sent in the form response in the form: controlId=localized-button-text

Consider a form containing two buttons:

<Requirement>
  <Credential>
    <ID>backButtonId</ID>
    <Type>none</Type>
  </Credential>
  <Label>
    <Type>none</Type>
  </Label>
  <Input>
    <Button>Back</Button>
  </Input>
</Requirement>
<Requirement>
  <Credential>
    <ID>nextButtonId</ID>
    <Type>none</Type>
  </Credential>
  <Label>
    <Type>none</Type>
  </Label>
  <Input>
    <Button>Next</Button>
  </Input>
</Requirement>
<!--NeedCopy-->

If the Back button is selected, then the POST should contain the back button as follows:

backButtonId=Back

However, if the Next button is selected, then the POST should contain the following: nextButtonId=Next

Text-based input

For text-based input, the text supplied by the user should be sent, appropriately encoded, in the form response in the form: credentialID=user-supplied-text

Consider the form containing the following text credential:

<Requirement>
  <Credential>
    <ID>textId</ID>
    <Type>textcredential</Type>
  </Credential>
  <Label>
    <Text>Generic text</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <Text>
      <Constraint>.+</Constraint>
    </Text>
  </Input>
</Requirement>
<!--NeedCopy-->

The following demonstrates the form response that should be returned for various text inputs:

  • domain\user: textId=domain%5cuser
  • áâäçèé: textId=%c3%a1%c3%a2%c3%a4%c3%a7%c3%a8%c3%a9
  • <empty string>: textId=

Check box

For check boxes, the value returned by the client should be true if the check box is selected and false if not.

Note for client implementors: The true and false values are case-sensitive.

Consider the form containing the following check box:

<Requirement>
  <Credential>
    <ID>checkboxId</ID>
    <Type>none</Type>
  </Credential>
  <Label>
    <Text>Do you consent to this operation?</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <CheckBox>
      <InitialValue>true</InitialValue>
    </CheckBox>
  </Input>
</Requirement>
<!--NeedCopy-->

If the checkbox is selected, then the POST should contain the following:

checkboxId=true

If the check box is not selected, then the POST should contain the following:

checkboxId=false

Save credentials control

Because this has important semantic obligations for the client, there is a distinguished credential type to specify when the client can offer the user the option to save credentials. This allows the server to control whether the option is presented, and to specify the appropriate localized description.

Notes for client implementors:

  • Only credentials with a SaveID tag are permitted to be saved.
  • Clients that don’t support saving credentials should omit the requirement element from display.

Consider the form containing the following save credentials check box.

<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>
<!--NeedCopy-->

If the check box is selected, then the POST should contain the following, as for a normal check box defined above:

saveCredentials=true

Notes for client implementors:

Clear saved credential

If during the authentication conversation the client receives a form with result code “more-info”, which contains the SaveID of a credential that has already been submitted to the server, the client should infer that the credential was not valid. The client should immediately erase the saved credential set. Saved credentials should also be erased if a request to a StoreFront service URL results in a CitrixAuth challenge with reason: badpassword

Update saved credential

During an authentication conversation, the client may receive a form with result code “update-credentials”. This indicates that the user is updating a credential (for example changing a password). If the SaveID of the credential matches that of a saved credential, the new value of the credential should be saved if authentication succeeds.

Radio buttons

For radio buttons, individual items contain localized display text and a non-localized value. Radio buttons should return the value associated with the currently selected item.

Consider the form containing the following radio buttons:

<Requirement>
  <Credential>
    <ID>radioButtonId</ID>
    <Type>textcredential</Type>
  </Credential>
  <Label>
    <Text>Choose one</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <RadioButton>
      <InitialSelection>Choice1</InitialSelection>
      <DisplayValues>
        <DisplayValue>
          <Display>Choice One Display Text</Display>
          <Value>Choice1</Value>
        </DisplayValue>
        <DisplayValue>
          <Display>Choice Two Display Text</Display>
          <Value>Choice2</Value>
        </DisplayValue>
        <DisplayValue>
          <Display>Choice Three Display Text</Display>
          <Value>Choice3</Value>
        </DisplayValue>
      </DisplayValues>
    </RadioButton>
  </Input>
</Requirement>
<!--NeedCopy-->

If the second item (Display Text Two) is selected, then the POST should contain radioButtonId=Choice2.

Note:

For server implementors: It is acceptable for clients to indicate that there is no selection by returning radioButtonId=.

For client implementors: The initial selection element is optional, the client implementor must decide what to do if there is no initial value.

Combo boxes

A combo box allows a single item to be selected. For combo boxes, individual items contain localized display text and a non-localized value. Combo boxes should return the value associated with the currently selected item.

Consider the form containing the following combo box:

<Requirement>
  <Credential>
    <ID>comboId</ID>
    <Type>textcredential</Type>
  </Credential>
  <Label>
    <Text>Combo-box</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <ComboBox>
      <InitialSelection>Value2</InitialSelection>
      <DisplayValues>
        <DisplayValue>
          <Display>Display Text One</Display>
          <Value>Value1</Value>
        </DisplayValue>
        <DisplayValue>
          <Display>Display Text Two</Display>
          <Value>Value2</Value>
        </DisplayValue>
        <DisplayValue>
          <Display>Display Text Three</Display>
          <Value>Value3</Value>
        </DisplayValue>
      </DisplayValues>
    </ComboBox>
  </Input>
</Requirement>
<!--NeedCopy-->

If the second item (Display Text Two) is selected, then the POST should contain the following:

comboId=Value2

Note:

For server implementors:

  • Form implementors should ensure that all display text items and values are unique and not empty.
  • It is acceptable for clients to indicate that there is no selection by returning comboId=.
  • Servers must be robust against invalid values being returned.
  • If the values are changed, it is suggested that any saved credential identifier is also changed to avoid the client sending back invalid saved credentials.

For client implementors:

  • The initial selection element is optional and it is the client implementor’s choice on how this should be handled.

Multi-combo boxes

A multi-combo box allows multiple items to be selected. For multi-combo boxes, individual items contain localized display text, a non-localized value, and a flag indicating whether the item is selected. Multi-combo boxes should return an entry for each selected item of the form: itemID=value.

Consider the form containing the following multi-combo box, where the second item is initially selected:

<Requirement>
  <Credential>
    <ID>multiComboId</ID>
    <Type>textcredential</Type>
  </Credential>
  <Label>
    <Text>Multi-select Combo</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <MultiComboBox>
      <DisplayValues>
        <DisplayValue>
          <Display>Alice</Display>
          <Value>Value1</Value>
        </DisplayValue>
        <DisplayValue>
          <Display>Bob</Display>
          <Value>Value2</Value>
          <Select>true</Select>
        </DisplayValue>
        <DisplayValue>
          <Display>Eve</Display>
          <Value>Value3</Value>
          <Select>false</Select>
        </DisplayValue>
      </DisplayValues>
    </MultiComboBox>
  </Input>
</Requirement>
<!--NeedCopy-->

If the second item and third items (Bob and Eve) are selected, then the POST should contain the following:

multiComboId=Value2&multiComboId=Value3

If no items are selected, then the POST should contain the following:

multiComboId=

Note:

For server implementors:

  • Form implementors should ensure that all display text items and values are unique.
  • Servers must be robust against invalid values being returned.
  • If the values are changed, it is suggested that any saved credential identifier is also changed to avoid the client sending back invalid saved credentials.
  • Multi-combo boxes are not suitable for user name or domain credentials.

For client implementors:

  • It is acceptable for clients to only return selected values that are unique.

X509 certificates

A multi-combo box allows multiple items to be selected. For multi-combo boxes, individual items contain localized display text, a non-localized value, and a flag indicating whether the item is selected. Multi-combo boxes should return an entry for each selected item of the form: itemID=value.

Consider the form containing the following multi-combo box, where the second item is initially selected:

<Requirement>
  <Credential>
    <ID>multiComboId</ID>
    <Type>textcredential</Type>
  </Credential>
  <Label>
    <Text>Multi-select Combo</Text>
    <Type>plain</Type>
  </Label>
  <Input>
    <MultiComboBox>
      <DisplayValues>
        <DisplayValue>
          <Display>Alice</Display>
          <Value>Value1</Value>
        </DisplayValue>
        <DisplayValue>
          <Display>Bob</Display>
          <Value>Value2</Value>
          <Select>true</Select>
        </DisplayValue>
        <DisplayValue>
          <Display>Eve</Display>
          <Value>Value3</Value>
          <Select>false</Select>
        </DisplayValue>
      </DisplayValues>
    </MultiComboBox>
  </Input>
</Requirement>
<!--NeedCopy-->

If the second item and third items (Bob and Eve) are selected, then the POST should contain multiComboId=Value2&multiComboId=Value3.

If no items are selected, then the POST should contain multiComboId=.

Citrix Common forms authentication API