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

@useauthio/nextjs

v0.4.4

Published

Next.js adapter for Authio: middleware, RSC helpers, and Provider.

Readme

@useauthio/nextjs

Part of Authio Lobby — Authio's drop-in passwordless authentication. Learn more at https://authio.com/products/lobby.

Next.js 14/15/16 adapter for Authio. Ships:

  • createAuthioMiddleware() — drop-in Edge middleware with silent session refresh.
  • createAuthioSignInHandler() — drop-in app/api/auth/sign-in handler that mints the cookie-bound login-CSRF nonce (v0.3+).
  • createAuthioCallbackHandler() / createAuthioRefreshHandler() / createAuthioSignOutHandler() — drop-in app/api/auth/* route handlers.
  • auth() — RSC helper that returns the verified session ({ userId, orgId, role }) inside Server Components and Route Handlers.
  • authMiddleware() — the original v0.1 JWKS-verifying middleware. Still exported for backward compat.

Recent additions

  • Embed @useauthio/widgets in App Router pages. Mint the widget JWT in a Server Component (or Server Action) using the existing auth() accessToken; render the widget inside a "use client" component. Full snippet on /sdks/nextjs.
  • Kind-aware route gating. Widget JWTs (kind: "widget") are refused on every customer-session surface by the data plane, but if you ever embed your own JWKS verify in middleware (e.g. for a proxied admin tool), reject kind === "widget" with widget_token_not_allowed_here — the canonical refusal.
  • Roles + permissions on the session. auth() exposes session.claims.roles (string in single-role mode, array in multi-role mode) and session.claims.permissions (always an array). Both are reserved-claim names; the only override path is the Pattern 3 Actions hook (/actions/pattern-3-customer-roles).
  • MCP / DCR. Customers turning their product into an OAuth provider can now flip dcr_mode and cimd_enabled in the dashboard. The Next.js adapter is unchanged — the /.well-known/oauth-authorization-server advertisements are served by auth-core and the new /oauth2/register / /oauth2/cimd/resolve endpoints are reachable directly with any HTTP client. See /guides/mcp-integration.

Install

pnpm add @useauthio/nextjs @useauthio/react

Full session lifecycle in twenty lines

// src/middleware.ts
import { createAuthioMiddleware } from "@useauthio/nextjs";
export default createAuthioMiddleware();
export const config = { matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"] };
// app/api/auth/sign-in/route.ts  (new in v0.3 — closes the login-CSRF gap)
import { createAuthioSignInHandler } from "@useauthio/nextjs/server";
export const { GET, POST } = createAuthioSignInHandler();
export const dynamic = "force-dynamic";
// app/api/auth/callback/route.ts
import { createAuthioCallbackHandler } from "@useauthio/nextjs/server";
export const GET = createAuthioCallbackHandler();
// app/api/auth/refresh/route.ts
import { createAuthioRefreshHandler } from "@useauthio/nextjs/server";
export const GET = createAuthioRefreshHandler();
// app/api/auth/sign-out/route.ts
import { createAuthioSignOutHandler } from "@useauthio/nextjs/server";
export const { GET, POST } = createAuthioSignOutHandler();

Point your "Sign in" link at the new handler — <a href="/api/auth/sign-in">Sign in</a> — instead of the hosted-UI URL directly. The handler mints a CSRF nonce, sets the authio_callback_state cookie on your origin, and redirects to Authio with the same value as ?client_state_nonce=…. The callback handler then verifies cookie ⟷ URL match before persisting any session.

That's it. You now have sign-in, silent refresh, sign-out, and login-CSRF defense — and your users stay signed in for the full org-policy refresh window (default 30 days) even though the underlying access JWT rotates every 15 minutes.

Login-CSRF defense

The v0.3 release closes a login-CSRF gap: an attacker who legitimately obtains an Authio access token could otherwise craft /api/auth/callback?access_token=<their JWT> and trick a victim into silently signing in as the attacker on the victim's browser.

The defense is the OAuth-style cookie-bound state nonce. The sign-in handler mints a 32-byte random nonce, sets it as an HttpOnly cookie on your origin, and forwards the same value to Authio. Auth-core persists it on the magic-link / OAuth state row and echoes it back on the callback redirect. The callback handler refuses any callback where cookie and URL disagree.

Upgrading from 0.2.x

You're not required to change anything: v0.3 is backwards-compatible. Your existing createAuthioCallbackHandler continues to work, but until you add createAuthioSignInHandler and update your sign-in <a> to point at it, the handler runs in legacy mode and writes a console.warn per callback. Migration is opt-in but strongly recommended — until you adopt it, this vector is still open against your app.

In a Server Component

import { auth } from "@useauthio/nextjs/server";

export default async function Page() {
  const { userId, orgId } = await auth();
  return <div>User {userId} in org {orgId ?? "(none selected)"}</div>;
}

Environment ID and custom auth domains

AUTHIO_PROJECT_ID is your dashboard environment ID (proj_…). The env var name is legacy; the API field is still project_id. Set it in server env so createAuthioSignInHandler() can forward it to Lobby (via a signed ctx token when auth-core supports it, or legacy query params as fallback).

The sign-in handler's default hosted UI URL is https://auth.authio.com/. Customers on the platform Lobby or a branded auth host pass hostedUiUrl:

createAuthioSignInHandler({
  hostedUiUrl: process.env.AUTHIO_HOSTED_UI_URL ?? "https://lobby.authio.com/",
});

DNS for a vanity auth hostname (e.g. auth.acme.com) is dashboard-side: CNAME to cname.authiodns.com. The SDK never references that CNAME at runtime — only your hostedUiUrl / end-user sign-in URL changes. See Custom domains.

Configuring cookie names

Multiple BFFs under the same parent domain need distinct cookie names so they don't collide. Pass the same names to every helper:

const opts = {
  sessionCookieName: "myapp_session",
  refreshCookieName: "myapp_refresh",
  apiUrl: process.env.AUTHIO_API_URL,
};

export default createAuthioMiddleware(opts);
export const GET = createAuthioCallbackHandler(opts);

License

MIT