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

@somewhere-tech/auth

v0.4.1

Published

First-party auth for somewhere.tech apps — session client + React hooks + backend handler. Write zero auth code; the bug-prone token layer ships once, correct.

Readme

@somewhere-tech/auth

First-party authentication for somewhere.tech apps. You write zero auth code — the bug-prone session/token layer ships once, correct, instead of being hand-rolled (and re-broken) in every app.

It owns the part that's actually hard: the access token, the background refresh, the rotation persistence, surviving network blips, and keeping multiple tabs in sync (a background tab picks up a rotated session off the storage event, and a request that raced a rotation retries once with the fresh pair instead of logging the user out). Those are exactly the bugs every hand-written auth layer hits (logged out on a wifi hiccup or by a second tab, the rotating-refresh-token desync, the half-written token pair).

Sessions are httpOnly cookies by default (0.2.0)

In a browser, the session now lives in httpOnly cookies set by your backend — there are no tokens in localStorage (or anywhere page JS can reach), so an XSS can't exfiltrate the session. The client sends credentials: 'include', the platform refreshes the cookie pair server-side, and the only thing cached locally is the (non-secret) user object so a page load paints the signed-in UI instantly while /me re-validates in the background. A network blip is never a logout — only an explicit auth rejection signs the user out.

Nothing to configure; mode is negotiated per sign-in:

  • Browser + current backend → cookie session (the default).
  • Browser + older backend that doesn't set cookies → automatic fallback to header/token mode; everything keeps working.
  • Existing signed-in users upgrading from 0.1.x keep their session (header transport is adopted as-is) and migrate to cookies on their next sign-in.
  • CLI / native / Node (no browser cookies) → header/token mode, same as 0.1.x. Force a mode with createSomewhereAuth({ mode: 'header' }) (or 'cookie').

In cookie mode getSession() is always null — the tokens are deliberately unreadable. Use useUser() / getCachedUser() / getUser() for signed-in state.

Cookie-authed requests are origin-checked server-side: a cross-origin page (including another project on the platform's shared domain) can't ride the cookies into your API. Browser calls to your backend must be same-origin — which they are, for every normal app shape.

Frontend (React)

import { SomewhereAuthProvider, SignedIn, SignedOut, useUser, useAuth } from '@somewhere-tech/auth/react';

function App() {
  return (
    <SomewhereAuthProvider>
      <SignedIn><Dashboard /></SignedIn>
      <SignedOut><SignIn /></SignedOut>
    </SomewhereAuthProvider>
  );
}

function Dashboard() {
  const user = useUser();          // null | User — re-renders on login/logout/rotation
  const auth = useAuth();          // actions + auth.fetch
  // auth.fetch is a drop-in fetch that carries the session and persists rotation.
  // You never touch a token.
  const load = () => auth.fetch('/api/notes').then(r => r.json());
  return <p>Hi {user?.email}</p>;
}

function SignIn() {
  const auth = useAuth();
  return <button onClick={async () => {
    window.location.href = await auth.googleSignInUrl();   // platform-owned Google — no Google project
  }}>Continue with Google</button>;
}

Google callback route:

import { AuthCallback } from '@somewhere-tech/auth/react';
// at your redirect_uri route:
export default () => <AuthCallback />;   // reads ?code=, completes sign-in, done

Backend (one function)

login/signup are developer-key-gated on the platform (so they can't be called from the browser). Mount the handler once and it wires them onto sw.auth:

// functions/api/auth/[...path].ts  — handles your /auth/* routes
import { somewhereAuth } from '@somewhere-tech/auth/server';
export default (req, sw) => somewhereAuth(req, sw);

That's the whole integration. The frontend client talks to these routes; the platform's sw.auth does JWT validation + the header-based auto-refresh; the client persists the rotated pair atomically.

Billing / entitlements (sw.billing)

Define plans + the features each grants in code (no dashboard editor), then gate on a stable feature slug — not a raw plan string. The user's feature list rides the session, so has() is a synchronous local check (no network):

// once, from a backend function — declarative, idempotent
await sw.billing.definePlans([
  { slug: 'free', name: 'Free', features: [] },
  { slug: 'pro',  name: 'Pro', price_cents: 2000, interval: 'month',
    features: ['export', 'api_access'] },
]);
import { useEntitlements, Gate } from '@somewhere-tech/auth/react';

function ExportButton() {
  const { has } = useEntitlements();
  return has('export') ? <Export/> : <Upsell/>;
}

// or declaratively — the entitlement mirror of <Protect>
<Gate feature="export" fallback={<Upsell/>}><Export/></Gate>;

Server-side, gate with the same slug: if (await sw.billing.has(user.id, 'export')). The plan a user is on is kept current automatically by the Stripe checkout webhook — you write zero webhook code.

Drop-in pricing + manage UI (both ride the session — no extra wiring):

import { PricingTable, BillingPortal } from '@somewhere-tech/auth/react';

<PricingTable/>     // catalog plans, current plan highlighted, Subscribe → checkout
<BillingPortal/>    // one-click manage / cancel via the Stripe portal

Logic-only/unstyled — theme with the sw-* class hooks. Or wire it yourself: auth.billing.subscribe(planSlug) and auth.billing.openBillingPortal() (both redirect to Stripe). Subscribe checks out for the signed-in user (resolved server-side, never a client id) and the price comes from the plan's stripe_price_id in your catalog. auth.billing.plans() returns the catalog.

Framework-agnostic core

No React? Use the client directly:

import { createSomewhereAuth } from '@somewhere-tech/auth';
const auth = createSomewhereAuth();            // { baseUrl, authPath, storage, storageKey } all optional
await auth.signIn({ email, password });
const res = await auth.fetch('/api/notes');    // session-aware fetch
auth.onChange(({ user }) => { /* re-render */ });

Why this exists

The platform already does the hard 80%: the OAuth client (no Google Cloud project, no provider credentials), JWT validation (sw.auth.fromRequest), and the refresh protocol. The missing 20% was the thin client layer — and that thin layer is precisely the bug-prone part. Shipping it once, correct, erases an entire class of auth bugs.

Status

Covered: email/password, Google (platform-owned), httpOnly cookie sessions (browser default) with dual-mode fallback, session-aware fetch with atomic rotation + blip resilience, optimistic cached user, React hooks/gates/callback. Magic-link for end users is a follow-on. 0.x — API may still move.