Skip to content

Refining the authentication dialog

Brent Moberly edited this page Jan 12, 2015 · 1 revision

Overview

This tutorial will guide you through the process of customizing the authentication dialog that we created in the last tutorial. Specifically, it will demonstrate how to use actions and conditions to enable or disable dialog controls and how to use user interface tasks to signal that authentication is in process.

Requirements

In order to complete this tutorial, you will need the following:

  • A web-server to host your content files
  • An operational InCert server
  • An xml or text editor

Setup

This tutorial assumes that you have created the tutorial, Creating Remote Content And Authenticating Users, and have a version of the engine that will start and attempt to contact your web-server.

Where we left off

In the last tutorial, we created an authentication dialog, but it was missing several key features:

  • Users should not be able to click the dialog's "Login" button without providing credentials.
  • Users should not be able to interact with the dialog while authentication is in progress.
  • The dialog should signal that authentication is in progress.

Enabling and Disabling the "Login" button if fields are empty

  1. The first thing that we need to do is to disable the authentication banner's "Login" button if one of its fields is empty. To do this, update banners.xml to the following:
<Content xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://incert.incommon.org/schemas ../Schemas/tasklist.xsd">
  <Banners>
    <SimpleBanner name="LoginBanner" height="550" width="500">
      <Content>
        <SimpleParagraph margin="0,36,0,36" fontSize="24" alignment="Center">
          <Content>
            <DirectTextContent>!ApplicationTitle! Login</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleParagraph>
          <Content>
            <DirectTextContent>Username:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleInputField settingKey="username" controlKey="username"/>
        <SimpleParagraph margin="0,8,0,0">
          <Content>
            <DirectTextContent>Passphrase:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <PasswordInputField settingKey="passphrase" controlKey="passphrase"/>
        <SimpleParagraph margin="0,8,0,0">
          <Content>
            <DirectTextContent>Credential 2:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleInputField settingKey="credential2"/>
        <SimpleParagraph margin="0,8,0,0">
          <Content>
            <DirectTextContent>Credential 3:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleInputField settingKey="credential3"/>
        <SimpleParagraph margin="0,8,0,0">
          <Content>
            <DirectTextContent>Credential 4:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleInputField settingKey="credential4"/>
        <SimpleParagraph controlKey="Instructions" margin="0,12,0,0">
          <Content>
            <DirectTextContent>Please provide your network credentials and click Login to continue.</DirectTextContent>
          </Content>
        </SimpleParagraph>
      </Content>
      <Buttons>
        <ResultButton>
          <Target>NextButton</Target>
          <Text>Login</Text>
          <IsDefaultButton>true</IsDefaultButton>
          <Result>ControlResults.NextResult</Result>
        </ResultButton>
        <DisabledButton>
          <Target>BackButton</Target>
          <Text>Back</Text>
        </DisabledButton>
        <UrlButton>
          <Target>HelpButton</Target>
          <Text>Help</Text>
          <Value>https://certdev0.incommontest.org/incommon/index.html</Value>
        </UrlButton>
      </Buttons>
      <Actions>
        <DisableControlAction>
          <ControlKey>NextButton</ControlKey>
        </DisableControlAction>
      </Actions>
    </SimpleBanner>
  </Banners>
</Content>

Here, we've added an action to disable our banner's "Login" button. This action will fire when the banner is loaded and whenever a change is made to one of the banner's elements.

  1. Upload banners.xml to your web-server and run your version of the engine. The authentication dialog's "Login" button should now be disabled.

login dialog

  1. This is not quite the behavior we want. We only want the "Login" button to be disabled if one or more of the banner's field are empty. To do this, we need to add some conditions to our action:
<DisableControlAction>
  <Conditions.Any>
    <Settings.SettingNotPresent key="username" />
    <Settings.SettingNotPresent key="passphrase" />
    <Settings.SettingNotPresent key="credential2" />
    <Settings.SettingNotPresent key="credential3" />
    <Settings.SettingNotPresent key="credential4" />
  </Conditions.Any>
  <ControlKey>NextButton</ControlKey>
