Skip to main content
Version: 5.0

Getting Started with Identity Vault

Generate the Application

Before we explore the use of Identity Vault, we need to scaffold an application. In this section, we will generate a tabs-based @ionic/vue application, perform some basic configuration, and add the iOS and Android platforms.

If you need to refresh your memory on the overall developer workflow for Capacitor, please do so now. However, here is a synopsis of the commands you will use the most while performing this tutorial:

  • npm start: Start the development server so the application can be run in the browser.
  • npm run build: Build the web portion of the application.
  • npm cap sync: Copy the web app and any new plugins into the native applications.
  • npm cap open android: Open Android Studio in order to build, run, and debug the application on Android.
  • npm cap open ios: Open Xcode in order to build, run, and debug the application on iOS.

Let's get started.

Terminal

_10
ionic start iv-getting-started tabs --type=vue

Use the Ionic CLI to generate the application.

Terminal

_10
ionic start iv-getting-started tabs --type=vue
_10
cd iv-getting-started

Change directory into the newly generated project.

Terminal
capacitor.config.ts

_12
import { CapacitorConfig } from '@capacitor/cli';
_12
_12
const config: CapacitorConfig = {
_12
appId: 'io.ionic.gettingstartediv',
_12
appName: 'iv-getting-started',
_12
webDir: 'www',
_12
server: {
_12
androidScheme: 'https',
_12
},
_12
};
_12
_12
export default config;

Change the appId to be something unique. The appId is used as the bundle ID / application ID. Therefore it should be a string that is unique to your organization and application. We will use io.ionic.gettingstartediv for this application.

It is best to do this before adding the iOS and Android platforms to ensure they are setup properly from the start.

Terminal
capacitor.config.ts

_10
ionic start iv-getting-started-ac tabs --type=vue
_10
cd iv-getting-started
_10
npm run build
_10
ionic cap add android
_10
ionic cap add ios

Build the application and install the platforms.

Terminal
capacitor.config.ts
package.json

_15
{
_15
"name": "iv-getting-started",
_15
"version": "0.0.1",
_15
"author": "Ionic Framework",
_15
"homepage": "https://ionicframework.com/",
_15
"scripts": {
_15
"dev": "vite",
_15
"build": "vue-tsc && vite build && cap sync",
_15
"preview": "vite preview",
_15
"test:e2e": "cypress run",
_15
"test:unit": "vitest",
_15
"lint": "eslint ."
_15
},
_15
...
_15
}

We should do a cap sync with each build. Change the scripts in package.json to do this.

Use the Ionic CLI to generate the application.

Change directory into the newly generated project.

Change the appId to be something unique. The appId is used as the bundle ID / application ID. Therefore it should be a string that is unique to your organization and application. We will use io.ionic.gettingstartediv for this application.

It is best to do this before adding the iOS and Android platforms to ensure they are setup properly from the start.

Build the application and install the platforms.

We should do a cap sync with each build. Change the scripts in package.json to do this.

Terminal

_10
ionic start iv-getting-started tabs --type=vue

Install Identity Vault

In order to install Identity Vault, you will need to use ionic enterprise register register your product key. This will create a .npmrc file containing the product key.

If you have already performed that step for your production application, you can just copy the .npmrc file from your production project. Since this application is for learning purposes only, you don't need to obtain another key.

You can now install Identity Vault and sync the platforms:

Terminal

_10
npm install @ionic-enterprise/identity-vault
_10
npx cap sync

Create the Composables

Our tutorial application will have a single vault that simulates storing our application's authentication session information. To manage this vault, we will create two composables:

  • useVaultFactory: Builds either a Vault or BrowserVault depending on the whether our application is running in a web or web-native context.
  • useSessionVault: Manages the vault itself.

useVaultFactory

Create a src/composables folder and add a file named src/composables/vault-factory.ts with the following contents:

src/composables/vault-factory.ts

_10
import { Capacitor } from '@capacitor/core';
_10
import { BrowserVault, Vault } from '@ionic-enterprise/identity-vault';
_10
_10
export const useVaultFactory = (): any => {
_10
const createVault = (): Vault | BrowserVault => (Capacitor.isNativePlatform() ? new Vault() : new BrowserVault());
_10
_10
return { createVault };
_10
};

useSessionVault

The useSessionVault composable will contain the functions that are used to manage the session vault for the application. Create the src/composables/session-vault.ts file with the following contents.

src/composables/session-vault.ts

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

Create and Initialization the Vault

Before we use Identity Vault, we need to make sure that our vault is properly created and initialized. It is important to note that creation and initialization are different processes. Creation is performed when the module for the composable is constructed and is limited to the creation of a JavaScript object.

The initialization involves communication with the native layer. As such it is asynchronous. Since initialization needs to complete before we can begin normal operation of the application, we run the initialization and await its completion before the main application component is mounted.

Important: awaiting the completion of initialization in this manner is a best-practice that should always be followed.

src/composables/session-vault.ts
src/main.ts

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

We will build this composable up to perform the vault creation and initialization.

src/composables/session-vault.ts
src/main.ts

_10
import { BrowserVault, Vault } from '@ionic-enterprise/identity-vault';
_10
import { useVaultFactory } from '@/composables/vault-factory';
_10
_10
const { createVault } = useVaultFactory();
_10
const vault: Vault | BrowserVault = createVault();
_10
_10
export const useSessionVault = (): any => ({});

Create the vault using our factory function.

src/composables/session-vault.ts
src/main.ts

