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 app. This is not a realistic experience for our users. Let's implement something more realistic.
The Login Page
Our application currently just starts right 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.
We will build upon the application we created in the getting started tutorial in order to implement a basic application startup workflow.
Let's Code
As mentioned previously, this tutorial builds upon the application created when doing the getting started tutorial. If you have the code from when you performed that tutorial, then you are good to go. If you need the code you can make a copy from our GitHub repository.
Create the New Pages
In order to implement our startup and authentication strategies, we need to have a LoginPage
. We will also replace
the "default" page (currently the Tab1Page
) with a StartPage
that will contain our startup logic.
Create basic shells for these pages.
Be sure to check the code for both pages. The StartPage
is very minimal and that is intentional.
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 three different pages: TabsPage
, LoginPage
, and StartPage
. We also want
the default route (/
) to be the StartPage
. Update the src/router/index.ts
accordingly.
Currently we only have routes for the TabsPage
and its children.
Lazy-load the TabsPage
.
Eager-load the StartPage
and change the /
to redirect to it.
Lazy-load the LoginPage
.
Currently we only have routes for the TabsPage
and its children.
Lazy-load the TabsPage
.
Eager-load the StartPage
and change the /
to redirect to it.
Lazy-load the LoginPage
.
The useAuthentication
Composable
Part of our startup strategy involves authentication. We will not really be performing authentication, but we will add the composable so that we have the infrastructure in place so we can later add authentication via a solution such as Auth Connect.
Start with an empty composable.
Get the functions and data that we need from the useSessionVault
composable.
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.
Start with an empty composable.
Get the functions and data that we need from the useSessionVault
composable.
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 a useAuthentication
composable that we can use in the rest of our app. This also gives us the hooks we need when we begin
using an actual authentication 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 useSessionVault
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 useSessionVault
composable to provide unlockSession()
and sessionIsLocked()
functions.
In the sessionIsLocked()
method, we do not get the reported state for vaults of type SecureStorage
or InMemory
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
We will start with this requirement: 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.
This may seem like an odd place to start, but it is the only requirement that involves the look and feel of the
StartPage
, so let's get that established first.
We are only conditionally showing the "Unlock" and "Redo Login" buttons. For now, we will hard code the condition to not display these buttons. With the basics in place, let's implement the rest of the logic.
We are only changing the <script>
block. The <template>
block stays the same as above. It is elided in the following
code for simplicity.
For the unlock flow, we will first attempt an unlock, and then see if we can navigate.
Perform this flow automatically after the user navigates to this page as well as via the "Unlock" button.
We will only attempt the unlock operation if the vault is actually locked. Try to unlock the vault. If the unlock fails, set the "show" flag so the user can try again or give up and go back to the login step.
If the user succeeded in unlocking the vault, determine if we should navigate to the LoginPage
or the Tab1Page
based on the current authentication status.
If the user chooses to redo the login, logout and navigate to the LoginPage
.
For the unlock flow, we will first attempt an unlock, and then see if we can navigate.
Perform this flow automatically after the user navigates to this page as well as via the "Unlock" button.
We will only attempt the unlock operation if the vault is actually locked. Try to unlock the vault. If the unlock fails, set the "show" flag so the user can try again or give up and go back to the login step.
If the user succeeded in unlocking the vault, determine if we should navigate to the LoginPage
or the Tab1Page
based on the current authentication status.
If the user chooses to redo the login, logout and navigate to the LoginPage
.
One item of note on the redoLogin()
code. If we are using an authentication system, we need to craft our logout()
method such that it can be called with a locked vault. Crafting the logout as such is beyond the scope of this tutorial.
Redirect on Lock
Upon locking, the session
will be set to null
. Update the App
component to watch to the session
. When the sessions
changes, check the vault's lock status. If the vault is locked navigate to /
. This will load the StartPage
and execute an
iteration of our unlock workflow.
Cleanup the Tab1Page
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 needs to 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 "Lock" and "Unlock" buttons and all code associated with them.
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 good basic application startup workflow. This is an example of a good workflow, but it
is not the only potential flow. For example, our application simply navigates to /tabs/tab1
after unlocking the
vault. You could, however, store information about the current state of the application and then restore to that
state after unlocking the application. Do whatever is right for your application.