</DisableControlAction>

You can use conditions to determine whether tasks or actions execute. There are two important things to note about the above:

  • We are using five instances of the Settings.SettingNotPresent condition. Each instance will return true if the condition is satisfied (which is to say, that there is no entry in the engine's setting store for the provided key) or false otherwise.
  • Our conditions are children of a 'Conditions.Any' tag. This means that the action will fire if any of the conditions is true. The ultimate effect will be that our banner's "Login" button will be disabled in any of its fields are blank.
  1. We also need to include code to re-enable the "Login" button. To do this, add the following action block to our banner:
<EnableControlAction>
  <Conditions.All>
    <Settings.SettingPresent key="username" />
    <Settings.SettingPresent key="passphrase" />
    <Settings.SettingPresent key="credential2" />
    <Settings.SettingPresent key="credential3" />
    <Settings.SettingPresent key="credential4" />
  </Conditions.All>
  <ControlKey>NextButton</ControlKey>
</EnableControlAction>

This action block is essentially the opposite of the above, employing five Settings.SettingPresent conditions wrapped in a Conditions.All parent. The end result is that the banner's "Login" button will be enabled if all five of its banner fields have content.

Your banners.xml file should now be as follows:

<Content xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://incert.incommon.org/schemas ../Schemas/tasklist.xsd">
  <Banners>
    <SimpleBanner name="LoginBanner" height="550" width="500">
      <Content>
        <SimpleParagraph margin="0,36,0,36" fontSize="24" alignment="Center">
          <Content>
            <DirectTextContent>!ApplicationTitle! Login</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleParagraph>
          <Content>
            <DirectTextContent>Username:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleInputField settingKey="username" controlKey="username"/>
        <SimpleParagraph margin="0,8,0,0">
          <Content>
            <DirectTextContent>Passphrase:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <PasswordInputField settingKey="passphrase" controlKey="passphrase"/>
        <SimpleParagraph margin="0,8,0,0">
          <Content>
            <DirectTextContent>Credential 2:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleInputField settingKey="credential2"/>
        <SimpleParagraph margin="0,8,0,0">
          <Content>
            <DirectTextContent>Credential 3:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleInputField settingKey="credential3"/>
        <SimpleParagraph margin="0,8,0,0">
          <Content>
            <DirectTextContent>Credential 4:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleInputField settingKey="credential4"/>
        <SimpleParagraph controlKey="Instructions" margin="0,12,0,0">
          <Content>
            <DirectTextContent>Please provide your network credentials and click Login to continue.</DirectTextContent>
          </Content>
        </SimpleParagraph>
      </Content>
      <Buttons>
        <ResultButton>
          <Target>NextButton</Target>
          <Text>Login</Text>
          <IsDefaultButton>true</IsDefaultButton>
          <Result>ControlResults.NextResult</Result>
        </ResultButton>
        <DisabledButton>
          <Target>BackButton</Target>
          <Text>Back</Text>
        </DisabledButton>
        <UrlButton>
          <Target>HelpButton</Target>
          <Text>Help</Text>
          <Value>https://certdev0.incommontest.org/incommon/index.html</Value>
        </UrlButton>
      </Buttons>
      <Actions>
        <DisableControlAction>
          <Conditions.Any>
            <Settings.SettingNotPresent key="username" />
            <Settings.SettingNotPresent key="passphrase" />
            <Settings.SettingNotPresent key="credential2" />
            <Settings.SettingNotPresent key="credential3" />
            <Settings.SettingNotPresent key="credential4" />
          </Conditions.Any>
          <ControlKey>NextButton</ControlKey>
        </DisableControlAction>
        <EnableControlAction>
            <Conditions.All>
              <Settings.SettingPresent key="username" />
              <Settings.SettingPresent key="passphrase" />
              <Settings.SettingPresent key="credential2" />
              <Settings.SettingPresent key="credential3" />
              <Settings.SettingPresent key="credential4" />
            </Conditions.All>
            <ControlKey>NextButton</ControlKey>
        </EnableControlAction>
      </Actions>
    </SimpleBanner>
  </Banners>
</Content>
  1. Upload banners.xml to your web-server and run the engine. The authentication dialog's "Login" button should now be disabled if one or more of its fields is empty.

login dialog login dialog

Informing users that authentication is in process

Now that we have the "Login" button working properly, we need to inform users that authentication is in process after they click the "Login" button. To do this, we will use a combination of control actions and tasks.

There are a lot of ways we can handle the authentication process. For now, let's add a progress message to our dialog and show this message while authentication is pending while disabling the dialog's other controls

  1. Add this ProgressParagraph block to your login banner's content block:

Please wait while !ApplicationTitle! verifies your network credentials ```

When active, the ProgressParagraph will append a series of dots to its base text. Note that the ProgressParagraph has both settingKey and controlKey attributes. These values as used to enable or disable the paragraph and change its content dynamically.

  1. We don't want our ProgressParagraph to be visible by default. Add this HideControlAction action to our banner's Actions block:
<HideControlAction onetime="true">
  <ControlKey>AuthenticatingMessage</ControlKey>
</HideControlAction>

The ControlKey value tells the engine to perform the action on our ProgressParagraph. We're using the onetime="true" attribute to tell the engine only to perform the action once. We only need to hide the control when the banner is initialized. There are no conditions here, so this action will always fire, but only once when the banner is initialized.

  1. Your banners.xml file should now be as follows:
<Content xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://incert.incommon.org/schemas ../Schemas/tasklist.xsd">
  <Banners>
    <SimpleBanner name="LoginBanner" height="550" width="500">
      <Content>
        <SimpleParagraph margin="0,36,0,36" fontSize="24" alignment="Center">
          <Content>
            <DirectTextContent>!ApplicationTitle! Login</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleParagraph>
          <Content>
            <DirectTextContent>Username:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleInputField settingKey="username" controlKey="username"/>
        <SimpleParagraph margin="0,8,0,0">
          <Content>
            <DirectTextContent>Passphrase:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <PasswordInputField settingKey="passphrase" controlKey="passphrase"/>
        <SimpleParagraph margin="0,8,0,0">
          <Content>
            <DirectTextContent>Credential 2:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleInputField settingKey="credential2"/>
        <SimpleParagraph margin="0,8,0,0">
          <Content>
            <DirectTextContent>Credential 3:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleInputField settingKey="credential3"/>
        <SimpleParagraph margin="0,8,0,0">
          <Content>
            <DirectTextContent>Credential 4:</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <SimpleInputField settingKey="credential4"/>
        <SimpleParagraph controlKey="Instructions" margin="0,12,0,0">
          <Content>
            <DirectTextContent>Please provide your network credentials and click Login to continue.</DirectTextContent>
          </Content>
        </SimpleParagraph>
        <ProgressParagraph settingKey="AuthenticatingMessage" controlKey="AuthenticatingMessage" margin="0,12,0,0">
          <Content>
            <DirectTextContent>Please wait while !ApplicationTitle! verifies your network credentials</DirectTextContent>
          </Content>
        </ProgressParagraph>
      </Content>
      <Buttons>
        <ResultButton>
          <Target>NextButton</Target>
          <Text>Login</Text>
          <IsDefaultButton>true</IsDefaultButton>
          <Result>ControlResults.NextResult</Result>
        </ResultButton>
        <DisabledButton>
          <Target>BackButton</Target>
          <Text>Back</Text>
        </DisabledButton>
        <UrlButton>
          <Target>HelpButton</Target>
          <Text>Help</Text>
          <Value>https://certdev0.incommontest.org/incommon/index.html</Value>
        </UrlButton>
      </Buttons>
      <Actions>
        <DisableControlAction>
          <Conditions.Any>
            <Settings.SettingNotPresent key="username" />
            <Settings.SettingNotPresent key="passphrase" />
            <Settings.SettingNotPresent key="credential2" />
            <Settings.SettingNotPresent key="credential3" />
            <Settings.SettingNotPresent key="credential4" />
          </Conditions.Any>
          <ControlKey>NextButton</ControlKey>
        </DisableControlAction>
        <EnableControlAction>
            <Conditions.All>
              <Settings.SettingPresent key="username" />
              <Settings.SettingPresent key="passphrase" />
              <Settings.SettingPresent key="credential2" />
              <Settings.SettingPresent key="credential3" />
              <Settings.SettingPresent key="credential4" />
            </Conditions.All>
            <ControlKey>NextButton</ControlKey>
        </EnableControlAction>
        <HideControlAction onetime="true">
          <ControlKey>AuthenticatingMessage</ControlKey>
        </HideControlAction>
      </Actions>
    </SimpleBanner>
  </Banners>
</Content>
  1. Upload banners.xml to your web server and re-run the engine. You should not see the progress paragraph.

login dialog

  1. When the user clicks the Login button, the authentication dialog returns a NextResult and the engine executes its next task, which is currently Autentication.AuthenticateUser. In tasklist.xml, add these four tasks between the UserInterface.ShowBorderedBannerModal and the Autentication.AuthenticateUser tasks:
<UserInterface.DisableAllBannerDialogControls>
  <Properties>
    <Dialog>Main dialog</Dialog>
    <ExcludeControlKey>HelpButton</ExcludeControlKey>
    <ExcludeControlKey>AuthenticatingMessage</ExcludeControlKey>
  </Properties>
</UserInterface.DisableAllBannerDialogControls>

<UserInterface.CollapseBannerControl>
  <Properties>
    <Dialog>Main dialog</Dialog>
    <ControlKey>Instructions</ControlKey>
  </Properties>
</UserInterface.CollapseBannerControl>

<UserInterface.ShowBannerControl>
  <Properties>
    <Dialog>Main dialog</Dialog>
    <ControlKey>AuthenticatingMessage</ControlKey>
  </Properties>
</UserInterface.ShowBannerControl>

<UserInterface.StartMessageTimer>
  <Properties>
    <SettingKey>AuthenticatingMessage</SettingKey>
  </Properties>
</UserInterface.StartMessageTimer>

Here, we are first disabling all of the authentication dialog's controls except the HelpButton using the UserInterface.DisableAllBannerDialogControls task. We're also not disabling our AuthenticatingMessage progress paragraph because we don't want its text to be grayed-out when it becomes visible.

Next, we hide the dialog's Instructions paragraph using UserInterface.CollapseBannerControl, which both makes the paragraph invisible and "collapses" the paragraph so that it no longer takes up vertical space on the dialog.

We then use UserInterface.ShowBannerControl to show our ProgressParagraph. UserInterface.StartMessageTimer tells our ProgressParagraph to start animating.

  1. Your tasklist.xml file should be as follows:
<Content xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://incert.incommon.org/schemas ../Schemas/tasklist.xsd">
  <Branches>
    <RoleBranch name="main" role="Remote" roleMode="Normal">
      <Control.GetContentFromEndpoint>
        <Properties>
          <ContentName>banners.xml</ContentName>
        </Properties>
      </Control.GetContentFromEndpoint>
      <UserInterface.StopMessageTimer>
        <Properties>
          <SettingKey>Splash screen progress text</SettingKey>
        </Properties>
      </UserInterface.StopMessageTimer>
      <UserInterface.HideDialog>
        <Properties>
          <Dialog>Splash screen dialog</Dialog>
        </Properties>
      </UserInterface.HideDialog>
      <UserInterface.ShowBorderedBannerModal>
        <Properties>
          <Dialog>Main dialog</Dialog>
          <Banner>LoginBanner</Banner>
        </Properties>
      </UserInterface.ShowBorderedBannerModal>
      <UserInterface.DisableAllBannerDialogControls>
        <Properties>
          <Dialog>Main dialog</Dialog>
          <ExcludeControlKey>HelpButton</ExcludeControlKey>
          <ExcludeControlKey>AuthenticatingMessage</ExcludeControlKey>
        </Properties>
      </UserInterface.DisableAllBannerDialogControls>
      <UserInterface.CollapseBannerControl>
        <Properties>
          <Dialog>Main dialog</Dialog>
          <ControlKey>Instructions</ControlKey>
        </Properties>
      </UserInterface.CollapseBannerControl>
      <UserInterface.ShowBannerControl>
        <Properties>
          <Dialog>Main dialog</Dialog>
          <ControlKey>AuthenticatingMessage</ControlKey>
        </Properties>
      </UserInterface.ShowBannerControl>
      <UserInterface.StartMessageTimer>
        <Properties>
          <SettingKey>AuthenticatingMessage</SettingKey>
        </Properties>
      </UserInterface.StartMessageTimer>
      <Authentication.AuthenticateUser>
        <Properties>
          <UsernameKey>username</UsernameKey>
          <PassphraseKey>passphrase</PassphraseKey>
          <Credential2Key>credential2</Credential2Key>
          <Credential3Key>credential3</Credential3Key>
          <Credential4Key>credential4</Credential4Key>
          <CertificateProvider>incommontest.org</CertificateProvider>
        </Properties>
      </Authentication.AuthenticateUser>
    </RoleBranch>
  </Branches>
</Content>
  1. Upload tasklist.xml to your web-server and run the engine. The authentication dialog should now disable itself and show our animated progress message when the user clicks "Login."

login dialog

At this point, things are almost perfect, but if you look closely, you'll notice that the "Login" button gets disabled and then re-enabled. This happens because we have a control action that tells the banner to re-enable the "Login" button if all of its text fields have content. The result is that the "Login" button gets disabled when we tell the dialog (via the UserInterface.DisableAllBannerDialogControls task) to disable all of its controls, but then the EnableControlAction fires again and because all of its conditions are met, re-enables the "Login" button.

  1. To fix this, we need to alter the conditions that determine whether EnableControlAction and DisableControlAction fire:
<DisableControlAction>
  <Conditions.Any>
    <Settings.SettingNotPresent key="username" />
    <Settings.SettingNotPresent key="passphrase" />
    <Settings.SettingNotPresent key="credential2" />
    <Settings.SettingNotPresent key="credential3" />
    <Settings.SettingNotPresent key="credential4" />
    <Settings.SettingEquals key="authenticating" value="true" />
  </Conditions.Any>
  <ControlKey>NextButton</ControlKey>
</DisableControlAction>

<EnableControlAction>
  <Conditions.All>
    <Settings.SettingPresent key="username" />
    <Settings.SettingPresent key="passphrase" />
    <Settings.SettingPresent key="credential2" />
    <Settings.SettingPresent key="credential3" />
    <Settings.SettingPresent key="credential4" />
    <Settings.SettingNotEqual key="authenticating" value="true" />
 </Conditions.All>

We've now changed the conditions driving each action so that the "Login" button will be disabled if any one of the settings associated with the dialog's fields are empty or if the settings value for the key authenticating is true. If none of the field values are blank and the authenticating is false, then we will enable the "Login" button.

  1. We also will need to introduce a task to set the value of authenticating. To do this, add the following task block immediately after UserInterface.ShowBorderedBannerModal:
<Settings.SetSettingText>
  <Properties>
    <Setter key="authenticating">true</Setter>
  </Properties>
</Settings.SetSettingText>

This task block will set authenticating to 'true' in the engine's temporary settings store, and this, in turn, will prevent EnableControlAction from re-enabling the "Login" button.

  1. Upload tasklist.xml and banners.xml to your web-server and run the engine again. This time, the "Login" button should stay disabled after you enter your credentials and click "Login."

login dialog

Conclusion

Our authentication dialog is now a lot better than it was when we started our tutorial. Users cannot click its "Login" button until they have completed all of its fields, and we now inform users that authentication is in progress.

There is still some work to be done, though. If users enter invalid credentials, the engine raises and error dialog and exits. This is a bit draconian. In the next tutorial, we will had error handling to our authentication dialog to give users the option of re-entering their credentials without having to restart the engine.