-
Notifications
You must be signed in to change notification settings - Fork 3
Handling authentication issues
This tutorial will show you how to use error branches to intervene when client issues occur. Here, we will be adding a "retry" dialog to the authentication process that we have been refining in the last two tutorials. This will allow users to re-enter their credentials if they type something wrong, etc.
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, Refining the authentication dialog, and have a version of the engine that will start and attempt to contact your web-server.
Using branches to group tasks
Up to this point, we have just been editing two branches, the local initialization
branch and the remote main
branch. It's possible, though, to create any number of sub-branches. One advantage to doing this is that it allows us to encapsulate various processes into corresponding branches. This not only makes the xml a little more readable, but it facilitates handling issues when they occur.
Currently, our authentication process consists of 6 or 7 tasks. It's certainly possible to implement error handling for each of these tasks, but it's much easier to group all of these tasks into a single authentication branch, and just implement error handling on the branch level. Let's do that now.
- Add the following to the
Branches
block of tasklist.xml
<TaskBranch name="authenticate">
</TaskBranch>
We'll add content in the next step, but, for now, this is all that you need to do to initialize a new task branch. The name
attribute is required, as this serves as the key for the branch in the engine's branch collection. Task branch names are case sensitive, so the engine will treat a branch named Authenticate
as different from one named authenticate
. If multiple branches have the same name, the branch that is loaded last will replace those loaded before it.
- Now, let's move all of our authentication tasks from the
main
branch to our newauthentication
branch.
<TaskBranch name="authenticate">
<UserInterface.ShowBorderedBannerModal>
<Properties>
<Dialog>Main dialog</Dialog>
<Banner>LoginBanner</Banner>
</Properties>
</UserInterface.ShowBorderedBannerModal>
<Settings.SetSettingText>
<Properties>
<Setter key="authenticating">true</Setter>
</Properties>
</Settings.SetSettingText>
<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 minimumTaskTime="10">
<Properties>
<UsernameKey>username</UsernameKey>
<PassphraseKey>passphrase</PassphraseKey>
<Credential2Key>credential2</Credential2Key>
<Credential3Key>credential3</Credential3Key>
<Credential4Key>credential4</Credential4Key>
<CertificateProvider>incommontest.org</CertificateProvider>
</Properties>
</Authentication.AuthenticateUser>
</TaskBranch>
- Now, add a
Control.ReturnBranchResult
to the end of themain
task branch:
<Control.ReturnBranchResult>
<Properties>
<Branch>authenticate</Branch>
</Properties>
</Control.ReturnBranchResult>
This xml block will tell the engine to run the authentication
branch and process the result. Here, the Branch
property identifies the branch to run. Again, the key is the name of the branch as defined by the branch's name
attribute.
- 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>
<Control.ReturnBranchResult>
<Properties>
<Branch>authenticate</Branch>
</Properties>
</Control.ReturnBranchResult>
</RoleBranch>
<TaskBranch name="authenticate">
<UserInterface.ShowBorderedBannerModal>
<Properties>
<Dialog>Main dialog</Dialog>
<Banner>LoginBanner</Banner>
</Properties>
</UserInterface.ShowBorderedBannerModal>
<Settings.SetSettingText>
<Properties>
<Setter key="authenticating">true</Setter>
</Properties>
</Settings.SetSettingText>
<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 minimumTaskTime="10">
<Properties>
<UsernameKey>username</UsernameKey>
<PassphraseKey>passphrase</PassphraseKey>
<Credential2Key>credential2</Credential2Key>
<Credential3Key>credential3</Credential3Key>
<Credential4Key>credential4</Credential4Key>
<CertificateProvider>incommontest.org</CertificateProvider>
</Properties>
</Authentication.AuthenticateUser>
</TaskBranch>
</Branches>
</Content>
-
Upload tasklist.xml to your server and run engine.exe. Things should work exactly like before.
-
Now let's add an error-handling branch for our authentication process. This is the branch that should be called if our authentication branch returns an issue. Add the following to the bottom of the
Branches
block of tasklist.xml:
<TaskBranch name="handle authentication issues">
<Control.PreserveLastResult>
<Properties>
<SettingKey>generic issue retry banner stored result</SettingKey>
</Properties>
</Control.PreserveLastResult>
<UserInterface.StopMessageTimer>
<Properties>
<SettingKey>AuthenticatingMessage</SettingKey>
</Properties>
</UserInterface.StopMessageTimer>
<Settings.SetSettingText>
<Properties>
<Setter key="generic issue retry banner title">Authentication Failed - Retry?</Setter>
<Setter key="generic issue retry banner description">!ApplicationTitle! could not verify your network credentials.</Setter>
<Setter key="generic issue retry banner retry cancel text">Click Retry to try again or Cancel to exit.</Setter>
</Properties>
</Settings.SetSettingText>
<UserInterface.ShowChildBannerModal>
<Properties>
<ParentDialog>Main dialog</ParentDialog>
<ChildDialog>notification dialog manager</ChildDialog>
<Banner>Generic issue retry banner</Banner>
</Properties>
</UserInterface.ShowChildBannerModal>
</TaskBranch>
Here, we're using the predefined banner, Generic issue retry banner
, to prompt the user for action. After storing the result that was passed in from the engine, this branch stops the AuthenticatingMessage
timer, sets the settings that determine the retry banner's title and content and then shows the banner.
The Generic issue retry banner
is configured by setting the following settings: generic issue retry banner stored result
stores the result (here the issue result) that was passed into the branch from the engine, and generic issue retry banner title
, generic issue retry banner description
, and generic issue retry banner retry cancel text
determine the banner's title and text.
If users click 'Retry' on the Generic issue retry banner
dialog, the dialog will return a ControlResults.RepeatBranchingTaskResult
, which will cause the branch that raised an issue (here, our authentication
branch) to repeat.
- Now, we need to wire-in our error-handling branch. To do this, add an
errorBranch
attribute to theControl.ReturnBranchResult
task that we added in step 3:
<Control.ReturnBranchResult errorBranch="handle authentication issues">
<Properties>
<Branch>authenticate</Branch>
</Properties>
</Control.ReturnBranchResult>
You can add an errorBranch
attribute to any task. If this attribute it present, the engine will execute the referenced branch if the task in question returns an issue result. The engine will then return the result of the error branch instead of the original issue result.
In the case of our authentication process, for example, if the authentication branch returns an issue result, the engine will run our handle authentication issues
branch. This branch will either return ControlResults.RepeatBranchingTaskResult
, which will cause the authentication
branch to repeat, or the original issue passed to the error branch, which will cause the engine to raise its error dialog and exit.
- Your tasklist.xml 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">
<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>
<Control.ReturnBranchResult errorBranch="handle authentication issues">
<Properties>
<Branch>authenticate</Branch>
</Properties>
</Control.ReturnBranchResult>
</RoleBranch>
<TaskBranch name="authenticate">
<UserInterface.ShowBorderedBannerModal>
<Properties>
<Dialog>Main dialog</Dialog>
<Banner>LoginBanner</Banner>
</Properties>
</UserInterface.ShowBorderedBannerModal>
<Settings.SetSettingText>
<Properties>
<Setter key="authenticating">true</Setter>
</Properties>
</Settings.SetSettingText>
<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 minimumTaskTime="10">
<Properties>
<UsernameKey>username</UsernameKey>
<PassphraseKey>passphrase</PassphraseKey>
<Credential2Key>credential2</Credential2Key>
<Credential3Key>credential3</Credential3Key>
<Credential4Key>credential4</Credential4Key>
<CertificateProvider>incommontest.org</CertificateProvider>
</Properties>
</Authentication.AuthenticateUser>
</TaskBranch>
<TaskBranch name="handle authentication issues">
<Control.PreserveLastResult>
<Properties>
<SettingKey>generic issue retry banner stored result</SettingKey>
</Properties>
</Control.PreserveLastResult>
<UserInterface.StopMessageTimer>
<Properties>
<SettingKey>AuthenticatingMessage</SettingKey>
</Properties>
</UserInterface.StopMessageTimer>
<Settings.SetSettingText>
<Properties>
<Setter key="generic issue retry banner title">Authentication Failed - Retry?</Setter>
<Setter key="generic issue retry banner description">!ApplicationTitle! could not verify your network credentials.</Setter>
<Setter key="generic issue retry banner retry cancel text">Click Retry to try again or Cancel to exit.</Setter>
</Properties>
</Settings.SetSettingText>
<UserInterface.ShowChildBannerModal>
<Properties>
<ParentDialog>Main dialog</ParentDialog>
<ChildDialog>notification dialog manager</ChildDialog>
<Banner>Generic issue retry banner</Banner>
</Properties>
</UserInterface.ShowChildBannerModal>
</TaskBranch>
</Branches>
</Content>
- Upload tasklist.xml to your server and run engine.exe. If you enter invalid credentials, you should now be prompted to retry the authentication process:
If you hit cancel, or close the dialog, the engine should raise its error dialog:
But if you hit retry, the engine should return you to the authentication dialog:
Things aren't perfect here, though. The instructions block is missing and the "Login" button will not enable. This is because we collapsed the instructions text and set the setting "authenticating" to true after the user initially clicked "Login."
- To fix these things, modify the
authenticate
branch to 'reset' the authentication dialog before it is shown:
<TaskBranch name="authenticate">
<UserInterface.StopMessageTimer>
<Conditions.Any>
<UserInterface.TimedMessageExists key="AuthenticatingMessage"/>
</Conditions.Any>
<Properties>
<SettingKey>AuthenticatingMessage</SettingKey>
</Properties>
</UserInterface.StopMessageTimer>
<UserInterface.HideBannerControl>
<Conditions.Any>
<UserInterface.BannerControlExists key="AuthenticatingMessage" dialog="Main dialog"/>
</Conditions.Any>
<Properties>
<Dialog>Main dialog</Dialog>
<ControlKey>AuthenticatingMessage</ControlKey>
</Properties>
</UserInterface.HideBannerControl>
<UserInterface.ShowBannerControl>
<Conditions.Any>
<UserInterface.BannerControlExists key="Instructions" dialog="Main dialog"/>
</Conditions.Any>
<Properties>
<Dialog>Main dialog</Dialog>
<ControlKey>Instructions</ControlKey>
</Properties>
</UserInterface.ShowBannerControl>
<Settings.SetSettingText>
<Properties>
<Setter key="authenticating">false</Setter>
</Properties>
</Settings.SetSettingText>
<UserInterface.ShowBorderedBannerModal>
<Properties>
<Dialog>Main dialog</Dialog>
<Banner>LoginBanner</Banner>
</Properties>
</UserInterface.ShowBorderedBannerModal>
<Settings.SetSettingText>
<Properties>
<Setter key="authenticating">true</Setter>
</Properties>
</Settings.SetSettingText>
<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 minimumTaskTime="10">
<Properties>
<UsernameKey>username</UsernameKey>
<PassphraseKey>passphrase</PassphraseKey>
<Credential2Key>credential2</Credential2Key>
<Credential3Key>credential3</Credential3Key>
<Credential4Key>credential4</Credential4Key>
<CertificateProvider>incommontest.org</CertificateProvider>
</Properties>
</Authentication.AuthenticateUser>
</TaskBranch>
Here, we've added four tasks to the start of our authenticating
branch. The first, UserInterface.StopMessageTimer
stops the authenticating message timer if it exists; the next two, UserInterface.HideBannerControl
and UserInterface.ShowBannerControl
hide the authenticating paragraph and show the instruction paragraph; the last task, Settings.SetSettingText
sets the authenticating
temporary setting to false.
Note that the UserInterface.StopMessageTimer
, UserInterface.HideBannerControl
, and UserInterface.ShowBannerControl
task blocks all specify conditions to test whether the targets are defined. We do this because these elements are only defined after LoginBanner
is loaded for the first time. None of these tasks will fail if the specified elements are not yet defined, but many will raise warnings in the event log. Hence the conditions.
- Upload tasklist.xml to your server and run the engine again. The authentication dialog should now behave correctly if users click 'Retry.'
Conclusion
Our authentication dialog is now, for the most part, complete. It activates correctly, displays the correct processing messages, and allows users to retry in the case of an authentication issue. There are still some polishing to be done, though. It would be nice, for example, if the engine would remember the user's username. We'll cover this and some other small improvements in the next tutorial.