For developers

Your users are about to show up with agents. Be ready in ~10 lines.

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.

Thread 1 · sign-in

One call tells you who signed in — and who's answerable.

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.

 signin.jsserver
// 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.
Drop-in adapters

Or skip the call — one line for your stack.

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.

next.js · live

NextAuth

A Credentials provider — BrowserID(config) — plus a client helper. Drops into your existing setup.

@browserid-ng/nextauth →
node · live

Express

A Passport Strategy and middleware — attaches the verified identity to req, 401s on failure.

@browserid-ng/express →
edge · live

Hono

Middleware for edge and workers runtimes — verifies at the boundary, sets the identity on the context.

@browserid-ng/hono →
node · live

Fastify

A preHandler that attaches request.browserid or short-circuits with a 401.

@browserid-ng/fastify →
Thread 2 · your tools

Restrict MCP tool access to specific users — without API keys.

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.

 server.mjs@browserid-ng/mcp-auth

      

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.

  • Scope-checked per tool, revocation re-checked per call.
  • Watch a revoke kill a live agent on the reference servers.

Coming soon — a warrant-gated GitHub server: issues and PRs on your behalf, attributed and scoped, no PAT in sight.

For your users

No accounts to create. They claim the email they already have.

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:

  • Domains with their own IdP sign users in directly — one step, under their own name.
  • Gmail and Workspace addresses prove themselves with a Google sign-in — nothing to type.
  • Everyone else falls back to a one-time code by email — a last resort, but it always works.