NULL.
Services
Read the integration guide →
🔑

Sign in with NULL

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.

no identity provider post-quantum per-site pseudonymous kit ready

The login that doesn’t phone home

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 IdP

Nobody in the middle

No OAuth phone-home, no token endpoint, no account at NULL for your site. Your server verifies the assertion locally.

psid

Per-site pseudonym

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.

Post-quantum

ML-DSA-65 by default

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.

How it works

Add the button

Link it to /null/start. The user’s messenger opens; they choose private (a per-site pseudonym) or named, and sign.

The assertion lands in the fragment

It reaches your callback page only — never a server log, proxy log, or Referer — and the page POSTs it same-origin.

Your server verifies it

nullLogin(app, { rp, onLogin }) consumes the challenge once, checks both signatures locally, and hands you a psid. No build step.

For developers: 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.
🔑

A login worth trusting

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 →