_19
import { BrowserVault, DeviceSecurityType, Vault, VaultType } from '@ionic-enterprise/identity-vault';
_19
import { useVaultFactory } from '@/composables/vault-factory';
_19
_19
const { createVault } = useVaultFactory();
_19
const vault: Vault | BrowserVault = createVault();
_19
_19
const initializeVault = async (): Promise<void> => {
_19
try {
_19
await vault.initialize({
_19
key: 'io.ionic.gettingstartediv',
_19
type: VaultType.SecureStorage,
_19
deviceSecurityType: DeviceSecurityType.None,
_19
});
_19
} catch (e: unknown) {
_19
await vault.clear();
_19
}
_19
};
_19
_19
export const useSessionVault = (): any => ({ initializeVault });

Create an initializeVault() function from which we will perform all vault initialization. Pass a configuration object to our vault. The meaning of the configuration properties will be explained later.

If the initialize() fails the best thing to do is simply clear it.

src/composables/session-vault.ts
src/main.ts

_30
import { createApp } from 'vue';
_30
import App from './App.vue';
_30
import router from './router';
_30
_30
import { IonicVue } from '@ionic/vue';
_30
_30
/* Core CSS required for Ionic components to work properly */
_30
import '@ionic/vue/css/core.css';
_30
_30
/* Basic CSS for apps built with Ionic */
_30
import '@ionic/vue/css/normalize.css';
_30
import '@ionic/vue/css/structure.css';
_30
import '@ionic/vue/css/typography.css';
_30
_30
/* Optional CSS utils that can be commented out */
_30
import '@ionic/vue/css/padding.css';
_30
import '@ionic/vue/css/float-elements.css';
_30
import '@ionic/vue/css/text-alignment.css';
_30
import '@ionic/vue/css/text-transformation.css';
_30
import '@ionic/vue/css/flex-utils.css';
_30
import '@ionic/vue/css/display.css';
_30
_30
/* Theme variables */
_30
import './theme/variables.css';
_30
_30
const app = createApp(App).use(IonicVue).use(router);
_30
_30
router.isReady().then(() => {
_30
app.mount('#app');
_30
});

src/main.ts currently just waits for the router to be ready before mounting the app.

src/composables/session-vault.ts
src/main.ts

_33
import { createApp } from 'vue';
_33
import App from './App.vue';
_33
import router from './router';
_33
_33
import { IonicVue } from '@ionic/vue';
_33
import { useSessionVault } from '@/composables/session-vault';
_33
_33
/* Core CSS required for Ionic components to work properly */
_33
import '@ionic/vue/css/core.css';
_33
_33
/* Basic CSS for apps built with Ionic */
_33
import '@ionic/vue/css/normalize.css';
_33
import '@ionic/vue/css/structure.css';
_33
import '@ionic/vue/css/typography.css';
_33
_33
/* Optional CSS utils that can be commented out */
_33
import '@ionic/vue/css/padding.css';
_33
import '@ionic/vue/css/float-elements.css';
_33
import '@ionic/vue/css/text-alignment.css';
_33
import '@ionic/vue/css/text-transformation.css';
_33
import '@ionic/vue/css/flex-utils.css';
_33
import '@ionic/vue/css/display.css';
_33
_33
/* Theme variables */
_33
import './theme/variables.css';
_33
_33
const { initializeVault } = useSessionVault();
_33
_33
initializeVault().then(async () => {
_33
const app = createApp(App).use(IonicVue).use(router);
_33
await router.isReady();
_33
app.mount('#app');
_33
});

Update src/main.ts to make sure our vault is fully initialized on startup before the main application component is mounted.

We will build this composable up to perform the vault creation and initialization.

Create the vault using our factory function.

Create an initializeVault() function from which we will perform all vault initialization. Pass a configuration object to our vault. The meaning of the configuration properties will be explained later.

If the initialize() fails the best thing to do is simply clear it.

src/main.ts currently just waits for the router to be ready before mounting the app.

Update src/main.ts to make sure our vault is fully initialized on startup before the main application component is mounted.

src/composables/session-vault.ts
src/main.ts

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

In this section, we created a vault using the key io.ionic.gettingstartediv. Our vault is a "Secure Storage" vault, which means that the information we store in the vault is encrypted in the keychain / keystore and is only visible to our application, but the vault is never locked. We will explore other types of vaults later in this tutorial.

Store a Value

Let's store some data in the vault. Here, we will:

  • Define our session information.
  • Add a function to useSessionVault to store a session.
  • Add a button to Tab1Page to store a fake session.

First, let's define the shape of our authentication session data via a TypeScript interface. Create a src/models folder and add a src/models/session.ts file.

src/models/session.ts

_10
export interface Session {
_10
firstName: string;
_10
lastName: string;
_10
email: string;
_10
accessToken: string;
_10
refreshToken: string;
_10
}

We can store multiple items within the vault, each with their own key. For this application, we will store a single item with the key of session. The vault has a setValue() method that is used for this purpose. Modify src/composables/session-vault.ts to store the session.

src/composables/session-vault.ts

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

Notice that we have created a very light wrapper around the vault's setValue() method. This is often all that is required. You may be tempted to just make the useSessionVault's vault value public and then use the Identity Vault methods directly on the vault. It is best-practice, however, to encapsulate the vault in a composable like this one and only expose the functionality that makes sense for your application.

With the "store session" feature properly abstracted, add a button to the Tab1Page that will simulate logging in by storing some fake authentication data in the vault.

src/views/Tab1Page.vue

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

We are currently displaying the generic starter "Explore Container" data.

src/views/Tab1Page.vue

_38
<template>
_38
<ion-page>
_38
<ion-header>
_38
<ion-toolbar>
_38
<ion-title>Tab 1</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">Tab 1</ion-title>
_38
</ion-toolbar>
_38
</ion-header>
_38
_38
<ion-list>
_38
<ion-item>
_38
<ion-label>
_38
<ion-button expand="block">Store</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>

Replace the explore container with a list containing a button.

src/views/Tab1Page.vue

