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

@profullstack/bridges

v0.2.0

Published

Let a host site's signed-in users use an app automatically: an account bridge over OAuth 2.1 authorization code + PKCE S256, with silent sign-in. Host handlers and app client are fetch-style (Request in, Response out), stateless (sealed codes), dependency

Readme

@profullstack/bridges

Let the people signed in to your site use an app on it without a second account.

A host (your site, which owns the accounts) and an app (a forum, docs, a tool) share one client secret. When someone opens the app, it asks the host who they are. If they are signed in there, they come back signed in here, with no prompt and no password. That is the silent sign-in, prompt=none. The app links its account to the host's id for that person, so renaming or changing an email on either side never moves anyone into someone else's account.

Under the hood it is the OAuth 2.1 authorization-code flow with PKCE S256, cut down to the one thing this needs:

  • Fetch-style. Each handler takes a Request and returns a Response, so it mounts on Next.js route handlers, Hono, Bun.serve, Workers or Express (via a fetch adapter).
  • Stateless. The code is sealed (AES-256-GCM) with the client secret and carries everything the token endpoint needs. The app's pending sign-in rides in one sealed cookie. Neither side needs a table. The host keeps only a replay set, in memory by default.
  • Zero dependencies. WebCrypto only. Runs on Node 20+, Bun, Deno, Cloudflare Workers and the edge.

First used by c0upons.com, whose members are signed in to c0upons.com/bbs (a tsbb board) automatically.

Install

npm install @profullstack/bridges
npx @profullstack/bridges client tsbb https://example.com/bbs/auth/bridge/callback

The second command prints the host's client entry and the app's settings, with a fresh secret. Put the secret in your vault, not in the repo.

Host (the site that owns the accounts)

// app/api/v1/bridge/[action]/route.js (Next.js)
import { createBridgeHost } from '@profullstack/bridges';

const bridge = createBridgeHost({
  clients: {
    tsbb: {
      secret: process.env.BRIDGE_TSBB_SECRET,
      redirectUris: ['https://example.com/bbs/auth/bridge/callback'],
    },
  },
  // Who is signed in here? Return null if nobody.
  async getUser(request) {
    const me = await sessionFrom(request);
    return me && { sub: me.id, name: me.name, email: me.email, emailVerified: true };
  },
  // A signed-out person on an interactive sign-in goes here, then back to returnTo.
  loginUrl: (returnTo) => `/login?next=${encodeURIComponent(returnTo)}`,
});

export const GET = (request) => bridge.authorize(request);   // /api/v1/bridge/authorize
export const POST = (request) => bridge.token(request);      // /api/v1/bridge/token

sub is required and must never change for a person. name, username, email and picture are hints for creating the app's account. An email counts as verified only when you pass emailVerified: true.

App (the product that wants the host's users)

import { createBridgeClient } from '@profullstack/bridges';

const bridge = createBridgeClient({
  authorizeUrl: 'https://example.com/api/v1/bridge/authorize',
  tokenUrl: 'https://example.com/api/v1/bridge/token',
  clientId: 'tsbb',
  clientSecret: process.env.BRIDGE_SECRET,
  redirectUri: 'https://example.com/bbs/auth/bridge/callback',
});

// Start: silently ('none') or with the host's login page ('login').
const { url, cookie, maxAge } = await bridge.begin({ returnTo: '/t/42', prompt: 'none' });
// set cookie `bridge_state=<cookie>` (httpOnly, Secure, SameSite=Lax, Max-Age=maxAge), redirect to url

// Callback:
const result = await bridge.complete(request.url, cookies.bridge_state);
if (result.ok) {
  // result.user = { sub, name?, email?, email_verified?, picture?, username? }
  // find the account linked to result.user.sub, or create and link one; sign them in
} else if (result.error === 'login_required') {
  // signed out on the host: carry on as a guest, and don't retry silently for a while
}
// redirect to result.returnTo

Sign in with CoinPay (or any OAuth 2.1 provider)

createOAuthClient has the same begin / complete shape for a real OAuth 2.1 / OpenID Connect provider: authorization code with PKCE S256, then one userinfo call. The access token is then dropped, because this signs people in and does not act for them. createCoinPayClient is the CoinPay preset. It asks for openid did profile email, and sub is the person's CoinPay DID when they have one. Sites built on CoinPay (c0upons.com, for one) use the same DID, so an app can link one person whether they arrive through the site's bridge or straight from CoinPay.

import { createCoinPayClient } from '@profullstack/bridges';

const coinpay = createCoinPayClient({
  clientId: process.env.COINPAY_CLIENT_ID,          // register at coinpayportal.com
  clientSecret: process.env.COINPAY_CLIENT_SECRET,
  redirectUri: 'https://example.com/auth/coinpay/callback',
  stateSecret: process.env.SESSION_SECRET,          // seals the state cookie, 32+ chars
});
const { url, cookie } = await coinpay.begin({ returnTo: '/' });
// ... later, on the callback:
const result = await coinpay.complete(request.url, cookies.coinpay_state); // { ok, user, returnTo }

CoinPay does not verify emails, so email_verified is false. Never use the email to find an existing account.

From a CLI. A CLI signs in through the app in a browser. It opens the app's device-approval page with the provider named, so the person goes straight to CoinPay instead of the app's sign-in page. tsbb does this as tsbb login <board> --with coinpay.

Security notes

  • Redirect URIs match exactly. An unregistered redirect_uri gets a plain 400 and never a redirect, so the endpoint can't become an open redirect that leaks codes.
  • Codes live 60 seconds and work once. Run several host processes? Pass a shared replayStore (anything with claim(jti, expiresAt) => Promise<boolean>, e.g. Redis SET NX PX).
  • The client secret is compared in constant time. It's sent in the token request body or with HTTP Basic, server to server only.
  • The app must check returnTo is a local path before redirecting to it.

CLI

bridges keygen                                  a new client secret
bridges client <client_id> <callback_url>       host entry + app settings, fresh secret
bridges check <authorize_url> <client_id> <callback_url>
                                                silent sign-in probe; "login_required" means it works

License

MIT, Profullstack, Inc.