PasskeyBase

Vue SDK

The @passkeybase/vue package provides a Vue 3 plugin, composables, and components for integrating passkey authentication into Vue applications.


Installation

Terminal
npm install @passkeybase/vue @passkeybase/js

Plugin Setup

Register the plugin with app.use(). This initializes the SDK and makes auth state available to all components via Vue's provide/inject system.

main.ts
import { createApp } from 'vue';
import { createPasskeyBase } from '@passkeybase/vue';
import App from './App.vue';

const app = createApp(App);

app.use(
  createPasskeyBase({
    clientId: 'pk_live_abc123',
    domain: 'myapp.auth.ing',
    redirectUri: window.location.origin + '/callback',
  })
);

app.mount('#app');

Auth Composables

useAuth()

Access reactive authentication state and methods. All returned values are Vue Refs that update automatically.

Dashboard.vue
<script setup lang="ts">
import { useAuth } from '@passkeybase/vue';

const {
  isAuthenticated,
  isLoading,
  loginWithRedirect,
  logout,
  getAccessToken,
} = useAuth();

async function fetchData() {
  const token = await getAccessToken();
  const res = await fetch('/api/data', {
    headers: { Authorization: `Bearer ${token}` },
  });
  return res.json();
}
</script>

<template>
  <div v-if="isLoading">Loading...</div>
  <div v-else-if="!isAuthenticated">
    <button @click="loginWithRedirect()">Log in with Passkey</button>
  </div>
  <div v-else>
    <h1>Welcome back</h1>
    <button @click="fetchData()">Fetch data</button>
    <button @click="logout()">Log out</button>
  </div>
</template>

useUser()

Access the current user's profile as a reactive ref.

Profile.vue
<script setup lang="ts">
import { useUser } from '@passkeybase/vue';

const { user, isLoading } = useUser();
</script>

<template>
  <div v-if="isLoading">Loading profile...</div>
  <div v-else-if="user">
    <h2>{{ user.displayName }}</h2>
    <p>{{ user.email }}</p>
    <p>User ID: {{ user.sub }}</p>
    <p>Passkeys registered: {{ user.credentials.length }}</p>
  </div>
</template>

Protected Routes

AuthGuard Component

Wrap any component tree that should only be visible to authenticated users. The fallback slot is shown while the auth state is being determined.

ProtectedView.vue
<script setup lang="ts">
import { AuthGuard } from '@passkeybase/vue';
</script>

<template>
  <AuthGuard>
    <!-- Only rendered when authenticated -->
    <DashboardView />

    <!-- Optional: shown while auth check is in progress -->
    <template #fallback>
      <div>Checking authentication...</div>
    </template>
  </AuthGuard>
</template>

Vue Router Navigation Guard

Protect routes at the router level using a beforeEach guard. Mark routes with meta.requiresAuth.

router.ts
import { createRouter, createWebHistory } from 'vue-router';
import { useAuth } from '@passkeybase/vue';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: () => import('./views/Home.vue') },
    { path: '/callback', component: () => import('./views/Callback.vue') },
    {
      path: '/dashboard',
      component: () => import('./views/Dashboard.vue'),
      meta: { requiresAuth: true },
    },
    {
      path: '/settings',
      component: () => import('./views/Settings.vue'),
      meta: { requiresAuth: true },
    },
  ],
});

router.beforeEach(async (to) => {
  if (to.meta.requiresAuth) {
    const { isAuthenticated, loginWithRedirect } = useAuth();
    if (!isAuthenticated.value) {
      await loginWithRedirect({ state: to.fullPath });
      return false;
    }
  }
});

export default router;

Login and Logout

Use the useAuth() composable for login and logout buttons, or build a custom UI.

NavBar.vue
<script setup lang="ts">
import { useAuth } from '@passkeybase/vue';

const { isAuthenticated, loginWithRedirect, logout } = useAuth();

function handleLogin() {
  loginWithRedirect({
    scopes: ['openid', 'profile', 'email'],
  });
}

function handleLogout() {
  logout({ returnTo: window.location.origin });
}
</script>

<template>
  <nav>
    <span>My App</span>
    <button v-if="isAuthenticated" @click="handleLogout">Sign out</button>
    <button v-else @click="handleLogin">Sign in with Passkey</button>
  </nav>
</template>

Handling the Callback

Create a callback page that calls handleRedirectCallback() and redirects the user into your app.

Callback.vue
<script setup lang="ts">
import { onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { useAuth } from '@passkeybase/vue';

const { handleRedirectCallback } = useAuth();
const router = useRouter();

onMounted(async () => {
  const result = await handleRedirectCallback();
  const returnTo = result.state || '/dashboard';
  router.push(returnTo);
});
</script>

<template>
  <div>Completing login...</div>
</template>

Passkey Enrollment

The useEnrollment() composable manages the passkey enrollment popup flow, including reactive loading and error state.

PasskeySettings.vue
<script setup lang="ts">
import { useEnrollment, useAuth } from '@passkeybase/vue';

const { getAccessToken } = useAuth();
const { enrollPasskey, isEnrolling, error } = useEnrollment();

async function handleEnroll() {
  // Fetch an enrollment token from your backend
  const token = await getAccessToken();
  const res = await fetch('/api/enrollment-token', {
    method: 'POST',
    headers: { Authorization: `Bearer ${token}` },
  });
  const { enrollmentToken } = await res.json();

  // Open the enrollment popup
  await enrollPasskey(enrollmentToken);
  alert('Passkey added successfully!');
}
</script>

<template>
  <div>
    <h2>Passkey Management</h2>
    <button @click="handleEnroll" :disabled="isEnrolling">
      {{ isEnrolling ? 'Enrolling...' : 'Add a new passkey' }}
    </button>
    <p v-if="error" class="error">{{ error.message }}</p>
  </div>
</template>

TypeScript Support

All composables and components are fully typed. The plugin options, return types, and component props all ship with TypeScript definitions.

TypeScript types
import type { User, AuthResult } from '@passkeybase/js';
import { useAuth, useUser } from '@passkeybase/vue';

// Composables return typed Refs
const { user } = useUser();              // Ref<User | undefined>
const { isAuthenticated } = useAuth();   // Ref<boolean>

// Plugin options are typed
import type { PasskeyBasePluginOptions } from '@passkeybase/vue';

const options: PasskeyBasePluginOptions = {
  clientId: 'pk_live_abc123',
  domain: 'myapp.auth.ing',
  redirectUri: '/callback',
};

Next steps