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

vinextauth

v0.7.0

Published

Drop-in NextAuth v4 replacement for Vinext + Cloudflare Workers. Zero Node.js dependencies, pure Web Crypto API.

Readme

VinextAuth

Drop-in NextAuth v4 replacement for Vinext + Cloudflare Workers. Zero Node.js dependencies — pure Web Crypto API.

Features

  • Edge-ready — runs on Cloudflare Workers, Vinext edge runtime, and any Web Crypto environment
  • Zero dependencies — no jose, no cookie, no Node.js built-ins
  • Pages Router + App Router — native support for both via toPages() and auth()
  • Generic types — type your session without module augmentation
  • Multi-tenant — dynamic baseUrl per request
  • Database sessions — full adapter lifecycle (create, read, delete)
  • Credentials — built-in rate limiting (5 attempts / 15 min, pluggable store)
  • Account linking — safe explicit API
  • Server-side updateupdateSession() from Server Actions
  • Token refresh — race-condition-safe automatic rotation

Installation

npm install vinextauth

Quick start

Pages Router (recommended for Vinext)

1. Configure auth

// src/auth.ts
import VinextAuth from "vinextauth"
import Google from "vinextauth/providers/google"

export const { GET, POST, auth, toPages, pagesAuth } = VinextAuth({
  secret: process.env.VINEXTAUTH_SECRET!,
  providers: [
    Google({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
  ],
})

2. Create the catch-all API route

// src/pages/api/auth/[...vinextauth].ts
import { toPages } from "@/auth"

export default toPages()

3. Wrap your app with SessionProvider

// src/pages/_app.tsx
import type { AppProps } from "vinext/app"
import { SessionProvider } from "vinextauth/react"

export default function App({ Component, pageProps }: AppProps) {
  return (
    <SessionProvider session={pageProps.session}>
      <Component {...pageProps} />
    </SessionProvider>
  )
}

4. Protect a page server-side

// src/pages/dashboard.tsx
import type { GetServerSideProps } from "vinext"
import { pagesAuth } from "@/auth"

export const getServerSideProps: GetServerSideProps = async (ctx) => {
  const session = await pagesAuth(ctx.req)
  if (!session) return { redirect: { destination: "/api/auth/signin", permanent: false } }
  return { props: { session } }
}

App Router

// src/app/api/auth/[...vinextauth]/route.ts
import { GET, POST } from "@/auth"
export { GET, POST }
// src/app/dashboard/page.tsx (Server Component)
import { auth } from "@/auth"
import { redirect } from "vinext/navigation"

export default async function Dashboard() {
  const session = await auth()
  if (!session) redirect("/api/auth/signin")
  return <h1>Hello, {session.user.name}</h1>
}

Configuration

VinextAuth({
  providers: [...],

  // Secret — or set VINEXTAUTH_SECRET env var
  secret: process.env.VINEXTAUTH_SECRET,

  // Dynamic base URL for multi-tenant apps
  baseUrl: (req) => `https://${req.headers.get("host")}`,

  session: {
    strategy: "jwt",          // "jwt" | "database"
    maxAge: 30 * 24 * 3600,   // 30 days
    updateAge: 24 * 3600,
  },

  theme: {
    brandName: "My App",
    logoUrl: "/logo.png",
    buttonColor: "#6366f1",
  },

  pages: {
    signIn: "/login",
    error: "/auth/error",
  },

  callbacks: {
    async jwt({ token, user }) {
      if (user) token.role = user.role
      return token
    },
    async session({ session, token }) {
      session.user.role = token.role
      return session
    },
  },
})

Providers

Google

import Google from "vinextauth/providers/google"

Google({
  clientId: process.env.GOOGLE_CLIENT_ID!,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
})

GitHub

import GitHub from "vinextauth/providers/github"

GitHub({
  clientId: process.env.GITHUB_CLIENT_ID!,
  clientSecret: process.env.GITHUB_CLIENT_SECRET!,
})

Credentials

import Credentials from "vinextauth/providers/credentials"

Credentials({
  async authorize({ email, password }) {
    const user = await db.user.findByEmail(email)
    if (!user || !verifyPassword(password, user.passwordHash)) return null
    return { id: user.id, email: user.email, name: user.name }
  },
})

Rate limiting is built-in (5 attempts / 15 min per IP). Configure or replace:

VinextAuth({
  credentials: {
    rateLimit: {
      maxAttempts: 10,
      windowMs: 10 * 60 * 1000,
      // store: myRedisRateLimiter,
    },
  },
})

Pages Router helpers

toPages() — catch-all API route handler

Returns a (req, res) handler compatible with Vinext / Next.js Pages Router.

// pages/api/auth/[...vinextauth].ts
import { toPages } from "@/auth"
export default toPages()

pagesAuth(req) — server-side session

Reads the session from request cookies inside getServerSideProps. Works without next/headers.

export const getServerSideProps: GetServerSideProps = async (ctx) => {
  const session = await pagesAuth(ctx.req)
  if (!session) return { redirect: { destination: "/api/auth/signin", permanent: false } }
  return { props: { session } }
}

Custom types — no module augmentation

// auth.ts
export const { GET, POST, auth, toPages, pagesAuth } = VinextAuth<
  { role: "admin" | "user" },  // session.user extras
  { role: string }              // JWT token extras
>({
  providers: [...],
  callbacks: {
    jwt({ token, user }) {
      if (user) token.role = (user as any).role
      return token
    },
    session({ session, token }) {
      session.user.role = token.role  // fully typed ✓
      return session
    },
  },
})

// server component or getServerSideProps
const session = await auth<{ role: "admin" | "user" }>()
session?.user.role // "admin" | "user" ✓

React hooks

"use client"
import { useSession, signIn, signOut } from "vinextauth/react"

export function UserMenu() {
  const { data: session, status } = useSession()

  if (status === "loading") return <Spinner />
  if (!session) return <button onClick={() => signIn("google")}>Sign in</button>

  return (
    <>
      <span>{session.user.name}</span>
      <button onClick={() => signOut()}>Sign out</button>
    </>
  )
}

Server helpers

auth() — App Router / Server Components

import { auth } from "@/auth"
const session = await auth()

getServerSession() — explicit config

import { getServerSession } from "vinextauth/server"
import { authOptions } from "@/auth"

const session = await getServerSession(authOptions)

updateSession() — update session data server-side

import { updateSession } from "vinextauth/server"
await updateSession(authOptions, { user: { role: "admin" } })

invalidateSession() — revoke server-side

import { invalidateSession } from "vinextauth/server"
await invalidateSession(authOptions)

Middleware

// middleware.ts
import { withAuth } from "vinextauth/middleware"

export default withAuth({
  pages: { signIn: "/login" },
  callbacks: {
    authorized({ token }) {
      return token?.role === "admin"
    },
  },
})

export const config = {
  matcher: ["/dashboard/:path*", "/admin/:path*"],
}

Database sessions (Cloudflare KV)

import { CloudflareKVAdapter } from "vinextauth/adapters/cloudflare-kv"

export const { GET, POST, auth } = VinextAuth({
  providers: [...],
  adapter: CloudflareKVAdapter(env.SESSION_KV),
  session: { strategy: "database" },
})

When strategy: "database":

  • Sign-in stores an opaque session token in the cookie (not a JWT)
  • Each request looks up the session in the adapter
  • Sign-out deletes the session, enabling true server-side revocation

Custom adapter

import type { AdapterInterface } from "vinextauth"

const myAdapter: AdapterInterface = {
  async getSession(sessionToken) { ... },
  async createSession(session) { ... },
  async updateSession(session) { ... },
  async deleteSession(sessionToken) { ... },
}

Account linking

VinextAuth({
  accountLinking: {
    enabled: true,
    requireVerification: true, // default — safe mode
  },
})

Custom rate limiter (Redis, KV, etc.)

import type { RateLimiter } from "vinextauth"

const myLimiter: RateLimiter = {
  async check(key) {
    // return { allowed: true } or { allowed: false, retryAfter: 60 }
  },
  async reset(key) { ... },
}

VinextAuth({
  credentials: { rateLimit: { store: myLimiter } },
})

Environment variables

| Variable | Required | Description | |---|---|---| | VINEXTAUTH_SECRET | Yes | Signing secret for JWTs. Generate: openssl rand -base64 32 | | VINEXTAUTH_URL | No | Base URL of the app. Auto-detected on Vercel. | | VERCEL_URL | No | Auto-detected on Vercel deployments |


Differences from NextAuth v4

| | NextAuth v4 | VinextAuth | |---|---|---| | Target runtime | Node.js | Vinext / Cloudflare Workers (edge) | | Edge runtime | Partial | Full (Web Crypto only) | | Custom types | Module augmentation | Generics VinextAuth<{role: string}>() | | auth() helper | No | Yes — pre-bound on handlers | | Pages Router handler | Manual | toPages() — one line | | Pages Router session | getServerSideProps + getSession | pagesAuth(req) — reads cookies directly | | Server-side session update | No | updateSession() | | Dynamic base URL | No | baseUrl: (req) => string | | Account linking | allowDangerousEmailAccountLinking | Safe explicit API | | Credentials rate limiting | Manual | Built-in | | Node.js dependencies | Yes | None |


Repository structure

vinextauth/                     ← monorepo root (private)
├── packages/
│   └── vinext-auth/            ← published npm package (vinextauth)
│       ├── src/
│       │   ├── handlers/       ← HTTP request handlers + Pages Router adapter
│       │   ├── core/           ← session, JWT, CSRF, rate limiting
│       │   ├── providers/      ← Google, GitHub, Credentials
│       │   ├── react/          ← SessionProvider, useSession, signIn, signOut
│       │   ├── server/         ← getServerSession, updateSession
│       │   ├── middleware/     ← withAuth edge middleware
│       │   └── adapters/       ← Cloudflare KV adapter
│       └── package.json
├── apps/
│   ├── dev/vinext/             ← dev sandbox (port 3001, all providers)
│   └── examples/
│       └── vinext-basic/       ← basic example (port 3002, Google + GitHub)
└── package.json                ← workspace root

Running locally

# install all workspace dependencies
npm install

# build the library
npm run build

# run the dev sandbox
cp apps/dev/vinext/.env.example apps/dev/vinext/.env.local
# fill in VINEXTAUTH_SECRET in .env.local
npm run dev:vinext        # http://localhost:3001

# run the basic example
cp apps/examples/vinext-basic/.env.example apps/examples/vinext-basic/.env.local
npm run dev:example-vinext  # http://localhost:3002

# run tests
npm run test

# watch mode (library + dev app simultaneously)
# terminal 1:
npm run dev
# terminal 2:
npm run dev:vinext

License

MIT