@sp-uvb/nuxt

Nuxt module for Universal Verification Broker (UVB) authentication
586
Install
npm install @sp-uvb/nuxt
Latest Version:0.2.1
Vue:3
License:MIT
Last Updated:Jun 2, 2026
Links: GitLab  ·  npm
Maintainer: brycejohnson-sparkz

@sp-uvb/nuxt

Nuxt 3 module for Universal Verification Broker (UVB) authentication. Provides automatic server middleware, composables for client-side access, and utilities for API routes.

Installation

npm install @sp-uvb/nuxt
# or
yarn add @sp-uvb/nuxt
# or
pnpm add @sp-uvb/nuxt

Quick Start

1. Add Module to nuxt.config.ts

export default defineNuxtConfig({
  modules: ['@sp-uvb/nuxt'],

  uvb: {
    tenantId: 'my-tenant',
    uvbUrl: 'http://localhost:8080', // optional
    cookieName: 'uvb_session', // optional
    excludePaths: ['/login', '/api/auth'], // optional
    autoAuth: true, // optional, default: true
  },
});

2. Use in Pages

<script setup lang="ts">
const { session, isAuthenticated } = useUvbAuth();
</script>

<template>
  <div>
    <div v-if="isAuthenticated">
      <h1>Welcome {{ session?.userId }}</h1>
      <p>Factors: {{ session?.factorsVerified.join(', ') }}</p>
    </div>
    <div v-else>
      <a >Login</a>
    </div>
  </div>
</template>

3. Use in API Routes

// server/api/profile.ts
export default defineEventHandler((event) => {
  const session = requireUvbSession(event);

  return {
    userId: session.userId,
    factors: session.factorsVerified,
  };
});

API

Module Options

Configure in nuxt.config.ts:

export default defineNuxtConfig({
  uvb: {
    // Required: Your UVB tenant ID
    tenantId: string

    // Optional: UVB server URL (default: 'http://localhost:8080')
    uvbUrl?: string

    // Optional: API key for server-to-server auth
    apiKey?: string

    // Optional: Cookie name for session token (default: 'uvb_session')
    cookieName?: string

    // Optional: Paths to exclude from authentication (default: [])
    excludePaths?: string[]

    // Optional: Enable automatic auth on all routes (default: true)
    autoAuth?: boolean
  }
})

Composables

useUvbSession()

Get the current UVB session.

const session = useUvbSession();

// session.value contains:
// {
//   userId: string
//   tenantId: string
//   sessionId: string
//   factorsVerified: string[]
//   expiresAt: Date
// }
useUvbAuth()

Get authentication state and helpers.

const {
  session, // Ref<UvbSession | null>
  isAuthenticated, // ComputedRef<boolean>
  hasFactor, // (factor: string) => boolean
  hasAllFactors, // (factors: string[]) => boolean
  hasAnyFactor, // (factors: string[]) => boolean
} = useUvbAuth();

Server Utilities

getUvbSession(event)

Get session from event context (returns undefined if no session).

export default defineEventHandler((event) => {
  const session = getUvbSession(event);

  if (!session) {
    return { error: 'Not authenticated' };
  }

  return { userId: session.userId };
});
requireUvbSession(event)

Require session or throw 401 error.

export default defineEventHandler((event) => {
  const session = requireUvbSession(event);

  // If we get here, user is authenticated
  return { userId: session.userId };
});
requireFactors(event, factors)

Require specific MFA factors or throw 403 error.

export default defineEventHandler((event) => {
  const session = requireFactors(event, ['totp', 'webauthn']);

  // User has both TOTP and WebAuthn verified
  return { message: 'Admin action completed' };
});
hasFactor(event, factor)

Check if user has specific factor.

export default defineEventHandler((event) => {
  const hasTotp = hasFactor(event, 'totp');

  return { hasTotp };
});
isOwner(event, resourceUserId)

Check if current user owns a resource.

export default defineEventHandler(async (event) => {
  const post = await getPost(event.context.params.id);

  if (!isOwner(event, post.userId)) {
    throw createError({ statusCode: 403, message: 'Not your post' });
  }

  return post;
});
requireOwnership(event, resourceUserId)

Require ownership or throw 403 error.

export default defineEventHandler(async (event) => {
  const post = await getPost(event.context.params.id);
  requireOwnership(event, post.userId);

  // Delete post
  await deletePost(post.id);
  return { success: true };
});

Examples

Basic Authentication Page

<!-- pages/profile.vue -->
<script setup lang="ts">
const { session, isAuthenticated } = useUvbAuth();
const router = useRouter();

// Redirect to login if not authenticated
onMounted(() => {
  if (!isAuthenticated.value) {
    router.push('/login');
  }
});
</script>

<template>
  <div v-if="isAuthenticated">
    <h1>Profile</h1>
    <p>User ID: {{ session?.userId }}</p>
    <p>Session ID: {{ session?.sessionId }}</p>
    <p>Verified Factors: {{ session?.factorsVerified.join(', ') }}</p>
  </div>
</template>

Conditional UI Based on Factors

<!-- pages/admin.vue -->
<script setup lang="ts">
const { session, hasAllFactors } = useUvbAuth();

const hasAdminAuth = computed(() => hasAllFactors(['totp', 'webauthn']));
</script>

