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

@permok/auth-nextjs

v0.6.0

Published

Add Peekay Auth sign-in, sessions and the Backend API to a Next.js app.

Readme

@permok/auth-nextjs

Add Peekay Auth sign-in and sessions to a Next.js 16 app. Your app gets no user table and no password handling: users sign in on Peekay Auth and come back with verified tokens, scoped to your app only (no cross-app single sign-on).

apps/demo is a complete working example.

1. Register your app

From the auth repo:

pnpm idp:client:create --name "My App" \
  --redirect https://myapp.example.com/api/auth/callback \
  --logout https://myapp.example.com

It prints a client ID and a secret. The secret is shown once.

Add --third-party if the app isn't built by you. Its users will then see an "Unverified app" notice at sign-up.

2. Environment

PEEKAY_AUTH_ISSUER=https://auth.example.com
PEEKAY_AUTH_CLIENT_ID=app_...
PEEKAY_AUTH_CLIENT_SECRET=sk_...
PEEKAY_AUTH_APP_URL=https://myapp.example.com
# 32 random bytes: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
PEEKAY_AUTH_COOKIE_SECRET=...

3. Wire it up (five files)

proxy.ts protects routes and keeps tokens fresh:

import { peekayProxy } from "@permok/auth-nextjs/proxy";

export default peekayProxy({ publicRoutes: ["/", "/pricing", "/blog/*"] });

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico)$).*)"],
};

app/api/auth/[...peekay]/route.ts handles sign-in, the callback and sign-out:

import { handleAuth } from "@permok/auth-nextjs/server";

export const { GET, POST } = handleAuth();

app/layout.tsx wraps the app and adds the components:

import "@permok/auth-nextjs/styles.css";
import { SignedIn, SignedOut, SignInButton } from "@permok/auth-nextjs/client";
import { PeekayProvider, UserButton } from "@permok/auth-nextjs/components";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <PeekayProvider>
          <header>
            <SignedIn><UserButton accountHref="/account" /></SignedIn>
            <SignedOut><SignInButton /></SignedOut>
          </header>
          {children}
        </PeekayProvider>
      </body>
    </html>
  );
}

app/account/page.tsx renders account management inside your app (see "Keep users in your app" below) — this is the default setup, not an optional extra:

import { UserProfile } from "@permok/auth-nextjs/components";
export default function Page() { return <UserProfile />; }

next.config.ts (needed only while the SDK is used from this monorepo as TypeScript source):

export default { transpilePackages: ["@permok/auth-nextjs"] };

4. Use it

Server Components, Route Handlers and Server Actions:

import { getAuth, requireAuth, currentUser } from "@permok/auth-nextjs/server";

const { isSignedIn, userId, user } = await getAuth();
const auth = await requireAuth(); // redirects to sign-in if needed
const live = await currentUser(); // calls the IdP: also confirms the session wasn't revoked

Client Components:

"use client";
import { useAuth } from "@permok/auth-nextjs/client";

const { isSignedIn, user } = useAuth();

Reference

| Export | From | What it does | | --- | --- | --- | | peekayProxy({ publicRoutes }) | /proxy | Refreshes expiring tokens. Sends signed-out page visits to sign-in and returns 401 JSON to APIs. | | handleAuth() | /server | Handles GET sign-in, GET callback and POST sign-out. | | getAuth() | /server | The current user. Verified locally from the token, with no network call. | | requireAuth() | /server | Same as getAuth(), but redirects to sign-in when signed out. | | currentUser() | /server | Fresh profile from the IdP's userinfo endpoint. Returns null if the session ended. | | getAccessToken() | /server | The raw access token, for calling your own APIs. | | PeekayProvider, UserButton, UserProfile | /components | Server Components. UserProfile is an in-app account page (name, password). | | SignIn, SignUp, ForgotPassword | /components | Embedded forms, no redirect (verified apps only). SignIn has a built-in "Forgot password?" link. | | useAuth, SignedIn, SignedOut, SignInButton, SignOutButton | /client | Client Components and hooks. | | peekayBackend(), createBackendClient(), PeekayApiError | /backend | Server-only Backend API client (needs an API key, see below). |

Sign-in links: /api/auth/sign-in?return_to=/somewhere.

Keep users in your app

<UserButton accountHref="/account" /> plus a page that renders <UserProfile /> (shown above) is the default setup: users change their name and password without ever leaving your app. The forms call /api/auth/account/* on your own server, which relays to the auth server with the user's token (it never reaches browser code). This needs a verified app. Without accountHref, "Manage account" falls back to a slim hosted account page with a "Back to your app" link, for apps that haven't wired up an in-app route yet.

Backend API

Your server can list the people who use your app and sign them out. Create an API key in your app's settings under Backend API keys and set PEEKAY_AUTH_API_KEY=pak_…. A key only sees users who have signed in to its own app.

import { peekayBackend } from "@permok/auth-nextjs/backend";

const api = peekayBackend();
const { data: users } = await api.users.list({ limit: 50 });
await api.users.revokeSessions(users[0].id); // sessions:write

The key acts for the app, not for the signed-in user. Full reference: <issuer>/docs/backend-api.

How it works

  • Tokens. Sign-in uses the authorization code flow with PKCE. The access token (10 minutes), the refresh token and the ID token are kept in one cookie, encrypted with AES-256-GCM, httpOnly and SameSite=Lax. Browser JavaScript never sees a token.
  • No cross-app SSO, and no shared accounts. The IdP session created by signing in is scoped to your app's client ID, and so is the account itself: the same email can have a completely separate Peekay Auth account on a different app. Being signed into another app (or the IdP's own dashboard) never signs a user into yours, and user.id is only stable within your own app.
  • Refresh. The proxy refreshes an access token within 60 seconds of expiry. It passes the new cookie to the current request, so getAuth() in that same render sees fresh claims.
  • Refresh tokens rotate. Each refresh token works once. Reusing an old one more than 30 seconds after it was replaced revokes the whole session at the IdP, because it means the token was copied. Within those 30 seconds the reuse is treated as a race between servers. On Vercel and other serverless hosts, two instances can refresh at the same moment, and both get valid tokens. The SDK also shares one refresh call between concurrent requests in the same process.
  • Sign-out revokes the refresh token and ends the IdP session for this app.
  • CSRF. sign-out accepts only POSTs from your app's own origin. The OAuth state value and the RFC 9207 iss check protect the callback.

Changelog

  • 0.4.0: Removed organizations end-to-end (OrgSwitcher, getOrganizations(), orgId / orgRole / permissions / has(), the Backend API's organization client, switch-org) and closed cross-app SSO: a session is now scoped to the app that created it, so signing into one app or the IdP dashboard no longer silently signs a user into another app. <UserButton> no longer takes showOrganizations.

  • 0.3.0: <UserProfile />: an in-app account page (change name and password) for verified apps, backed by new /api/auth/account/* routes in handleAuth(). <UserButton> takes accountHref. The default "Manage account" link now opens a slim hosted account page for the app's users. New getAccount() server helper.

  • 0.2.0: Backend API client, @permok/auth-nextjs/backend: users (list, get, sign out of the app), authenticated with an app API key.

  • 0.1.1: Security: post-login return_to paths containing control characters (for example a tab, which browsers strip, turning /\t/evil.com into //evil.com) are now rejected. Upgrade recommended.

  • 0.1.0: First release.