npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sp-uvb/nuxt

v0.1.0

Published

Nuxt module for Universal Verification Broker (UVB) authentication

Readme

@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 href="/login">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 href="/mfa/setup">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