Nobody in the middle
No OAuth phone-home, no token endpoint, no account at NULL for your site. Your server verifies the assertion locally.
Login that can’t phone home. In the default private mode nobody — not Google, not Apple, not NULL — sees your users sign in; a named login fetches one public record, which your server can route through the oblivious lane.
Add login where the user proves a NULL identity directly to YOUR server, and no third party is in the loop to log it. Five lines of Express, verified locally, post-quantum by default.
Sign-in-with-Google and every OAuth button tell the identity provider each time you log in somewhere — a perfect cross-site tracking record. “Sign in with NULL” is structurally incapable of that: the user’s NULL messenger signs a short assertion for your domain and your one-time challenge, hands it to your page in the URL fragment, and your server verifies the two signatures (secp256k1 + ML-DSA-65) itself with @noble. There is no NULL endpoint in the loop to log a thing.
No OAuth phone-home, no token endpoint, no account at NULL for your site. Your server verifies the assertion locally.
Each login yields a 40-hex id stable for your site and meaningless everywhere else — two sites can’t join it, and neither can NULL.
Named logins require the FIPS-204 bind; the whole assertion is bound to your domain and a single-use challenge, so it can’t be replayed.
Link it to /null/start. The user’s messenger opens; they choose private (a per-site pseudonym) or named, and sign.
It reaches your callback page only — never a server log, proxy log, or Referer — and the page POSTs it same-origin.
nullLogin(app, { rp, onLogin }) consumes the challenge once, checks both signatures locally, and hands you a psid. No build step.
npm i @null/login-rp (Node ≥ 20, ESM). Express: nullLogin(app, { rp, onLogin }); any backend: nullLoginHandlers(). Two hostile-audit rounds, login-CSRF + session-fixation closed, 205 tests green. OIDC-shaped apps use sub = psid.The user proves who they are to you and to no one else. That isn’t a feature bolted on — it’s the whole architecture.
Read the integration guide →