_41
<template>
_41
<ion-page>
_41
<ion-header>
_41
<ion-toolbar>
_41
<ion-title>Tab 1</ion-title>
_41
</ion-toolbar>
_41
</ion-header>
_41
<ion-content :fullscreen="true">
_41
<ion-header collapse="condense">
_41
<ion-toolbar>
_41
<ion-title size="large">Tab 1</ion-title>
_41
</ion-toolbar>
_41
</ion-header>
_41
_41
<ion-list>
_41
<ion-item>
_41
<ion-label>
_41
<ion-button expand="block">Store</ion-button>
_41
</ion-label>
_41
</ion-item>
_41
</ion-list>
_41
</ion-content>
_41
</ion-page>
_41
</template>
_41
_41
<script setup lang="ts">
_41
import {
_41
IonButton,
_41
IonContent,
_41
IonHeader,
_41
IonItem,
_41
IonLabel,
_41
IonList,
_41
IonPage,
_41
IonTitle,
_41
IonToolbar,
_41
} from '@ionic/vue';
_41
import { useSessionVault } from '@/composables/session-vault';
_41
_41
const { storeSession } = useSessionVault();
_41
</script>

Get the storeSession() function from useSessionVault().

src/views/Tab1Page.vue

_51
<template>
_51
<ion-page>
_51
<ion-header>
_51
<ion-toolbar>
_51
<ion-title>Tab 1</ion-title>
_51
</ion-toolbar>
_51
</ion-header>
_51
<ion-content :fullscreen="true">
_51
<ion-header collapse="condense">
_51
<ion-toolbar>
_51
<ion-title size="large">Tab 1</ion-title>
_51
</ion-toolbar>
_51
</ion-header>
_51
_51
<ion-list>
_51
<ion-item>
_51
<ion-label>
_51
<ion-button expand="block" @click="storeClicked">Store</ion-button>
_51
</ion-label>
_51
</ion-item>
_51
</ion-list>
_51
</ion-content>
_51
</ion-page>
_51
</template>
_51
_51
<script setup lang="ts">
_51
import {
_51
IonButton,
_51
IonContent,
_51
IonItem,
_51
IonLabel,
_51
IonList,
_51
IonPage,
_51
IonHeader,
_51
IonTitle,
_51
IonToolbar,
_51
} from '@ionic/vue';
_51
import { useSessionVault } from '@/composables/session-vault';
_51
_51
const { storeSession } = useSessionVault();
_51
_51
const storeClicked = async (): Promise<void> => {
_51
await storeSession({
_51
email: 'test@ionic.io',
_51
firstName: 'Tessa',
_51
lastName: 'Testsmith',
_51
accessToken: '4abf1d79-143c-4b89-b478-19607eb5ce97',
_51
refreshToken: '565111b6-66c3-4527-9238-6ea2cc017126',
_51
});
_51
};
_51
</script>

Store some made up test data.

We are currently displaying the generic starter "Explore Container" data.

Replace the explore container with a list containing a button.

Get the storeSession() function from useSessionVault().

Store some made up test data.

src/views/Tab1Page.vue

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

We have stored data in our vault. The next step is to get the data back out of the vault.

Get a Value

In order to better illustrate the operation of the vault, we will modify the Tab1Page to display our session if one is stored. Get the session value from our 'useSessionVault' composable and create an area in which to display the session information.

src/views/Tab1Page.vue

_59
<template>
_59
<ion-page>
_59
<ion-header>
_59
<ion-toolbar>
_59
<ion-title>Tab 1</ion-title>
_59
</ion-toolbar>
_59
</ion-header>
_59
<ion-content :fullscreen="true">
_59
<ion-header collapse="condense">
_59
<ion-toolbar>
_59
<ion-title size="large">Tab 1</ion-title>
_59
</ion-toolbar>
_59
</ion-header>
_59
_59
<ion-list>
_59
<ion-item>
_59
<ion-label>
_59
<ion-button expand="block" @click="storeClicked">Store</ion-button>
_59
</ion-label>
_59
</ion-item>
_59
<ion-item>
_59
<div>
_59
<div>{{ session?.email }}</div>
_59
<div>{{ session?.firstName }} {{ session?.lastName }}</div>
_59
<div>{{ session?.accessToken }}</div>
_59
<div>{{ session?.refreshToken }}</div>
_59
</div>
_59
</ion-item>
_59
</ion-list>
_59
</ion-content>
_59
</ion-page>
_59
</template>

This displays the session when the user presses the "Store" button. However, if you refresh the browser or restart the application, the session data is no longer displayed. That is because our session variable was cleared.

Add a function to useSessionVault that encapsulates getting the session. Checking if the vault is empty first ensures that we don't try to unlock a vault that may be locked but empty, which can happen in some cases.

src/composables/session-vault.ts

