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

@voult/next

v0.1.2

Published

Voult authentication for Next.js App Router: one catch-all route, getSession() for Server Components, and an Edge middleware guard

Readme

@voult/next

Voult authentication for the Next.js App Router (Next 14.2+): every auth route from one file, getSession() / requireSession() for Server Components, and an optional guard that redirects signed-out visitors.

Install

npm install @voult/next @voult/sdk @voult/react

.env.local (the same variables as @voult/express; npx voult init writes them):

VOULT_CLIENT_ID=
VOULT_CLIENT_SECRET=
VOULT_SESSION_SECRET=   # 32+ random characters: openssl rand -hex 32

Add http://localhost:3000/api/auth/oauth/callback under Callback URLs in the Voult dashboard (it also covers magic links).

1. The routes: one file

// app/api/auth/[...voult]/route.js
import { createVoultRouteHandlers } from '@voult/next';

export const { GET, POST, PATCH, DELETE } = createVoultRouteHandlers();

That serves the whole router at /api/auth: sign-up/in, sign-out, MFA, sessions, linked accounts, passkeys, magic links, hosted OAuth (/api/auth/oauth/google/start). It runs on the Node.js runtime (the default for route handlers). Mounted elsewhere? createVoultRouteHandlers({ basePath: '/auth' }).

2. The browser: @voult/react

// app/providers.jsx
'use client';
import { VoultProvider } from '@voult/react';
export default function Providers({ children }) {
  return <VoultProvider apiBase="/api/auth">{children}</VoultProvider>;
}

Wrap app/layout.jsx's body in <Providers>, then use useSession(), useVoult() and <OAuthButton> in client components as with any React app.

3. Server Components: getSession() / requireSession()

// app/account/page.jsx
import { requireSession } from '@voult/next';

export default async function Account() {
  const { user } = await requireSession(); // signed out → /login?returnTo=/account
  return <p>Signed in as {user.email}</p>;
}
  • getSession() → { user, verified } or null. verified: true means Voult confirmed the session on this request, so a revoked session is caught.
  • Why verified exists: access tokens live 15 minutes and Voult rotates the refresh token every time it's used. A Server Component can't write cookies, so it must not renew (the rotated token would be lost and Voult's reuse detection would then end the session). When the access token has expired there, getSession() returns the user from the signed session cookie with verified: false, and requireSession() sends the browser through /api/auth/session/renew, which renews in a route handler and comes straight back.
  • In route handlers and server actions, getSession() renews and sets the cookies itself.
  • requireSession({ signInPath: '/login' }): change the sign-in page.

After signing in or out, navigate with a full page load (window.location.assign(path)), not router.push(). In production Next prefetches links, so its client router may hold a response fetched while the visitor was signed out (such as the proxy's redirect to /login) and replay it after sign-in.

4. Optional: redirect signed-out visitors early

// proxy.js on Next 16 (middleware.js on Next 14–15)
import { voultMiddleware } from '@voult/next/middleware';

export default voultMiddleware({ publicPaths: ['/', '/login', '/blog/*'], signInPath: '/login' });
export const config = { matcher: ['/((?!_next/|favicon.ico|.*\\..*).*)'] };

Import it from @voult/next/middleware, not @voult/next: that entry point is Edge-safe (WebCrypto only, no @voult/sdk).

It only checks that a correctly signed session cookie exists, so it is a UX guard, not the authority: it can't see a revoked session. Pages that must be protected call requireSession(). It also records the request path so requireSession() can send users back after signing in. The auth routes and the sign-in page are always public, and /api/* is never redirected (a fetch() would get the sign-in page's HTML instead of your route's 401 JSON): API routes check getSession() themselves.

License

MIT