Skip to content

Authentication

MohamedAshrafElsaed edited this page Jul 9, 2026 · 1 revision

Authentication

Each project has a secret. Nothing sensitive ever ships in the browser bundle.

flowchart TD
  R["request"] --> P{"project found?"}
  P -- no --> E404["404"]
  P -- yes --> ADM{"X-Loupe-Admin == secret?"}
  ADM -- yes --> OKA["authorized (admin) — dashboard / MCP"]
  ADM -- no --> USR{"HMAC-SHA256(user, secret)<br/>== X-Loupe-Hmac?"}
  USR -- yes --> OKU["authorized (user) — SDK"]
  USR -- no --> E401["401"]
Loading

SDK (user identity)

The host app's server computes HMAC-SHA256(user.id, PROJECT_SECRET) and injects it into the page as userHmac. The SDK sends it as X-Loupe-User + X-Loupe-Hmac. Writes are gated, and a user can only post comments as themselves. The browser never sees the secret, so identity can't be spoofed.

// on the host app's server (never in the browser)
const userHmac = crypto.createHmac("sha256", PROJECT_SECRET).update(user.id).digest("hex");
// → inject into the page and pass to Loupe.init({ userHmac })

Dashboard & MCP (admin)

The back-office authenticates with X-Loupe-Admin = the project secret. The dashboard reads it from ?key= (persisted to localStorage); the MCP server reads LOUPE_ADMIN_KEY. In production these sit behind a real authenticated session.

Screenshots

Blobs are served by unguessable id today; production should use signed URLs.

Prototype note

Project secrets are seeded by hand (npm run seed) in the prototype. Production adds a project/team management UI.

Clone this wiki locally