_40
import { BrowserVault, DeviceSecurityType, Vault, VaultType } from '@ionic-enterprise/identity-vault';
_40
import { useVaultFactory } from '@/composables/vault-factory';
_40
import { Session } from '@/models/session';
_40
import { ref } from 'vue';
_40
_40
const { createVault } = useVaultFactory();
_40
const vault: Vault | BrowserVault = createVault();
_40
const session = ref<Session | null>(null);
_40
_40
const initializeVault = async (): Promise<void> => {
_40
try {
_40
session.value = await vault.getValue<Session>('session');

Call the function from Tab1Page.

src/views/Tab1Page.vue

_61
<template>
_61
<ion-page>
_61
<ion-header>
_61
<ion-toolbar>
_61
<ion-title>Tab 1</ion-title>
_61
</ion-toolbar>
_61
</ion-header>
_61
<ion-content :fullscreen="true">
_61
<ion-header collapse="condense">
_61
<ion-toolbar>
_61
<ion-title size="large">Tab 1</ion-title>
_61
</ion-toolbar>
_61
</ion-header>
_61
const { getSession, session, storeSession } = useSessionVault();

We now have a way to store and retrieve the session. When you first run the application, the session area will be blank. When you press the Store button you will see the session information on the page. If you restart the application, you will see the session information.

If you would like to clear the session information at this point, remove the application from your device (physical or simulated) and re-install it. In the web, you can close the running tab and open new one.

Next we will see how to remove this data from within our application.

Remove the Session from the Vault

The vault has two different methods that we can use to remove the data:

  • clear: Clear all of the data stored in the vault and remove the vault from the keystore / keychain.
    • This operation does not require the vault to be unlocked.
    • This operation will remove the existing vault from the keychain / keystore.
    • Subsequent operations on the vault such as storing a new session will not require the vault to be unlocked since the vault had been removed.
    • Use this method if your vault stores a single logical entity, even if it uses multiple entries to do so.
  • removeValue: Clear just the data stored with the specified key.
    • This operation does require the vault to be unlocked.
    • This operation will not remove the existing vault from the keychain / keystore even though the vault may be empty.
    • Subsequent operations on the vault such as storing a new session may require the vault to be unlocked since the vault had been removed.
    • Use this method if your vault stores multiple logical entities.

Note: We will address locking and unlocking a vault later in this tutorial.

Our vault stores session information. Having a single vault that stores only the session information is the best-practice for this type of data, and it is the practice we are using here. Thus we will use the clear() method to clear the session.

src/composables/session-vault.ts

_46
import { BrowserVault, DeviceSecurityType, Vault, VaultType } from '@ionic-enterprise/identity-vault';
_46
import { useVaultFactory } from '@/composables/vault-factory';
_46
import { Session } from '@/models/session';
_46
import { ref } from 'vue';
_46
_46
const { createVault } = useVaultFactory();
_46
const vault: Vault | BrowserVault = createVault();
_46
const clearSession = async (): Promise<void> => {

Add a "Clear" button to the Tab1Page.

src/views/Tab1Page.vue

_69
<template>
_69
<ion-page>
_69
<ion-header>
_69
<ion-toolbar>
_69
<ion-title>Tab 1</ion-title>
_69
</ion-toolbar>
_69
</ion-header>
_69
<ion-content :fullscreen="true">
_69
<ion-header collapse="condense">
_69
<ion-toolbar>
_69
<ion-title size="large">Tab 1</ion-title>
_69
</ion-toolbar>
_69
</ion-header>
_69
_69
<ion-list>
_69
<ion-item>
_69
<ion-label>
_69
<ion-button expand="block" @click="storeClicked">Store</ion-button>
_69
</ion-label>
_69
</ion-item>
_69
<ion-item>
_69
<ion-label>
_69
<ion-button expand="block" color="danger" @click="clearSession">Clear</ion-button>
_69
</ion-label>
_69
</ion-item>
_69
<ion-item>
_69
<div>
_69
<div>{{ session?.email }}</div>
_69
<div>{{ session?.firstName }} {{ session?.lastName }}</div>
_69
<div>{{ session?.accessToken }}</div>
_69
<div>{{ session?.refreshToken }}</div>
_69
</div>
_69
</ion-item>
_69
</ion-list>
_69
</ion-content>
_69
</ion-page>
_69
</template>
_69
_69
<script setup lang="ts">
_69
import {

Updated the Vault Type

We are currently using a "Secure Storage" vault, but there are several other vault types. In this section, we will explore the DeviceSecurity, InMemory, and SecureStorage types.

Setting the Vault Type

We can use the vault's updateConfig() method to change the type of vault that the application is using..

src/composables/session-vault.ts

_46
import { BrowserVault, DeviceSecurityType, Vault, VaultType } from '@ionic-enterprise/identity-vault';
_46
import { useVaultFactory } from '@/composables/vault-factory';
_46
import { Session } from '@/models/session';
_46
import { ref } from 'vue';
_46
_46
const { createVault } = useVaultFactory();
_46
const vault: Vault | BrowserVault = createVault();
_46
const session = ref<Session | null>(null);
_46
_46
const initializeVault = async (): Promise<void> => {
_46
try {
_46
await vault.initialize({
_46
key: 'io.ionic.gettingstartediv',
_46
type: VaultType.SecureStorage,
_46
deviceSecurityType: DeviceSecurityType.None,
_46
});
_46
} catch (e: unknown) {
_46
await vault.clear();
_46
}
_46
};
_46
_46
const storeSession = async (s: Session): Promise<void> => {
_46
vault.setValue('session', s);
_46
session.value = s;
_46
};
_46
_46
const getSession = async (): Promise<void> => {
_46
if (await vault.isEmpty()) {
_46
session.value = null;
_46
} else {
_46
session.value = await vault.getValue<Session>('session');
_46
}
_46
};
_46
_46
const clearSession = async (): Promise<void> => {
_46
await vault.clear();
_46
session.value = null;
_46
};
_46
_46
export const useSessionVault = (): any => ({
_46
clearSession,
_46
getSession,
_46
initializeVault,
_46
session,
_46
storeSession,
_46
});

Here is the src/composables/session-vault.ts that we have created thus far.

src/composables/session-vault.ts

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

The UnlockMode specifies the logical combinations of settings we wish to support within our application.

src/composables/session-vault.ts

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

Add an updateUnlockMode() function. Take a single argument for the mode.

src/composables/session-vault.ts

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

The vault's updateConfig() method takes a full vault configuration object, so pass our current config. Cast it to IdentityVaultConfig to signify that we know the value is not undefined at this point.

src/composables/session-vault.ts

_68
import {
_68
BrowserVault,
_68
DeviceSecurityType,
_68
IdentityVaultConfig,
_68
Vault,
_68
VaultType,
_68
} from '@ionic-enterprise/identity-vault';
_68
import { useVaultFactory } from '@/composables/vault-factory';
_68
import { Session } from '@/models/session';
_68
import { ref } from 'vue';
_68
_68
export type UnlockMode = 'BiometricsWithPasscode' | 'InMemory' | 'SecureStorage';
_68
_68
const { createVault } = useVaultFactory();
_68
const vault: Vault | BrowserVault = createVault();
_68
const session = ref<Session | null>(null);
_68
_68
const initializeVault = async (): Promise<void> => {
_68
try {
_68
await vault.initialize({
_68
key: 'io.ionic.gettingstartediv',
_68
type: VaultType.SecureStorage,
_68
deviceSecurityType: DeviceSecurityType.None,
_68
});
_68
} catch (e: unknown) {
_68
await vault.clear();
_68
}
_68
};
_68
_68
const storeSession = async (s: Session): Promise<void> => {
_68
vault.setValue('session', s);
_68
session.value = s;
_68
};
_68
_68
const getSession = async (): Promise<void> => {
_68
if (await vault.isEmpty()) {
_68
session.value = null;
_68
} else {
_68
session.value = await vault.getValue<Session>('session');
_68
}
_68
};
_68
_68
const clearSession = async (): Promise<void> => {
_68
await vault.clear();
_68
session.value = null;
_68
};
_68
_68
const updateUnlockMode = async (mode: UnlockMode): Promise<void> => {
_68
const type =
_68
mode === 'BiometricsWithPasscode'
_68
? VaultType.DeviceSecurity
_68
: mode === 'InMemory'
_68
? VaultType.InMemory
_68
: VaultType.SecureStorage;
_68
await vault.updateConfig({
_68
...(vault.config as IdentityVaultConfig),
_68
type,
_68
});
_68
};
_68
_68
export const useSessionVault = (): any => ({
_68
clearSession,
_68
getSession,
_68
initializeVault,
_68
session,
_68
storeSession,
_68
updateUnlockMode,
_68
});

Update the type based on the specified mode.

src/composables/session-vault.ts

_70
import {
_70
BrowserVault,
_70
DeviceSecurityType,
_70
IdentityVaultConfig,
_70
Vault,
_70
VaultType,
_70
} from '@ionic-enterprise/identity-vault';
_70
import { useVaultFactory } from '@/composables/vault-factory';
_70
import { Session } from '@/models/session';
_70
import { ref } from 'vue';
_70
_70
export type UnlockMode = 'BiometricsWithPasscode' | 'InMemory' | 'SecureStorage';
_70
_70
const { createVault } = useVaultFactory();
_70
const vault: Vault | BrowserVault = createVault();
_70
const session = ref<Session | null>(null);
_70
_70
const initializeVault = async (): Promise<void> => {
_70
try {
_70
await vault.initialize({
_70
key: 'io.ionic.gettingstartediv',
_70
type: VaultType.SecureStorage,
_70
deviceSecurityType: DeviceSecurityType.None,
_70
});
_70
} catch (e: unknown) {
_70
await vault.clear();
_70
}
_70
};
_70
_70
const storeSession = async (s: Session): Promise<void> => {
_70
vault.setValue('session', s);
_70
session.value = s;
_70
};
_70
_70
const getSession = async (): Promise<void> => {
_70
if (await vault.isEmpty()) {
_70
session.value = null;
_70
} else {
_70
session.value = await vault.getValue<Session>('session');
_70
}
_70
};
_70
_70
const clearSession = async (): Promise<void> => {
_70
await vault.clear();
_70
session.value = null;
_70
};
_70
_70
const updateUnlockMode = async (mode: UnlockMode): Promise<void> => {
_70
const type =
_70
mode === 'BiometricsWithPasscode'
_70
? VaultType.DeviceSecurity
_70
: mode === 'InMemory'
_70
? VaultType.InMemory
_70
: VaultType.SecureStorage;
_70
const deviceSecurityType = type === VaultType.DeviceSecurity ? DeviceSecurityType.Both : DeviceSecurityType.None;
_70
await vault.updateConfig({
_70
...(vault.config as IdentityVaultConfig),
_70
type,
_70
deviceSecurityType,
_70
});
_70
};
_70
_70
export const useSessionVault = (): any => ({
_70
clearSession,
_70
getSession,
_70
initializeVault,
_70
session,
_70
storeSession,
_70
updateUnlockMode,
_70
});

Update the deviceSecurityType based on the value of the type.

src/composables/session-vault.ts

_71
import {
_71
BrowserVault,
_71
DeviceSecurityType,
_71
IdentityVaultConfig,
_71
Vault,
_71
VaultType,
_71
} from '@ionic-enterprise/identity-vault';
_71
import { useVaultFactory } from '@/composables/vault-factory';
_71
import { Session } from '@/models/session';
_71
import { ref } from 'vue';
_71
_71
export type UnlockMode = 'BiometricsWithPasscode' | 'InMemory' | 'SecureStorage';
_71
_71
const { createVault } = useVaultFactory();
_71
const vault: Vault | BrowserVault = createVault();
_71
const session = ref<Session | null>(null);
_71
_71
const initializeVault = async (): Promise<void> => {
_71
try {
_71
await vault.initialize({
_71
key: 'io.ionic.gettingstartediv',
_71
type: VaultType.SecureStorage,
_71
deviceSecurityType: DeviceSecurityType.None,
_71
});
_71
} catch (e: unknown) {
_71
await vault.clear();
_71
await updateUnlockMode('SecureStorage');
_71
}
_71
};
_71
_71
const storeSession = async (s: Session): Promise<void> => {
_71
vault.setValue('session', s);
_71
session.value = s;
_71
};
_71
_71
const getSession = async (): Promise<void> => {
_71
if (await vault.isEmpty()) {
_71
session.value = null;
_71
} else {
_71
session.value = await vault.getValue<Session>('session');
_71
}
_71
};
_71
_71
const clearSession = async (): Promise<void> => {
_71
await vault.clear();
_71
session.value = null;
_71
};
_71
_71
const updateUnlockMode = async (mode: UnlockMode): Promise<void> => {
_71
const type =
_71
mode === 'BiometricsWithPasscode'
_71
? VaultType.DeviceSecurity
_71
: mode === 'InMemory'
_71
? VaultType.InMemory
_71
: VaultType.SecureStorage;
_71
const deviceSecurityType = type === VaultType.DeviceSecurity ? DeviceSecurityType.Both : DeviceSecurityType.None;
_71
await vault.updateConfig({
_71
...(vault.config as IdentityVaultConfig),
_71
type,
_71
deviceSecurityType,
_71
});
_71
};
_71
_71
export const useSessionVault = (): any => ({
_71
clearSession,
_71
getSession,
_71
initializeVault,
_71
session,
_71
storeSession,
_71
updateUnlockMode,
_71
});

If the vault.initialize() fails use the new updateUnlockMode() to reset the vault's configuration to SecureStorage.

Here is the src/composables/session-vault.ts that we have created thus far.

The UnlockMode specifies the logical combinations of settings we wish to support within our application.

Add an updateUnlockMode() function. Take a single argument for the mode.

The vault's updateConfig() method takes a full vault configuration object, so pass our current config. Cast it to IdentityVaultConfig to signify that we know the value is not undefined at this point.

Update the type based on the specified mode.

Update the deviceSecurityType based on the value of the type.

If the vault.initialize() fails use the new updateUnlockMode() to reset the vault's configuration to SecureStorage.

src/composables/session-vault.ts

_46
import { BrowserVault, DeviceSecurityType, Vault, VaultType } from '@ionic-enterprise/identity-vault';
_46
import { useVaultFactory } from '@/composables/vault-factory';
_46
import { Session } from '@/models/session';
_46
import { ref } from 'vue';
_46
_46
const { createVault } = useVaultFactory();
_46
const vault: Vault | BrowserVault = createVault();
_46
const session = ref<Session | null>(null);
_46
_46
const initializeVault = async (): Promise<void> => {
_46
try {
_46
await vault.initialize({
_46
key: 'io.ionic.gettingstartediv',
_46
type: VaultType.SecureStorage,
_46
deviceSecurityType: DeviceSecurityType.None,
_46
});
_46
} catch (e: unknown) {
_46
await vault.clear();
_46
}
_46
};
_46
_46
const storeSession = async (s: Session): Promise<void> => {
_46
vault.setValue('session', s);
_46
session.value = s;
_46
};
_46
_46
const getSession = async (): Promise<void> => {
_46
if (await vault.isEmpty()) {
_46
session.value = null;
_46
} else {
_46
session.value = await vault.getValue<Session>('session');
_46
}
_46
};
_46
_46
const clearSession = async (): Promise<void> => {
_46
await vault.clear();
_46
session.value = null;
_46
};
_46
_46
export const useSessionVault = (): any => ({
_46
clearSession,
_46
getSession,
_46
initializeVault,
_46
session,
_46
storeSession,
_46
});

Why the UnlockMode?

One natural question from above may be "why create an UnlockMode type when you can pass in the VaultType and figure things out from there?" The answer to that is that any time you incorporate a third-party library into your code like this, you should create an "adapter" service that utilizes the library within the domain of your application.

This has two major benefits:

  1. It insulates the rest of the application from change. If the next major version of Identity Vault has breaking changes that need to be addressed, the only place in the code they need to be addressed is in this service. The rest of the code continues to interact with the vault via the interface defined by the service.
  2. It reduces vendor tie-in, making it easier to swap to different libraries in the future if need be.

The ultimate goal is for the only modules in the application directly import from @ionic-enterprise/identity-vault to be services like this one that encapsulate operations on a vault.

Setting the deviceSecurityType Value

The deviceSecurityType property only applies when the type is set to DeviceSecurity. We could use any of the following DeviceSecurityType values:

  • Biometrics: Use the system's default biometric option to unlock the vault.
  • SystemPasscode: Use the system's designated system passcode (PIN, Pattern, etc.) to unlock the vault.
  • Both: Primarily use the biometric hardware to unlock the vault, but use the system passcode as a backup for cases where the biometric hardware is not configured or biometric authentication has failed.

For our application, we will just keep it simple and use Both when using DeviceSecurity vault. This is a very versatile option and makes the most sense for most applications.

With vault types other than DeviceSecurity, always use DeviceSecurityType.None.

Update the Tab1Page

We can now add some buttons to the Tab1Page in order to try out the different vault types. Update the src/views/Tab1Page.vue as shown below.

src/views/Tab1Page.vue

_88
<template>
_88
<ion-page>
_88
<ion-header>
_88
<ion-toolbar>
_88
<ion-title>Tab 1</ion-title>
_88
</ion-toolbar>
_88
</ion-header>
_88
<ion-content :fullscreen="true">
_88
<ion-header collapse="condense">
_88
<ion-toolbar>
_88
<ion-title size="large">Tab 1</ion-title>
_88
</ion-toolbar>
_88
</ion-header>
_88
_88
<ion-list>
_88
<ion-item>
_88
<ion-label>
_88
<ion-button expand="block" @click="storeClicked">Store</ion-button>
_88
</ion-label>
_88
</ion-item>
_88
<ion-item>
_88
<ion-label>
_88
<ion-button expand="block" color="danger" @click="clearSession">Clear</ion-button>
_88
</ion-label>
_88
</ion-item>
_88
<ion-item>
_88
<ion-label>
_88
<ion-button expand="block" color="secondary" @click="updateUnlockMode('BiometricsWithPasscode')"
_88
>Use Biometrics</ion-button
_88
>
_88
</ion-label>
_88
</ion-item>
_88
<ion-item>
_88
<ion-label>
_88
<ion-button expand="block" color="secondary" @click="updateUnlockMode('InMemory')"
_88
>Use In Memory</ion-button
_88
>
_88
</ion-label>
_88
</ion-item>
_88
<ion-item>
_88
<ion-label>
_88
<ion-button expand="block" color="secondary" @click="updateUnlockMode('SecureStorage')"
_88
>Use Secure Storage</ion-button
_88
>
_88
</ion-label>
_88
</ion-item>
_88
<ion-item>
_88
<div>
_88
<div>{{ session?.email }}</div>
_88
<div>{{ session?.firstName }} {{ session?.lastName }}</div>
_88
<div>{{ session?.accessToken }}</div>
_88
<div>{{ session?.refreshToken }}</div>
_88
</div>
_88
</ion-item>
_88
</ion-list>
_88
</ion-content>
_88
</ion-page>
_88
</template>
_88
_88
<script setup lang="ts">
_88
import {
_88
IonButton,
_88
IonContent,
_88
IonItem,
_88
IonLabel,
_88
IonList,
_88
IonPage,
_88
IonHeader,
_88
IonTitle,
_88
IonToolbar,

Build the application and run it on a device upon which you have biometrics enabled. Perform the following steps for each type of vault:

  1. Press the "Store" button to put data in the vault.
  2. Choose a vault type via one of the "Use" buttons.
  3. Close the application (do not just put it in the background, but close it).
  4. Restart the application.

You should see the following results:

  • "Use Biometrics": On an iPhone with FaceID, this will fail. We will fix that next. On all other devices, however, a biometric prompt will be displayed to unlock the vault. The data will be displayed once the vault is unlocked.
  • "Use In Memory": The data is no longer set. As the name implies, there is no persistence of this data.
  • "Use Secure Storage": The stored data is displayed without unlocking.

Native Configuration

If you tried the tests above on an iPhone with Face ID, your app should have crashed upon restarting when using a biometric vault. If you run npx cap sync you will see what is missing.


_10
[warn] Configuration required for @ionic-enterprise/identity-vault.
_10
Add the following to Info.plist:
_10
<key>NSFaceIDUsageDescription</key>
_10
<string>Use Face ID to authenticate yourself and login</string>

Open the ios/App/App/Info.plist file and add the specified configuration. The actual string value can be anything you want, but the key must be NSFaceIDUsageDescription.

ios/App/App/Info.plist

_51
<?xml version="1.0" encoding="UTF-8"?>
_51
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
_51
<plist version="1.0">
_51
<dict>
_51
<key>CFBundleDevelopmentRegion</key>
_51
<string>Use Face ID to authenticate yourself and login</string>

Biometrics should work on the iPhone at this point.

Locking and Unlocking the Vault

Going forward we will begin exploring functionality that only works when the application is run on a device. As such, you should begin testing on a device instead of using the development server.

Right now, the only way to "lock" the vault is to close the application. In this section we will look at a couple of other ways to lock the vault as well as ways to unlock it.

Manually Locking the Vault

In src/composables/session-vault.ts, wrap the vault's lock() method so we can use it in our Tab1Page.

src/composables/session-vault.ts

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

Add a lock button in src/views/Tab1Page.vue.

src/views/Tab1Page.vue

_92
<template>
_92
<ion-page>
_92
<ion-header>
_92
<ion-toolbar>
_92
<ion-title>Tab 1</ion-title>
_92
</ion-toolbar>
_92
</ion-header>
_92
<ion-content :fullscreen="true">
_92
<ion-header collapse="condense">
_92
<ion-toolbar>
_92
<ion-title size="large">Tab 1</ion-title>
_92
</ion-toolbar>
_92
</ion-header>
_92
_92
<ion-list>
_92
<ion-item>
_92
<ion-label>
_92
<ion-button expand="block" @click="storeClicked">Store</ion-button>
_92
</ion-label>
_92
</ion-item>
_92
<ion-item>
_92
<ion-label>
_92
<ion-button expand="block" color="danger" @click="clearSession">Clear</ion-button>
_92
</ion-label>
_92
</ion-item>
_92
<ion-item>
_92
<ion-label>
_92
<ion-button expand="block" color="secondary" @click="updateUnlockMode('BiometricsWithPasscode')"
_92
>Use Biometrics</ion-button
_92
>
_92
</ion-label>
_92
</ion-item>
_92
<ion-item>
_92
<ion-label>
_92
<ion-button expand="block" color="secondary" @click="updateUnlockMode('InMemory')"
_92
>Use In Memory</ion-button
_92
>
_92
</ion-label>
_92
</ion-item>
_92
<ion-item>
_92
<ion-label>
_92
<ion-button expand="block" color="secondary" @click="updateUnlockMode('SecureStorage')"
_92
>Use Secure Storage</ion-button
_92
>
_92
</ion-label>
_92
</ion-item>
_92
<ion-item>
_92
<ion-label>
_92
<ion-button expand="block" color="warning" @click="lockSession">Lock</ion-button>
_92
</ion-label>
_92
</ion-item>
_92
<ion-item>
_92
<div>
_92
<div>{{ session?.email }}</div>
_92
<div>{{ session?.firstName }} {{ session?.lastName }}</div>
_92
<div>{{ session?.accessToken }}</div>
_92
<div>{{ session?.refreshToken }}</div>
_92
</div>
_92
</ion-item>
_92
</ion-list>
_92
</ion-content>
_92
</ion-page>
_92
</template>
_92
_92
<script setup lang="ts">
_92
import {
_92
IonButton,
_92
const { clearSession, getSession, lockSession, storeSession, updateUnlockMode } = useSessionVault();

When we press the "Lock" button, the session data is no longer displayed. The actual status of the vault depends on the last "unlock mode" button pressed prior to locking the vault.

  • "Use Biometrics": The vault has been locked and the session data will not be accessible until it is unlocked.
  • "Use In Memory": The session data no longer exists.
  • "Use In Secure Storage": The session data is in the vault, but is not locked.

Unlocking the Vault

To verify the behaviors noted above, you need to be able to unlock the vault. To do this you can use the vault's unlock() method or you can perform an operation that requires the vault to be unlocked. When we unlock the vault, we need to restore the session data in our page, so we can just use our getSession() function. When it calls the vault's getValue(), the getValue() will attempt to unlock the vault.

Add the following code to src/views/Tab1Page.vue:

src/views/Tab1Page.vue

_97
<template>
_97
<ion-page>
_97
<ion-header>
_97
<ion-toolbar>
_97
<ion-title>Tab 1</ion-title>
_97
</ion-toolbar>
_97
</ion-header>
_97
<ion-content :fullscreen="true">
_97
<ion-header collapse="condense">
_97
<ion-button expand="block" color="warning" @click="getSession">Unlock</ion-button>

We can now use the "Lock" and "Unlock" buttons to verify the behavior of each of our unlock modes.

Locking in the Background

We can manually lock our vault, but it would be nice if the vault locked for us automatically. This can be accomplished by setting lockAfterBackgrounded which will lock the vault when the application is resumed, if the app was backgrounded for the configured amount of time. We can configure this by doing two actions when initializing the vault:

  • Set the lockAfterBackgrounded value in the config. This value is specified in milliseconds.
  • Set the onLock callback so the session is cleared on lock.
src/composables/session-vault.ts

_80
import {
_80
BrowserVault,
_80
DeviceSecurityType,
_80
IdentityVaultConfig,
_80
Vault,
_80
VaultType,
_80
} from '@ionic-enterprise/identity-vault';
_80
import { useVaultFactory } from '@/composables/vault-factory';
_80
import { Session } from '@/models/session';
_80
import { ref } from 'vue';
_80
_80
export type UnlockMode = 'BiometricsWithPasscode' | 'InMemory' | 'SecureStorage';
_80
_80
vault.onLock(() => (session.value = null));

Architectural Considerations

Construction vs. Initialization

Have a look at the src/composables/session-vault.ts file. Notice that it is very intentional about separating construction and initialization. This is very important.

Identity Vault allows you to pass the configuration object via the new Vault(cfg) constructor. This, however, will make asynchronous calls which makes construction indeterminate. This is bad.

Always use a pattern of:

  • Construct the vault via new Vault() (default constructor, no configuration).
  • Pass the configuration to the vault.initialize(cfg) function.
  • Perform the initialization itself prior to mounting the application and make sure that the code is properly awaiting its completion.

Control Unlocking on Startup and Navigation

Our code is currently automatically unlocking the vault upon startup due to the code in ngOnInit(). This is OK for our app, but it could be a problem if we had situations where multiple calls to get data from a locked vault all happened simultaneously. For example if we have AuthGuards and HTTP Interceptors also trying to access the vault at the same time. Always make sure you are controlling the vault lock status in such situations to ensure that only one unlock attempt is being made at a time.

We will see various strategies for this in later tutorials. You can also refer to our troubleshooting guide for further guidance.

Initial Vault Type Configuration

When we first initialize the vault we use the following configuration:


_10
await vault.initialize({
_10
key: 'io.ionic.gettingstartediv',
_10
type: VaultType.SecureStorage,
_10
deviceSecurityType: DeviceSecurityType.None,
_10
lockAfterBackgrounded: 2000,
_10
});

It is important to note that this is an initial configuration. Once a vault is created, it (and its current configuration) persist between invocations of the application. Thus, if the configuration of the vault is updated by the application, the updated configuration will be read when the application is reopened. For example, if the lockAfterBackgrounded has been updated to 5000 milliseconds, then when we start the application again with the vault already existing, lockAfterBackgrounded will remain set to 5000 milliseconds. The configuration we pass here is only used if we later destroy and re-create this vault.

Notice that we are specifying a type of VaultType.SecureStorage. It is best to use either VaultType.SecureStorage or VaultType.InMemeory when calling initialize() to avoid the potential of creating a vault of a type that cannot be supported. We can always update the type later after and the updated type will "stick." We want to start, however, with an option that will always word regardless of the device's configuration.

Single Vault vs Multiple Vaults

Identity Vault is ideal for storing small chunks of data such as authentication information or encryption keys. Our sample application contains a single vault. However, it may make sense to use multiple vaults within your application's architecture.

Ask yourself the following questions:

  1. What type of data is stored?
  2. Under what conditions should the data be available to the application?

Let's say the application is storing the following information:

  • The authentication session data.
  • A set of encryption keys.

You can use a single vault to store this data if all of the following are true:

  • You only want to access the vault via a single service.
  • The requirements for when the data is accessible is identical.

You should use multiple vaults to store this data if any of the following are true:

  • You logically want to use different services for different types of data.
  • You logically would like to use different services to access different types of data.
  • The requirements for when the data is accessible differs in some way. For example, perhaps the authentication information is locked behind a biometric key while access to the encryption keys requires a custom set in-app code to be entered.

If you decide to use multiple vaults, a best-practice is to create a separate service for each vault. That is, in the interest of proper organization within your code, each vault service should only manage a single vault.