Handling Application Startup
Overview
When we created the application for the getting started tutorial we made sure Identity Vault was properly initialized before we used it. However, we just jumped right into the first tab of the main part of the application without a startup or login process.
The Login Page
Our application currently just starts up in the application itself and we have a button that the user can press to store the authentication information in the vault. This is not realistic. Our application should have a page where the user logs in.
In our case, this will still just be a button that the user presses to fake a log in, but we are getting a step closer to an actual flow by having the login page.
The Startup Flow
When our application starts, the session can be in one of the following states:
- Locked:
- With valid authentication tokens.
- With invalid authentication tokens.
- Not logged in.
If the application is locked, the application shall give the user the opportunity to unlock the vault. If the unlock fails, the user shall be given the option to either try again or to clear the session data and log in again.
If the user unlocks the vault and the resulting authentication information is valid, the first tab shall be loaded. For our tutorial application, if we have session data the session is, by definition, valid.
If the user unlocks the vault and the resulting authentication information is expired, the login page shall be loaded. Having expired or otherwise invalid authentication information is not technically possible in our tutorial application, but we will code for it none the less.
If the user is not logged in, the login page shall be loaded.
The Unlock on Resume Flow
It is also possible for the vault to lock while the application is in the background. In reality, Identity Vault detects
that the application has resumed and will lock the vault if more than lockAfterBackgrounded
milliseconds have passed.
One flow we may want to implement in this case is to give the user the opportunity to unlock the vault immediately upon
receiving an onLock
event for the vault. In this scenario, the user stays on whichever page they were on when the app locked.
If the user cancels the unlock, however, we should navigate to an "Unlock" page where we give the user various options as to what they would like to do next (these are usually to attempt to unlock again, or to log out and back in to the application).
If you are using lockAfterBackgrounded
do not interact with the vault in a resume
event handler. Doing so will cause a
race condition resulting in indeterminate behavior. Always manage the state of the vault through the onLock
and onUnlock
event handlers, never the resume
event.
Let's Code
We will build upon the application we created in the getting started tutorial in order to implement the basic application startup workflow along with the unlocking upon resume flow.
If you have the code from when you performed the getting started tutorial, then you are good to go. If you need the code you can make a copy from our GitHub repository.
Generate New Pages
In order to implement our startup and authentication strategies, we need to create some new pages:
LoginPage
: Handle the our simple login flow.StartPage
: The "default" route page to use on startup. This page will determine if the user needs to login, try to unlock a vault, go directly into the application.UnlockPage
: Provide the user with options if they cancel an unlock attempt.
Generate these pages.
Update Routes
With the new pages in place, the routing needs to be fixed. The application's routing scheme has two levels: a base
page level and a sub-page level. As such, each of our routes has one of the following formats: /base-page
or
/base-page/sub-page
.
At the base page level, we want to have four different pages: TabsPage
, LoginPage
, StartPage
, and UnlockPage
.
We also want the default route (/
) to be the StartPage
. Update the src/app/app.routes.ts
file to:
- Define the
/tabs
route. - Define the
/login
route. - Define the
/start
route. - Define the
/unlock
route. - Create a redirect from
/
to/start
.
The TabsPage
(route: /tabs
) has sub-pages. The sub-pages are already set up, but we need to make the following
adjustments to src/app/tabs/tabs.routes.ts
:
- Remove the redirect for
/
since it was moved tosrc/app/app.routes.ts
. - Change the main path from
tabs
(which is now defined insrc/app/app.routes.ts
) to an empty string.
The AuthenticationService
Part of our startup strategy involves authentication. We will not really be performing authentication, but we will add the service so that we have the infrastructure in place so we can later add authentication via a solution such as Auth Connect.
Generate the authentication service.
An empty service is created.
Inject the SessionVaultService
.
The user needs to be able to log in. Since we do not yet have an authentication strategy, we will store a fake session.
For the logout()
, just clear the stored session.
To determine if the user is authenticated, check for a stored session.
Generate the authentication service.
An empty service is created.
Inject the SessionVaultService
.
The user needs to be able to log in. Since we do not yet have an authentication strategy, we will store a fake session.
For the logout()
, just clear the stored session.
To determine if the user is authenticated, check for a stored session.
We now have an AuthenticationService
that we can use in the rest of our app. We also have a service that we can
update to add our actual authentication services using a solution such as Auth Connect.
The LoginPage
The login page simply includes a "Login" button.
When the button is pressed the following tasks are performed:
- Attempt to log in.
- If the login succeeds, go to the
Tab1Page
. - If the login fails we will just log it for now. When actual authentication is implemented this may be a good place to display a "Login Failed" message, but that is beyond the scope of this tutorial.
Update the SessionVaultService
The startup logic needs to determine if the vault is currently locked and provide a mechanism to unlock the vault
if it is locked. Update the SessionVaultService
to provide unlock()
and isLocked()
methods.
In the isLocked()
method, we are ignoring the actual state for SecureStorage
or InMemory
type vaults because
Identity Vault will report them as "locked" even though they logically cannot lock. This is a long standing quirk
with Identity Vault that would be a breaking change to fix.
The StartPage
When the application starts, it will be in one of two states:
- Logged out: the user needs to be redirected to the login page.
- Logged in and locked: the user should be given the opportunity to unlock the app.
As such, the StartPage
does not contain any real user interaction so the UI can be minimal. You could show a spinner,
or your company logo, or something like that. For our simple app we will just show a blank page.
We can also trim the component code down to just a minimal Component
class which will make it easier to build it back up again.
With the basics in place, let's implement the rest of the logic.
As noted above, the page may attempt an unlock operation, and it will navigate somewhere based on the current authentication state.
If the vault is locked try to unlock it. If the unlock fails (usually due to the user canceling the attempt), then navigate to the "Unlock" page so the user can decide the best course of action.
If the vault is unlocked (either due to not having been locked or the user successfully unlocking it), determine if we should navigate to
the LoginPage
or the Tab1Page
based on the current authentication status.
As noted above, the page may attempt an unlock operation, and it will navigate somewhere based on the current authentication state.
If the vault is locked try to unlock it. If the unlock fails (usually due to the user canceling the attempt), then navigate to the "Unlock" page so the user can decide the best course of action.
If the vault is unlocked (either due to not having been locked or the user successfully unlocking it), determine if we should navigate to
the LoginPage
or the Tab1Page
based on the current authentication status.
The UnlockPage
The UnlockPage
is only visited if the user chooses to cancel the unlocking of the vault. The purpose of this page is to give the user
reasonable options for what they can try next: either re-try the unlock or completely redo their login.
We start with the template page code
At this point, the user has two options: try to unlock the vault again or redo the login.
Add the components we are using as well as shells for the click event handlers.
To redo the login, logout and then navigate to the Login Page.
Try to unlock the vault and navigate to the first page of the app if successful.
We start with the template page code
At this point, the user has two options: try to unlock the vault again or redo the login.
Add the components we are using as well as shells for the click event handlers.
To redo the login, logout and then navigate to the Login Page.
Try to unlock the vault and navigate to the first page of the app if successful.
At this point the UnlockPage
is functionally complete, but the OnInit
logic is not being used and should be removed. Doing so is left
as an exercise for the reader.
Handle a Vault Locking In-App
The vault will lock upon resume if the application has been in the background more than lockAfterBackgrounded
milliseconds.
In our case, that is 2000 milliseconds. When this happens, it would be nice to initiate an initial unlock attempt without navigating
the user away from wherever they are in the app. The AppComponent
is a good centralized location from which to handle this logic.
Cleanup the Tab1Page
This step is completely optional. The tutorial application will work perfectly fine with the code as-is. There are
several items in the Tab1Page
that no longer make sense, however, and now is a good time to clean those up. Here is
a synopsis of what can be cleaned up:
- Remove the "Store" button and all code associated with it.
- Change the "Clear" button to a "Logout" button and update the click handler accordingly.
- Remove the "Unlock" button and all code associated with it.
- Remove the code that subscribes to the
locked$
observable and clears the displayed session data when the vault is locked. This is no longer needed because we navigate away from the page entirely when the vault locks.
Cleaning this all up is left as an exercise to the reader but we provide the completed code here for you to compare against.
Next Steps
In this tutorial, we created a basic application startup workflow. This is an example of a good workflow, but it is not the only potential flow. Tailor it for your application.