Create Biometric-Powered Login Pages in Vue.js
Anna Pobletts
Posted on February 8, 2022
In this article, you will learn how to build a simple Vue 3 application and add biometric authentication using Passage.
Users will log in to your application using the biometrics built into their devices (e.g. Face ID, Windows Hello, etc) or with magic links sent to their email. This app is built such that it only allows authenticated users to view a simple dashboard and blocks unauthenticated users. This guide will walk through creating a Vue 3 app using the Vue CLI, creating basic components, and adding authentication to the application using Passage.
If you are already familiar with Vue, you can go straight to our full example application on GitHub or skip to this section to learn how to integrate biometric authentication into an existing application.
Setup
To get started, first install the Vue CLI. The Vue CLI lets you get up and running quickly with pre-configured build settings.
npm install -g @vue/cli
Then create a new application using the Vue CLI. The tool will provide you with options to manually select versions and features you want. For this tutorial, use the "Manually select features" option and select the "Router" feature. Make sure to select Vue 3. You can just hit enter through the remaining features.
vue create example-app
cd example-app
You can test your app by running the following command and then visiting http://localhost:8080.
npm run serve
You can leave this up and running throughout the tutorial to see your progress.
Build Components for App
Set up routes for Home and Dashboard pages
Our application will have two pages, a home page with a login screen and a dashboard page that is authenticated. First, create the directory structure and routes for those pages. Create the following directories and files to set up for the router and our two main components.
cd src/
mkdir views
touch views/Dashboard.vue
touch views/Home.vue
Now let's start filling out these files. Copy the following code into the router/index.js file to replace the default router.
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import Dashboard from '../views/Dashboard.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard
}
]
const router = createRouter({
history: createWebHistory(),
mode: 'history',
routes
})
export default router
Create a Banner component
Create a banner component that will be used on both the home and dashboard pages. Copy the following code to components/Banner.vue.
<template>
<div class="mainHeader">
<a href="https://passage.id/"><div class="passageLogo"></div></a>
<div class="header-text">Passage + Vue.js 3 Example App</div>
<div class="spacer"></div>
<a class="link" href="https://passage.id/">Go to Passage</a>
</div>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Banner',
})
</script>
<style scoped>
.mainHeader{
width: 100%;
padding: 20px 30px;
display: flex;
align-items: center;
background-color: #27417E;
color: white;
}
.header-text {
font-size: 24px;
margin-left: 10px;
}
.passageLogo {
background-image: url('https://storage.googleapis.com/passage-docs/passage-logo.svg');
background-repeat: no-repeat;
width: 60px;
height: 60px;
cursor: pointer;
}
.spacer {
flex-grow: 1;
}
.link {
margin-left: 20px;
color: white;
text-decoration-color: white;
}
</style>
Replace the template and script tags in App.vue to use the router and add some simple styling.
<template>
<div>
<Banner />
<div class="main-container">
<router-view/>
</div>
<div className="footer">
Learn more with our
<a href="https://docs.passage.id">Documentation</a> and
<a href="https://github.com/passageidentity">Github</a>.
<br>
</div>
</div>
</template>
<style>
body {
margin: 0px;
height: 100vh;
font-family: sans-serif;
background-color: #E5E5E5;
}
.main-container {
background: white;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
border-radius: 20px;
width: 310px;
min-height: 310px;
margin: 30px auto;
}
.footer {
text-align: center;
font-size: 18px;
}
</style>
and add the router and banner to main.js.
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import Banner from './components/Banner.vue'
createApp(App)
.use(router)
.component('Banner', Banner)
.mount('#app')
This means that once the components are created, the home page will be accessible at http://localhost:8080/ and the dashboard will be at http://localhost:8080/dashboard.
Build Home Component
Add the following code to views/Home.vue to create the home page.
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Home',
})
</script>
Build Dashboard Component
Add the following code to views/Dashboard.vue to create the simple dashboard page.
<template>
<div class="dashboard">
<div class="title">Welcome!</div>
</div>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Dashboard',
})
</script>
<style scoped>
.dashboard{
padding: 30px 30px 20px;
}
.title {
font-size: 24px;
font-weight: 700;
margin-bottom: 30px;
}
.message {
overflow-wrap: anywhere;
}
.link {
color: black;
text-decoration-color: black;
}
</style>
Add Authentication with Passage
Now it is time to add authentication to our application using Passage! First, install Passage from the root directory of your example app.
npm install --save @passageidentity/passage-auth
Then import the package in the module where you intend to use the custom element, in this case the Home.vue view.
import '@passageidentity/passage-auth'
Importing this script will register the Passage custom element for use in your Vue components. For more information about custom elements refer to the online documentation.
Create an application in the Passage Console with the following settings:
- Authentication Origin:
http://localhost:8080
- Redirect URL:
/dashboard
Once you've created your application, copy your Application ID out the console and into a .env file in the root of your example repository.
# .env
VUE_APP_PASSAGE_APP_ID=
In the Home component, import Passage and add the custom element to the template.
<template>
<passage-auth :app-id="appId"></passage-auth>
</template>
<script>
import { defineComponent } from 'vue'
import '@passageidentity/passage-auth'
export default defineComponent({
name: 'Home',
setup() {
const appId = process.env.VUE_APP_PASSAGE_APP_ID
return {
appId,
}
},
})
</script>
Your application now has a full login and register experience!
You might notice a warning in the console about the custom element. Vue works with custom elements out of the box, but by default it will log a warning to the console that it could not resolve the component for the custom element. To configure Vue with information that the <passage-auth>
tag is a custom element and suppress this warning, you need to add this configuration to your vue.config.js file. Create this file at the top-level directory of your repository.
module.exports = {
publicPath: '/',
chainWebpack: (config) => {
config.module
.rule('vue')
.use('vue-loader')
.tap((options) => ({
...options,
compilerOptions: {
// treat any tag that starts with passage- as custom elements
isCustomElement: (tag) => tag.startsWith('passage-'),
},
}))
},
}
Once you’ve added this, you will need to restart the server for the changes to take effect.
Verifying User Authentication
Last but not least, the application needs to prevent unauthenticated users from accessing the dashboard page. You will set up protections that will show an error message to unauthenticated users trying to access the dashboard, but that does not prevent them from reading any data that might be on the dashboard, since it is all stored in the JavaScript files.
For simplicity, there is not a backend server in this example. A simple authentication check using the PassageUser
class will be implemented to protect the dashboard page from unauthorized access.
💡 Please keep in mind that this dashboard protection will not protect sensitive API endpoints. Your server should always use one of the Passage backend libraries to authorize users before returning sensitive data.
This check is implemented by creating a composable to check the authentication status of the current user using Passage. Create a file called useAuthStatus.js in the composables directory.
mkdir composables/
touch composables/useAuthStatus.js
Copy the following code into that file. This code uses Passage to check if the current user is authenticated.
import { ref } from 'vue'
import { PassageUser } from '@passageidentity/passage-auth/passage-user'
export function useAuthStatus(){
const isLoading = ref(true)
const isAuthorized = ref(false)
const username = ref('')
new PassageUser().userInfo().then(userInfo => {
if(userInfo === undefined){
isLoading.value = false
return
}
username.value = userInfo.email ? userInfo.email : userInfo.phone
isAuthorized.value = true
isLoading.value = false
})
return {
isLoading,
isAuthorized,
username,
}
}
Next, incorporate this check into the Dashboard component, since authentication is required before showing the dashboard. The dashboard will show two different messages based on the result of the authentication check. The final Dashboard.vue will look like this.
<template>
<div class="dashboard">
<div v-if="isLoading"/>
<div v-else-if="isAuthorized">
<div class="title">Welcome!</div>
<div class="message">
You successfully signed in with Passage.
<br/><br/>
Your Passage User ID is: <b>{{username}}</b>
</div>
</div>
<div v-else>
<div class="title">Unauthorized</div>
<div class="message">
You have not logged in and cannot view the dashboard.
<br/><br/>
<a href="/" class="link">Login to continue.</a>
</div>
</div>
</div>
</template>
<script>
import { defineComponent } from 'vue'
import { useAuthStatus } from '../composables/useAuthStatus'
export default defineComponent({
name: 'Dashboard',
setup() {
const {isLoading, isAuthorized, username} = useAuthStatus()
return {
isLoading,
isAuthorized,
username,
}
},
})
</script>
<style scoped>
.dashboard{
padding: 30px 30px 20px;
}
.title {
font-size: 24px;
font-weight: 700;
margin-bottom: 30px;
}
.message {
overflow-wrap: anywhere;
}
.link {
color: black;
text-decoration-color: black;
}
</style>
Unauthenticated users who try to visit /dashboard will be shown an "Unauthorized" message, while authorized users will see the dashboard that includes their Passage User ID.
Conclusion
Now you can try out the biometrics authentication in the application you just built! Your application should look something like this and you can see the login experience as your users would.
To recap, you have just:
- created an application with Vue.js
- added biometric authentication to your app with Passage
- learned how to verify the authentication status of your users with Passage
Keep an eye out for part 2 of this post, where we show you how to use Passage to protect your backend API endpoints in a Vue.js + Express.js web application!
To learn more about Passage and biometric authentication for web applications, you can:
- Explore our dashboard to view and create users, customize your application, and add friends
- Read our guides for other tech stacks and learn how to authorize requests in your backend server
- Join our Discord and say hi
Passage is in beta and actively seeking feedback on the product. If you have feedback, bug reports, or feature requests, we would love to hear from you. You can email me at anna@passage.id or fill out this form.
This article was originally published on the Passage blog.
Posted on February 8, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.