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

@rekey.dev/nextjs

v2.1.0

Published

Rekey helpers for Next.js — middleware, server-side auth(), cookie session.

Readme

@rekey.dev/nextjs

ReliPay is now Rekey. This package was previously published as the equivalent @relipay/* package, which is deprecated. Env vars renamed RELIPAY_*REKEY_* (as of 2.0.0 the old names are no longer read — set REKEY_*). relipay.dev (the old domain) will redirect to rekey.dev after the domain migration.

Rekey helpers for the Next.js App Router (14, 15 and 16): route-gating middleware, server-side auth() / signIn() / signUp() / signOut(), and an httpOnly cookie session — built on top of @rekey.dev/node.

For AI coding agents: start at AGENTS.md.

npm i @rekey.dev/nextjs
# or: pnpm add @rekey.dev/nextjs / yarn add @rekey.dev/nextjs

Setup

Two credentials, two homes. The secret key powers the server (auth(), API routes); the publishable key powers browser login/register — it's public by design and safe to ship in client JS.

# Server-only (never NEXT_PUBLIC_):
REKEY_URL=https://api.rekey.dev
REKEY_SECRET=rp_live_…              # Application secret key (Panel → Application → API Keys)

# Browser-safe (exposed to client bundle):
NEXT_PUBLIC_REKEY_URL=https://api.rekey.dev
NEXT_PUBLIC_REKEY_PUBLIC_KEY=rp_pub_…   # Application publishable key

https://api.rekey.dev is Rekey Cloud's API, the same origin for every Cloud workspace — requests are scoped by your API key, not by the URL. Self-hosting, use your own deployment's public origin instead (http://localhost:3030 locally). Both URL variables are the same origin; the NEXT_PUBLIC_ prefix only tells the bundler it may be inlined. There is no default for either — see docs/api-url.md for why, and for how to verify the origin before wiring keys.

Never ship the secret key to the browser. @rekey.dev/nextjs/server pulls Node-only deps and reads REKEY_SECRET; importing it from a Client Component or middleware will fail to bundle (that's the safety net working). Browser code uses the publishable key via @rekey.dev/nextjs/client.

Entrypoints

| Import | Runtime | Credential | Use case | | --- | --- | --- | --- | | @rekey.dev/nextjs/middleware | Edge | none (cookie presence) | Gate routes in middleware.ts (cheap, no network). | | @rekey.dev/nextjs/server | Node | secret key | auth(), signIn(), signUp(), createSession() + your @rekey.dev/node API calls. | | @rekey.dev/nextjs/client | Browser | publishable key | rekeyBrowser() — sign-in/up, magic-link, passkey, license verify, plans from a Client Component, no backend round-trip. | | @rekey.dev/nextjs/cookies | anywhere | none | ACCESS_COOKIE / REFRESH_COOKIE / *_OPTS / cookieSecureFrom(). Zero dependencies — import cookie names from here, not from the root barrel. |

The split keeps the Edge bundle small and the secret key out of the browser — the /client module only imports the publishable-key browser client.

Import the cookie constants from /cookies, not from the root. The root barrel re-exports ./server, which pulls next/server and reads REKEY_SECRET; importing it from a Client Component is a build error. The /cookies entry exists precisely so a client component never has to.

Which login path?

Both are valid; pick per app:

  • Server-action login (secret key, /server) — the browser never holds any Application key; tokens go straight into httpOnly cookies. Best default when you have a server. See Quickstart step 3.
  • Browser login (publishable key, /client) — sign users in directly from a Client Component, then hand the tokens to a route handler that calls createSession() to set the same httpOnly cookies. Best for client-component-driven flows. See Publishable login → secret-key API routes.

Quickstart

1. Gate routes — middleware.ts:

import { rekeyMiddleware } from '@rekey.dev/nextjs/middleware';

export default rekeyMiddleware({
  signInUrl: '/login',
  publicRoutes: ['/', '/login', '/signup', '/forgot-password', '/api/auth'],
});

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\..*).*)'],
};

2. Read the session — any server component:

import { auth } from '@rekey.dev/nextjs/server';

export default async function Dashboard() {
  const session = await auth(); // { user, accessToken } | null
  if (!session) return null;    // middleware already redirected; defensive
  return <p>Hi {session.user.email}</p>;
}

3. Sign in — a server action:

'use server';
import { redirect } from 'next/navigation';
import { signIn } from '@rekey.dev/nextjs/server';

export async function signInAction(fd: FormData) {
  const outcome = await signIn({
    email: String(fd.get('email')),
    password: String(fd.get('password')),
  });
  if (outcome.kind === 'mfa_required') redirect('/login?error=MFA_REQUIRED');
  redirect('/dashboard');
}

For everything @rekey.dev/nextjs doesn't cover (billing, credits, usage, orgs, password reset, sessions), construct a @rekey.dev/node client in a server-only module and call it from server actions / route handlers — passing session.accessToken for per-user reads.

Publishable login → secret-key API routes

The browser logs the user in with the publishable key; the resulting tokens are handed to a route handler that sets the httpOnly session cookies; everything server-side (auth(), your API routes) keeps using the secret key. No 'use server' action needed for login.

1. Client Component — register / sign in with the publishable key:

'use client';
import { rekeyBrowser } from '@rekey.dev/nextjs/client';
import { useRouter } from 'next/navigation';

export function LoginForm() {
  const router = useRouter();
  async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    const fd = new FormData(e.currentTarget);
    // signUp for register; signIn for login — both publishable-key authorized.
    const out = await rekeyBrowser().signIn({
      email: String(fd.get('email')),
      password: String(fd.get('password')),
    });
    if (out.mfaRequired) {
      router.push(`/login/mfa?token=${out.mfaChallengeToken}`);
      return;
    }
    // Hand tokens to the server to set httpOnly cookies.
    await fetch('/api/auth/session', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ accessToken: out.accessToken, refreshToken: out.refreshToken }),
    });
    router.push('/dashboard');
  }
  return (
    <form onSubmit={onSubmit}>
      <input name="email" type="email" required />
      <input name="password" type="password" required />
      <button type="submit">Sign in</button>
    </form>
  );
}

2. Route handler — finalize the session into httpOnly cookies:

// app/api/auth/session/route.ts
import { createSession } from '@rekey.dev/nextjs/server';

export async function POST(req: Request) {
  const { accessToken, refreshToken } = await req.json();
  // Add your CSRF/origin check here — this sets cookies verbatim.
  await createSession({ accessToken, refreshToken });
  return Response.json({ ok: true });
}

3. Secret-key API route — per-user data, server-side:

// app/api/credits/route.ts
import { auth } from '@rekey.dev/nextjs/server';
import { Rekey } from '@rekey.dev/node';

const rekey = new Rekey({
  apiUrl: process.env.REKEY_URL!,
  secretKey: process.env.REKEY_SECRET!,   // secret key — server-only
});

export async function GET() {
  const session = await auth();             // from the httpOnly cookies
  if (!session) return new Response('Unauthorized', { status: 401 });
  const balance = await rekey.credits.getBalance({ endUserId: session.user.id });
  return Response.json(balance);
}

After this, middleware.ts and auth() work identically to the server-action path — the only difference is where the login call ran (browser vs server). Plans + license verification can also run straight from the browser: rekeyBrowser().getPlans() (which resolves to {items, page}, like every list method), rekeyBrowser().verifyLicense({ key, machineFingerprint }).

Core API

@rekey.dev/nextjs/middleware

| Export | Description | | --- | --- | | rekeyMiddleware({ publicRoutes?, signInUrl? }) | Middleware that lets publicRoutes through and redirects unauthenticated requests to signInUrl?next=…. Gates on cookie presence; validity is checked deeper via auth(). | | MiddlewareConfig | Type for the config object. |

@rekey.dev/nextjs/server

| Export | Description | | --- | --- | | auth() | Resolve the session from cookies. Tries the access token, refreshes-and-rotates once on expiry, returns null only when both fail. | | signIn({ email, password }) | Returns { kind: 'session' } (cookies set) or { kind: 'mfa_required', mfaChallengeToken } (no cookies — collect a code and complete via mfaVerify). | | mfaVerify({ mfaChallengeToken, code }) | Complete an MFA-required sign-in; sets cookies on success. | | signUp({ email, password, metadata? }) | Create the user + start a session (always sets cookies). | | createSession({ accessToken, refreshToken }) | Set the httpOnly session cookies from tokens a browser login produced. Use in a route handler to finalize a @rekey.dev/nextjs/client sign-in. | | signOut(redirectTo?) | Revoke the refresh token + clear cookies; optionally redirect. | | Session / SignInOutcome | Session + sign-in result types. |

@rekey.dev/nextjs/client (browser — publishable key)

| Export | Description | | --- | --- | | rekeyBrowser({ apiUrl?, publishableKey? }) | Browser client configured from NEXT_PUBLIC_REKEY_URL + NEXT_PUBLIC_REKEY_PUBLIC_KEY (or overrides). Methods: signIn, signUp, mfaVerify, requestMagicLink, verifyMagicLink, startPasskeyAuthentication, verifyPasskeyAuthentication, getPlans, verifyLicense. | | RekeyBrowserClient | The underlying class, re-exported from @rekey.dev/react. |

@rekey.dev/nextjs (root)

| Export | Description | | --- | --- | | mcpConnectionInfo({ apiUrl, appSlug }) | Build the MCP URL + claude mcp add command to render a "Connect to Claude" button (pure string-building). | | ACCESS_COOKIE / REFRESH_COOKIE / ACCESS_COOKIE_OPTS / REFRESH_COOKIE_OPTS | Re-exported here for convenience, but import them from @rekey.dev/nextjs/cookies — that entry is dependency-free and safe from a Client Component, while this barrel is a server entry. |

@rekey.dev/nextjs/cookies (dependency-free)

| Export | Description | | --- | --- | | ACCESS_COOKIE / REFRESH_COOKIE | "rekey_access" / "rekey_refresh". | | ACCESS_COOKIE_OPTS / REFRESH_COOKIE_OPTS | Cookie options, for actions that write the token pair directly (e.g. after organizations.switch). | | cookieSecureFrom(headers) | The per-request Secure decision — see Cookie model. |

Cookie model

Two httpOnly cookies, set by signIn / signUp:

  • rekey_access — 15 min (matches access-token lifetime).
  • rekey_refresh — 30 days.

Both are sameSite=lax, httpOnly, and carry Secure unless the request arrived as plain HTTP on a loopback host — so local http://localhost dev still works, and a TLS deployment gets Secure whatever NODE_ENV happens to be. The decision is made per request, in this order:

  1. REKEY_COOKIE_SECURE=true / =false, if set — wins outright.
  2. X-Forwarded-Proto (first hop) — httpsSecure.
  3. Otherwise the Host: localhost / 127.0.0.1 / [::1] / *.localhost ⇒ not Secure; anything else ⇒ Secure.

Rule 3 is fail-secure. If you serve a public hostname over plain HTTP, the browser will refuse the cookie and sign-in will appear to do nothing — set REKEY_COOKIE_SECURE=false if that is genuinely what you want. This replaced a process.env.NODE_ENV === 'production' check, which answered a request-time question at build time and silently emitted cookies without Secure on any deployment where Next did not inline NODE_ENV as exactly "production".

cookieSecureFrom(headers) is exported from @rekey.dev/nextjs/cookies if you need the same decision for a cookie of your own.

Gotchas

  • auth() can only rotate from a server action or route handler. Next.js forbids server components from writing cookies, so a stale-access read in a server component returns null instead of refreshing. Trigger auth() from the action driving the page, or accept the redirect.
  • Entitlements are resolved server-side. Gate features by calling rekey.billing.getEntitlements(session.accessToken, …) from a server module — never from client state.
  • billingSubject: 'org' requires an organizationId. On a per-team-billing Application, createCheckout without one throws BILLING_ORGANIZATION_REQUIRED. Read the live config via rekey.applications.me() and gate the checkout UI on it.
  • Switching active org returns a fresh token pair — write both back into ACCESS_COOKIE / REFRESH_COOKIE with the exported opts, or later reads use the stale org view.
  • Checkout activation is async — and not your webhook. Subscriptions flip to ACTIVE when the provider (Stripe/PayPal) calls Rekey's webhook endpoint, which the operator configures in the panel; your Next.js app never receives or verifies that traffic. Re-fetch getSubscription / getEntitlements on your successUrl page. (The SDK's verifyWebhookSignature is only for webhooks Rekey sends to your app — user-lifecycle events; see docs/billing.md.)
  • Don't import @rekey.dev/nextjs/server from a Client Component or middleware — it pulls Node-only deps.

Links

License

MIT