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

@pithy-sh/auth

v0.7.0

Published

Passwordless auth for Cloudflare. Magic link, email OTP, Google, Apple. Mobile and web, both first-class. Built on Better Auth. No email and password, ever.

Readme

@pithy-sh/auth

Passwordless auth for Cloudflare. Magic link, email OTP, Google, Apple. Mobile and web, both first-class. Built on Better Auth. No email and password, ever.

This capability fills core's identity seams. It mints sessions, issues short-lived JWT access tokens, registers devices, and validates every request — so other capabilities just call requireAuth().

pithy add auth

Documentation: pithy.sh/docs/capabilities/auth. Overview, adding it, using it, and the reference: the token model, sessions and devices, JWKS.

Everything else is on the site. pithy.sh/docs is canonical — new prose goes there, not here.

The client surface

Better Auth builds a client from its own plugin list. The server's type never crosses into a browser bundle, so composing organization() on the server is half of it — add organizationClient() beside it and authClient.organization is fully typed, with no cast:

import { createAuthClient } from "better-auth/client";
import { emailOTPClient, magicLinkClient, organizationClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  baseURL: "https://api.example.com",
  basePath: "/auth",
  plugins: [magicLinkClient(), emailOTPClient(), organizationClient()],
});

The kit's own sign-in plugins have client halves too, and they go in the same list — nothing about the client is inherited from the server.

The one thing that does need the server's type is inferAdditionalFields, which teaches the client about extra user and session fields. AuthInstance is parameterized in the plugin tuple for exactly that:

import type { AuthInstance } from "@pithy-sh/auth/src/instance/auth";
import type { organization } from "better-auth/plugins/organization";

type AppAuth = AuthInstance<[ReturnType<typeof organization>]>;
// …then `inferAdditionalFields<AppAuth>()` in the plugins list above.

This section stays here. src/http/routes.ts and src/client/api.ts both explain a design decision by pointing a reader at it by name — the flat response shape is read rather than rewritten precisely because createAuthClient is a first-class surface, and that argument is only checkable against the client this documents.

Provider sign-in is identity-based, by design

Pressing a provider button while signed out is not a claim on an address. It is a claim on an identity. Four outcomes, and they are the whole of it:

| # | Situation | Outcome | |---|---|---| | 1 | The provider identity is already attached to an account | Signs in | | 2 | Not attached, and the provider's verified address matches an account | Links and signs in | | 3 | Not attached, address unverified at the provider, an account exists there | Refused | | 4 | Not attached, no account at that address | Refused |

Rows 3 and 4 answer identically, and that is a security property rather than a rough edge. They differ only in whether an account exists at an address the caller chose, and telling a stranger which one they hit is an account-enumeration oracle: one provider account, none on the target, unlimited queries. The decision is made in src/instance/providerSignInGate.ts, before Better Auth branches, so the two codes that used to answer it are never produced. The true reason goes to the audit trail, where an operator can read it and a browser cannot.

So: do not "improve" this by matching more addresses, and do not make the refusal more helpful. An address is not a credential — a provider hands over whatever primary happens to be set, chosen years ago for unrelated reasons — and every sentence that separates "you have no account here" from "you have one and this provider is not attached to it" is the oracle back. The remedy for a refused sign-in is the same either way: sign in with a magic link, then connect the provider from the account. Linking from inside the account is a different flow with both sides already proven, and it is deliberately unconstrained by the address.

License

MIT — adopter-side app value. The root LICENSE covers it.