Passwordless sign-in with the email your users already have — and when their agents come knocking, the same check tells you exactly who's acting, for whom, with what permission.
POST the assertion to /verify, pinned to your audience. A human signs in: grantee === email, done. An agent signed in? You get both names and the exact scopes its human signed for your site — rate-limit per agent, audit per human, reject anything unsigned. No registration, no client IDs, no secrets to store. One honest note: the hosted verifier sees which of your users sign in — run your own if you'd rather it didn't.
// One call — a verified identity for whoever signs in const who = await verify(assertion, audience) who.email // "alice@gmail.com" — attributed to: the human who.grantee // "alice+researcher@gmail.com" — the agent who.scopes // ["post"] — what Alice signed for your site who.issuer // "gmail.com" — checked against its own DNS // humans sign in the same way — then grantee === email.
Each adapter wraps the same audience-pinned, fail-closed verification. Every result names who it's attributed to and who acted — your policy, your call.
A Credentials provider — BrowserID(config) — plus a client helper. Drops into your existing setup.
A Passport Strategy and middleware — attaches the verified identity to req, 401s on failure.
Middleware for edge and workers runtimes — verifies at the boundary, sets the identity on the context.
@browserid-ng/hono →A preHandler that attaches request.browserid or short-circuits with a 401.
A personal access token in an agent's config is unscoped, unattributed, and revocable only by rotating the secret everywhere. A warrant is the opposite: this site, these actions, this agent, that human — and revoking it at browserid.me/account kills the agent's very next call, fail-closed, touching nothing else.
Already have a stdio MCP server? You don't need to write any of this — npx @browserid-ng/gate puts it behind the same warrant gate, with a web console for per-person, per-tool access. About gate → The library below is the same machinery when you want it in-process.
Every call arrives with a verified grantor, grantee, and scopes — or is refused. It rides MCP's own OAuth 2.1, so the host (Claude, Cursor, Claude Desktop, …) speaks it unmodified and never learns BrowserID exists.
Coming soon — a warrant-gated GitHub server: issues and PRs on your behalf, attributed and scoped, no PAT in sight.
Any email address works — it's a BrowserID identity on every app that accepts browserid-ng, and the same identity their agents delegate from. How sign-in happens depends on the domain: