Skip to main content
Version: 5.0

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:

  1. Locked:
    1. With valid authentication tokens.
    2. With invalid authentication tokens.
  2. 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.

src/views/LoginPage.vue
src/views/StartPage.vue

_20
<template>
_20
<ion-page>
_20
<ion-header>
_20
<ion-toolbar>
_20
<ion-title>Login</ion-title>
_20
</ion-toolbar>
_20
</ion-header>
_20
<ion-content :fullscreen="true">
_20
<ion-header collapse="condense">
_20
<ion-toolbar>
_20
<ion-title size="large">Login</ion-title>
_20
</ion-toolbar>
_20
</ion-header>
_20
</ion-content>
_20
</ion-page>
_20
</template>
_20
_20
<script setup lang="ts">
_20
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/vue';
_20
</script>

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.

src/router/index.ts

_39
import { createRouter, createWebHistory } from '@ionic/vue-router';
_39
import { RouteRecordRaw } from 'vue-router';
_39
import TabsPage from '../views/TabsPage.vue';
_39
_39
const routes: Array<RouteRecordRaw> = [
_39
{
_39
path: '/',
_39
redirect: '/tabs/tab1',
_39
},
_39
{
_39
path: '/tabs/',
_39
component: TabsPage,
_39
children: [
_39
{
_39
path: '',
_39
redirect: '/tabs/tab1',
_39
},
_39
{
_39
path: 'tab1',
_39
component: () => import('@/views/Tab1Page.vue'),
_39
},
_39
{
_39
path: 'tab2',
_39
component: () => import('@/views/Tab2Page.vue'),
_39
},
_39
{
_39
path: 'tab3',
_39
component: () => import('@/views/Tab3Page.vue'),
_39
},
_39
],
_39
},
_39
];
_39
_39
const router = createRouter({
_39
history: createWebHistory(import.meta.env.BASE_URL),
_39
routes,
_39
});
_39
_39
export default router;

Currently we only have routes for the TabsPage and its children.

src/router/index.ts

_38
import { createRouter, createWebHistory } from '@ionic/vue-router';
_38
import { RouteRecordRaw } from 'vue-router';
_38
_38
const routes: Array<RouteRecordRaw> = [
_38
{
_38
path: '/',
_38
redirect: '/tabs/tab1',
_38
},
_38
{
_38
path: '/tabs/',
_38
component: () => import('@/views/TabsPage.vue'),
_38
children: [
_38
{
_38
path: '',
_38
redirect: '/tabs/tab1',
_38
},
_38
{
_38
path: 'tab1',
_38
component: () => import('@/views/Tab1Page.vue'),
_38
},
_38
{
_38
path: 'tab2',
_38
component: () => import('@/views/Tab2Page.vue'),
_38
},
_38
{
_38
path: 'tab3',
_38
component: () => import('@/views/Tab3Page.vue'),
_38
},
_38
],
_38
},
_38
];
_38
_38
const router = createRouter({
_38
history: createWebHistory(import.meta.env.BASE_URL),
_38
routes,
_38
});
_38
_38
export default router;

Lazy-load the TabsPage.

src/router/index.ts

_43
import { createRouter, createWebHistory } from '@ionic/vue-router';
_43
import { RouteRecordRaw } from 'vue-router';
_43
import StartPage from '@/views/StartPage.vue';
_43
_43
const routes: Array<RouteRecordRaw> = [
_43
{
_43
path: '/',
_43
redirect: '/start',
_43
},
_43
{
_43
path: '/start',
_43
component: StartPage,
_43
},
_43
{
_43
path: '/tabs/',
_43
component: () => import('@/views/TabsPage.vue'),
_43
children: [
_43
{
_43
path: '',
_43
redirect: '/tabs/tab1',
_43
},
_43
{
_43
path: 'tab1',
_43
component: () => import('@/views/Tab1Page.vue'),
_43
},
_43
{
_43
path: 'tab2',
_43
component: () => import('@/views/Tab2Page.vue'),
_43
},
_43
{
_43
path: 'tab3',
_43
component: () => import('@/views/Tab3Page.vue'),
_43
},
_43
],
_43
},
_43
];
_43
_43
const router = createRouter({
_43
history: createWebHistory(import.meta.env.BASE_URL),
_43
routes,
_43
});
_43
_43
export default router;

Eager-load the StartPage and change the / to redirect to it.

src/router/index.ts

_47
import { createRouter, createWebHistory } from '@ionic/vue-router';
_47
import { RouteRecordRaw } from 'vue-router';
_47
import StartPage from '@/views/StartPage.vue';
_47
_47
const routes: Array<RouteRecordRaw> = [
_47
{
_47
path: '/',
_47
redirect: '/start',
_47
},
_47
{
_47
path: '/start',
_47
component: StartPage,
_47
},
_47
{
_47
path: '/login',
_47
component: () => import('@/views/LoginPage.vue'),
_47
},
_47
{
_47
path: '/tabs/',
_47
component: () => import('@/views/TabsPage.vue'),
_47
children: [
_47
{
_47
path: '',
_47
redirect: '/tabs/tab1',
_47
},
_47
{
_47
path: 'tab1',
_47
component: () => import('@/views/Tab1Page.vue'),
_47
},
_47
{
_47
path: 'tab2',
_47
component: () => import('@/views/Tab2Page.vue'),
_47
},
_47
{
_47
path: 'tab3',
_47
component: () => import('@/views/Tab3Page.vue'),
_47
},
_47
],
_47
},
_47
];
_47
_47
const router = createRouter({
_47
history: createWebHistory(import.meta.env.BASE_URL),
_47
routes,
_47
});
_47
_47
export default router;

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.

src/router/index.ts

_39
import { createRouter, createWebHistory } from '@ionic/vue-router';
_39
import { RouteRecordRaw } from 'vue-router';
_39
import TabsPage from '../views/TabsPage.vue';
_39
_39
const routes: Array<RouteRecordRaw> = [
_39
{
_39
path: '/',
_39
redirect: '/tabs/tab1',
_39
},
_39
{
_39
path: '/tabs/',
_39
component: TabsPage,
_39
children: [
_39
{
_39
path: '',
_39
redirect: '/tabs/tab1',
_39
},
_39
{
_39
path: 'tab1',
_39
component: () => import('@/views/Tab1Page.vue'),
_39
},
_39
{
_39
path: 'tab2',
_39
component: () => import('@/views/Tab2Page.vue'),
_39
},
_39
{
_39
path: 'tab3',
_39
component: () => import('@/views/Tab3Page.vue'),
_39
},
_39
],
_39
},
_39
];
_39
_39
const router = createRouter({
_39
history: createWebHistory(import.meta.env.BASE_URL),
_39
routes,
_39
});
_39
_39
export default router;

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.

src/composables/authentication.ts

_10
export const useAuthentication = (): any => ({});

Start with an empty composable.

src/composables/authentication.ts

_10
import { useSessionVault } from './session-vault';
_10
_10
const { clearSession, getSession, session, storeSession } = useSessionVault();
_10
_10
export const useAuthentication = (): any => ({});

Get the functions and data that we need from the useSessionVault composable.

src/composables/authentication.ts

_14
import { useSessionVault } from './session-vault';
_14
_14
const { clearSession, getSession, session, storeSession } = useSessionVault();
_14
_14
const login = (): Promise<void> =>
_14
storeSession({
_14
email: 'test@ionic.io',
_14
firstName: 'Tessa',
_14
lastName: 'Testsmith',
_14
accessToken: '4abf1d79-143c-4b89-b478-19607eb5ce97',
_14
refreshToken: '565111b6-66c3-4527-9238-6ea2cc017126',
_14
});
_14
_14
export const useAuthentication = (): any => ({ login });

The user needs to be able to log in. Since we do not yet have an authentication strategy, we will store a fake session.

src/composables/authentication.ts

_16
import { useSessionVault } from './session-vault';
_16
_16
const { clearSession, getSession, session, storeSession } = useSessionVault();
_16
_16
const login = (): Promise<void> =>
_16
storeSession({
_16
email: 'test@ionic.io',
_16
firstName: 'Tessa',
_16
lastName: 'Testsmith',
_16
accessToken: '4abf1d79-143c-4b89-b478-19607eb5ce97',
_16
refreshToken: '565111b6-66c3-4527-9238-6ea2cc017126',
_16
});
_16
_16
const logout = (): Promise<void> => clearSession();
_16
_16
export const useAuthentication = (): any => ({ login, logout });

For the logout(), just clear the stored session.

src/composables/authentication.ts

_25
import { useSessionVault } from './session-vault';
_25
_25
const { clearSession, getSession, session, storeSession } = useSessionVault();
_25
_25
const login = (): Promise<void> =>
_25
storeSession({
_25
email: 'test@ionic.io',
_25
firstName: 'Tessa',
_25
lastName: 'Testsmith',
_25
accessToken: '4abf1d79-143c-4b89-b478-19607eb5ce97',
_25
refreshToken: '565111b6-66c3-4527-9238-6ea2cc017126',
_25
});
_25
_25
const logout = (): Promise<void> => clearSession();
_25
_25
const isAuthenticated = async (): Promise<boolean> => {
_25
await getSession();
_25
return !!session.value;
_25
};
_25
_25
export const useAuthentication = (): any => ({
_25
isAuthenticated,
_25
login,
_25
logout,
_25
});

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.

src/composables/authentication.ts

_10
export const useAuthentication = (): any => ({});

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.

src/views/LoginPage.vue

_38
<template>
_38
<ion-page>
_38
<ion-header>
_38
<ion-toolbar>
_38
<ion-title>Login</ion-title>
_38
</ion-toolbar>
_38
</ion-header>
_38
<ion-content :fullscreen="true">
_38
<ion-header collapse="condense">
_38
<ion-toolbar>
_38
<ion-title size="large">Login</ion-title>
_38
</ion-toolbar>
_38
</ion-header>
_38
_38
<ion-list>
_38
<ion-item>
_38
<ion-label>
_38
<ion-button expand="block">Login</ion-button>
_38
</ion-label>
_38
</ion-item>
_38
</ion-list>
_38
</ion-content>
_38
</ion-page>
_38
</template>
_38
_38
<script setup lang="ts">
_38
import {
_38
IonButton,
_38
IonContent,
_38
IonHeader,
_38
IonItem,
_38
IonLabel,
_38
IonList,
_38
IonPage,
_38
IonTitle,
_38
IonToolbar,
_38
} from '@ionic/vue';
_38
</script>

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.
src/app/login/login.page.ts

_52
<template>
_52
<ion-page>
_52
<ion-header>
_52
<ion-toolbar>
_52
<ion-title>Login</ion-title>
_52
</ion-toolbar>
_52
</ion-header>
_52
<ion-content :fullscreen="true">
_52
<ion-header collapse="condense">
_52
<ion-toolbar>
_52
<ion-title size="large">Login</ion-title>
_52
</ion-toolbar>
_52
</ion-header>
_52
_52
<ion-list>
_52
<ion-item>
_52
<ion-label>
_52
<ion-button expand="block" @click="handleLogin">Login</ion-button>
_52
</ion-label>
_52
</ion-item>
_52
</ion-list>
_52
</ion-content>
_52
</ion-page>
_52
</template>
_52
_52
<script setup lang="ts">
_52
import {
_52
IonButton,
_52
IonContent,
_52
IonHeader,
_52
IonItem,
_52
IonLabel,
_52
IonList,
_52
IonPage,
_52
IonTitle,
_52
IonToolbar,
_52
} from '@ionic/vue';
_52
import { useRouter } from 'vue-router';
_52
import { useAuthentication } from '@/composables/authentication';
_52
_52
const { login } = useAuthentication();
_52
const router = useRouter();
_52
_52
const handleLogin = async () => {
_52
try {
_52
await login();
_52
router.replace('/tabs/tab1');
_52
} catch (error: unknown) {
_52
console.error('Failed to log in', error);
_52
}
_52
};
_52
</script>

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.

src/composables/session-vault.ts

_94
import { useVaultFactory } from '@/composables/vault-factory';
_94
import { Session } from '@/models/session';
_94
import {
_94
BrowserVault,
_94
DeviceSecurityType,
_94
IdentityVaultConfig,
_94
Vault,
_94
VaultType,
_94
} from '@ionic-enterprise/identity-vault';
_94
import { ref } from 'vue';
_94
_94
export type UnlockMode = 'BiometricsWithPasscode' | 'InMemory' | 'SecureStorage';
_94
_94
const { createVault } = useVaultFactory();
_94
const vault: Vault | BrowserVault = createVault();
_94
const session = ref<Session | null>(null);
_94
_94
const initializeVault = async (): Promise<void> => {
_94
try {
_94
await vault.initialize({
_94
key: 'io.ionic.gettingstartediv',
_94
type: VaultType.SecureStorage,
_94
deviceSecurityType: DeviceSecurityType.None,
_94
lockAfterBackgrounded: 2000,
_94
});
_94
} catch (e: unknown) {
_94
await vault.clear();
_94
await updateUnlockMode('SecureStorage');
_94
}
_94
_94
vault.onLock(() => (session.value = null));
_94
};
_94
_94
const storeSession = async (s: Session): Promise<void> => {
_94
vault.setValue('session', s);
_94
session.value = s;
_94
};
_94
_94
const getSession = async (): Promise<void> => {
_94
session.value = await vault.getValue<Session>('session');

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.

src/views/StartPage.vue

_29
<template>
_29
<ion-page>
_29
<ion-content class="ion-padding">
_29
<ion-list v-if="showUnlock">
_29
<ion-item>
_29
<ion-label>
_29
<ion-button expand="block" @click="performUnlockFlow">Unlock</ion-button>
_29
</ion-label>
_29
</ion-item>
_29
<ion-item>
_29
<ion-label>
_29
<ion-button expand="block" @click="redoLogin">Redo Login</ion-button>
_29
</ion-label>
_29
</ion-item>
_29
</ion-list>
_29
</ion-content>
_29
</ion-page>
_29
</template>
_29
_29
<script setup lang="ts">
_29
import { ref } from 'vue';
_29
import { IonButton, IonContent, IonItem, IonLabel, IonList, IonPage } from '@ionic/vue';
_29
_29
const showUnlock = ref(false);
_29
_29
const performUnlockFlow = async (): Promise<void> => {};
_29
_29
const redoLogin = async (): Promise<void> => {};
_29
</script>

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.

src/views/StartPage.vue

_21
<script setup lang="ts">
_21
import { ref } from 'vue';
_21
import { IonButton, IonContent, IonItem, IonLabel, IonList, IonPage, onIonViewDidEnter } from '@ionic/vue';
_21
_21
const showUnlock = ref(false);
_21
_21
const performUnlockFlow = async (): Promise<void> => {
_21
await attemptUnlock();
_21
await attemptNavigation();
_21
};
_21
_21
const redoLogin = async (): Promise<void> => {};
_21
_21
const attemptNavigation = async (): Promise<void> => {};
_21
_21
const attemptUnlock = async (): Promise<void> => {};
_21
_21
onIonViewDidEnter(() => {
_21
performUnlockFlow();
_21
});
_21
</script>

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.

src/views/StartPage.vue

_31
<script setup lang="ts">
_31
import { ref } from 'vue';
_31
import { IonButton, IonContent, IonItem, IonLabel, IonList, IonPage, onIonViewDidEnter } from '@ionic/vue';
_31
import { useSessionVault } from '@/composables/session-vault';
_31
_31
const showUnlock = ref(false);
_31
const { sessionIsLocked, unlockSession } = useSessionVault();
_31
_31
const performUnlockFlow = async (): Promise<void> => {
_31
await attemptUnlock();
_31
await attemptNavigation();
_31
};
_31
_31
const redoLogin = async (): Promise<void> => {};
_31
_31
const attemptNavigation = async (): Promise<void> => {};
_31
_31
const attemptUnlock = async (): Promise<void> => {
_31
if (await sessionIsLocked()) {
_31
try {
_31
await unlockSession();
_31
} catch (err: unknown) {
_31
showUnlock.value = true;
_31
}
_31
}
_31
};
_31
_31
onIonViewDidEnter(() => {
_31
performUnlockFlow();
_31
});
_31
</script>

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.

src/views/StartPage.vue

_43
<script setup lang="ts">
_43
import { ref } from 'vue';
_43
import { IonButton, IonContent, IonItem, IonLabel, IonList, IonPage, onIonViewDidEnter } from '@ionic/vue';
_43
import { useRouter } from 'vue-router';
_43
import { useAuthentication } from '@/composables/authentication';
_43
import { useSessionVault } from '@/composables/session-vault';
_43
_43
const showUnlock = ref(false);
_43
const { isAuthenticated } = useAuthentication();
_43
const { sessionIsLocked, unlockSession } = useSessionVault();
_43
const router = useRouter();
_43
_43
const performUnlockFlow = async (): Promise<void> => {
_43
await attemptUnlock();
_43
await attemptNavigation();
_43
};
_43
_43
const redoLogin = async (): Promise<void> => {};
_43
_43
const attemptNavigation = async (): Promise<void> => {
_43
if (!(await sessionIsLocked())) {
_43
if (await isAuthenticated()) {
_43
router.replace('/tabs/tab1');
_43
} else {
_43
router.replace('/login');
_43
}
_43
}
_43
};
_43
_43
const attemptUnlock = async (): Promise<void> => {
_43
if (await sessionIsLocked()) {
_43
try {
_43
await unlockSession();
_43
} catch (err: unknown) {
_43
showUnlock.value = true;
_43
}
_43
}
_43
};
_43
_43
onIonViewDidEnter(() => {
_43
performUnlockFlow();
_43
});
_43
</script>

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.

src/views/StartPage.vue

_46
<script setup lang="ts">
_46
import { ref } from 'vue';
_46
import { IonButton, IonContent, IonItem, IonLabel, IonList, IonPage, onIonViewDidEnter } from '@ionic/vue';
_46
import { useRouter } from 'vue-router';
_46
import { useAuthentication } from '@/composables/authentication';
_46
import { useSessionVault } from '@/composables/session-vault';
_46
_46
const showUnlock = ref(false);
_46
const { isAuthenticated, logout } = useAuthentication();
_46
const { sessionIsLocked, unlockSession } = useSessionVault();
_46
const router = useRouter();
_46
_46
const performUnlockFlow = async (): Promise<void> => {
_46
await attemptUnlock();
_46
await attemptNavigation();
_46
};
_46
_46
const redoLogin = async (): Promise<void> => {
_46
await logout();
_46
router.replace('/login');
_46
};
_46
_46
const attemptNavigation = async (): Promise<void> => {
_46
if (!(await sessionIsLocked())) {
_46
if (await isAuthenticated()) {
_46
router.replace('/tabs/tab1');
_46
} else {
_46
router.replace('/login');
_46
}
_46
}
_46
};
_46
_46
const attemptUnlock = async (): Promise<void> => {
_46
if (await sessionIsLocked()) {
_46
try {
_46
await unlockSession();
_46
} catch (err: unknown) {
_46
showUnlock.value = true;
_46
}
_46
}
_46
};
_46
_46
onIonViewDidEnter(() => {
_46
performUnlockFlow();
_46
});
_46
</script>

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.

src/views/StartPage.vue

_21
<script setup lang="ts">
_21
import { ref } from 'vue';
_21
import { IonButton, IonContent, IonItem, IonLabel, IonList, IonPage, onIonViewDidEnter } from '@ionic/vue';
_21
_21
const showUnlock = ref(false);
_21
_21
const performUnlockFlow = async (): Promise<void> => {
_21
await attemptUnlock();
_21
await attemptNavigation();
_21
};
_21
_21
const redoLogin = async (): Promise<void> => {};
_21
_21
const attemptNavigation = async (): Promise<void> => {};
_21
_21
const attemptUnlock = async (): Promise<void> => {};
_21
_21
onIonViewDidEnter(() => {
_21
performUnlockFlow();
_21
});
_21
</script>

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.

src/App.vue

_21
<template>
_21
<ion-app>
_21
<ion-router-outlet />
_21
</ion-app>
_21
</template>
_21
_21
<script setup lang="ts">
_21
import { IonApp, IonRouterOutlet } from '@ionic/vue';
_21
import { watch } from 'vue';
_21
import { useRouter } from 'vue-router';
_21
import { useSessionVault } from '@/composables/session-vault';
_21
_21
const router = useRouter();
_21
const { session, sessionIsLocked } = useSessionVault();
_21
_21
watch(session, async () => {
_21
if (await sessionIsLocked()) {
_21
router.replace('/');
_21
}
_21
});
_21
</script>

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.

src/views/Tab1Page.vue

_79
<template>
_79
<ion-page>
_79
<ion-header>
_79
<ion-toolbar>
_79
<ion-title>Tab 1</ion-title>
_79
</ion-toolbar>
_79
</ion-header>
_79
<ion-content :fullscreen="true">
_79
<ion-header collapse="condense">
_79
<ion-toolbar>
_79
<ion-title size="large">Tab 1</ion-title>
_79
</ion-toolbar>
_79
</ion-header>
_79
_79
<ion-list>
_79
<ion-item>
_79
<ion-label>
_79
<ion-button expand="block" color="danger" @click="logoutClicked">Logout</ion-button>
_79
</ion-label>
_79
</ion-item>
_79
<ion-item>
_79
<ion-label>
_79
<ion-button expand="block" color="secondary" @click="updateUnlockMode('BiometricsWithPasscode')"
_79
>Use Biometrics</ion-button
_79
>
_79
</ion-label>
_79
</ion-item>
_79
<ion-item>
_79
<ion-label>
_79
<ion-button expand="block" color="secondary" @click="updateUnlockMode('InMemory')"
_79
>Use In Memory</ion-button
_79
>
_79
</ion-label>
_79
</ion-item>
_79
<ion-item>
_79
<ion-label>
_79
<ion-button expand="block" color="secondary" @click="updateUnlockMode('SecureStorage')"
_79
>Use Secure Storage</ion-button
_79
>
_79
</ion-label>
_79
</ion-item>
_79
<ion-item>
_79
<div>
_79
<div>{{ session?.email }}</div>
_79
<div>{{ session?.firstName }} {{ session?.lastName }}</div>
_79
<div>{{ session?.accessToken }}</div>
_79
<div>{{ session?.refreshToken }}</div>
_79
</div>
_79
</ion-item>
_79
</ion-list>
_79
</ion-content>
_79
</ion-page>
_79
</template>
_79
_79
<script setup lang="ts">
_79
import { useAuthentication } from '@/composables/authentication';
_79
import { useSessionVault } from '@/composables/session-vault';
_79
import {
_79
IonButton,
_79
IonContent,
_79
IonHeader,
_79
IonItem,
_79
IonLabel,
_79
IonList,
_79
IonPage,
_79
IonTitle,
_79
IonToolbar,
_79
} from '@ionic/vue';
_79
import { useRouter } from 'vue-router';
_79
_79
const { logout } = useAuthentication();
_79
const { session, updateUnlockMode } = useSessionVault();
_79
const router = useRouter();
_79
_79
const logoutClicked = async (): Promise<void> => {
_79
await logout();
_79
router.replace('/');
_79
};
_79
</script>

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.