Guide

Read the DID and protect routes.

Use the server session created by the VibeID callback to identify users, guard pages, and protect API routes.

Create one shared server helper

Keep your VibeID route handlers and session reader in the same module. The DID lives on the verified server session.

// lib/vibe-id.ts
import { createVibeIdRouteHandlers } from "@vibe-id/next";

export const vibeId = createVibeIdRouteHandlers({
  basePath: "/api/vibe-id",
  siteUrl: process.env.NEXT_PUBLIC_SITE_URL,
});

export const vibeAuth = vibeId.auth;

Protect a server-rendered page

Read the session from the HttpOnly cookie in a server component. Redirect when it is missing.

// app/account/page.tsx
import { redirect } from "next/navigation";
import { vibeAuth } from "@/lib/vibe-id";

export default async function AccountPage() {
  const session = await vibeAuth.getSessionFromCookies();

  if (!session) {
    redirect("/sign-in");
  }

  return (
    <main>
      <h1>Account</h1>
      <p>Signed in as {session.profile?.displayName ?? session.did}</p>
      <p>DID: {session.did}</p>
    </main>
  );
}

Protect an API route

Use the same session reader in route handlers. Return a 401 before touching user data.

// app/api/me/route.ts
import { NextResponse } from "next/server";
import { vibeAuth } from "@/lib/vibe-id";

export async function GET() {
  const session = await vibeAuth.getSessionFromCookies();

  if (!session) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  return NextResponse.json({
    did: session.did,
    profile: session.profile ?? null,
  });
}

Map a DID to your users

Treat the DID as the stable VibeID account key. Your app can create a new user for an unknown DID, link it to an existing user after another login method succeeds, or require an invite before allowing access.

const user = await db.user.findUnique({
  where: { vibeIdDid: session.did },
});

if (!user) {
  redirect("/finish-sign-up");
}

Use the session on the client

Client components can call the session endpoint for display state. Keep authorization decisions on the server.

"use client";

import { useVibeIdSignIn } from "@vibe-id/react";

export function SessionBadge() {
  const vibe = useVibeIdSignIn();

  if (!vibe.session) {
    return null;
  }

  return <span>{vibe.session.profile?.displayName ?? vibe.session.did}</span>;
}