<template>
  <div>
    <div v-if="hasAdminAuth">
      <h1>Admin Panel</h1>
      <button @click="performAdminAction">Delete All Data</button>
    </div>
    <div v-else>
      <p>Additional authentication required</p>
      <a >Setup MFA</a>
    </div>
  </div>
</template>

Protected API Route

// server/api/admin/users.ts
export default defineEventHandler(async (event) => {
  // Require authentication
  const session = requireUvbSession(event);

  // Require strong MFA
  requireFactors(event, ['totp', 'webauthn']);

  // Fetch users
  const users = await fetchUsers();
  return users;
});

API Route with Optional Auth

// server/api/posts/[id].ts
export default defineEventHandler(async (event) => {
  const postId = event.context.params.id;
  const post = await getPost(postId);

  // Optional: Show extra data if authenticated
  const session = getUvbSession(event);
  if (session) {
    post.isOwner = post.userId === session.userId;
  }

  return post;
});

Conditional MFA Requirements

// server/api/transfer.post.ts
export default defineEventHandler(async (event) => {
  const session = requireUvbSession(event);
  const body = await readBody(event);

  // Require strong auth for large transfers
  if (body.amount > 10000) {
    requireFactors(event, ['totp', 'webauthn']);
  } else if (body.amount > 1000) {
    requireFactors(event, ['totp']);
  }

  // Process transfer
  return { success: true };
});

Resource Ownership Check

// server/api/posts/[id].delete.ts
export default defineEventHandler(async (event) => {
  const session = requireUvbSession(event);
  const postId = event.context.params.id;
  const post = await getPost(postId);

  // Ensure user owns the post
  requireOwnership(event, post.userId);

  await deletePost(postId);
  return { success: true };
});

Using Environment Variables

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@sp-uvb/nuxt'],

  uvb: {
    tenantId: process.env.UVB_TENANT_ID!,
    uvbUrl: process.env.UVB_URL || 'http://localhost:8080',
    apiKey: process.env.UVB_API_KEY,
    excludePaths: ['/login', '/register', '/api/auth'],
  },
});

Middleware for Protected Routes

// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
  const { isAuthenticated } = useUvbAuth();

  if (!isAuthenticated.value && to.path !== '/login') {
    return navigateTo('/login');
  }
});

Then use in pages:

<!-- pages/dashboard.vue -->
<script setup lang="ts">
definePageMeta({
  middleware: 'auth',
});
</script>

<template>
  <div>Protected Dashboard</div>
</template>

Server Middleware for Specific Routes

// server/middleware/admin-auth.ts
export default defineEventHandler((event) => {
  // Only apply to /api/admin/* routes
  if (!event.path.startsWith('/api/admin')) {
    return;
  }

  const session = requireUvbSession(event);
  requireFactors(event, ['totp', 'webauthn']);
});

Fetch with Session Token

// composables/useApi.ts
export function useApi() {
  const config = useRuntimeConfig();

  const fetch = async (url: string, options = {}) => {
    const token = useCookie(config.public.uvb.cookieName || 'uvb_session');

    return $fetch(url, {
      ...options,
      headers: {
        ...options.headers,
        Authorization: `Bearer ${token.value}`,
      },
    });
  };

  return { fetch };
}

Logout Function

// composables/useAuth.ts
export function useAuth() {
  const config = useRuntimeConfig();
  const session = useUvbSession();

  const logout = async () => {
    // Clear session cookie
    const cookie = useCookie(config.public.uvb.cookieName || 'uvb_session');
    cookie.value = null;

    // Clear local session state
    session.value = null;

    // Redirect to login
    await navigateTo('/login');
  };

  return { logout };
}

Disabling Auto Auth

// nuxt.config.ts - Disable automatic authentication
export default defineNuxtConfig({
  uvb: {
    tenantId: 'my-tenant',
    autoAuth: false, // Disable automatic middleware
  },
});

Then manually protect routes:

// server/api/protected.ts
import { getUvbSession } from '#imports';

export default defineEventHandler((event) => {
  // Manually validate session
  const session = getUvbSession(event);
  if (!session) {
    throw createError({ statusCode: 401, message: 'Unauthorized' });
  }

  return { data: 'protected' };
});

TypeScript

This module includes full TypeScript definitions:

import type { UvbSession } from '@sp-uvb/nuxt';

// Session type
interface UvbSession {
  userId: string;
  tenantId: string;
  sessionId: string;
  factorsVerified: string[];
  expiresAt: Date;
}

License

MIT

Related Packages

@sp-uvb/vue

Official Vue.js composables for Universal Verification Broker (UVB)

47,545
nuxt-basic-authentication-module

Basic Authentication Module for Nuxt 3

5,381 4
nuxt-supabase-team-auth

Drop-in Nuxt module for team-based authentication with Supabase

2,981 1
nuxt-plotly

nuxt-plotly module is thin Nuxt3 wrapper for plotly.js

35,721 32
nuxt-facebook-chat

Effortless integration of Facebook's Customer Chat SDK into Nuxt 3. Enhance user...

1,874 7

Version History

Version Released Vue License
0.2.1 ^3.0 MIT
0.2.0 ^3.0 MIT
0.1.0 ^3.0 MIT