Pattern: one shared password (HTTP Basic Auth)

When to reach for this. You have a single credential to share — an owner-only /admin, a staging lock, a one-person tool, a client preview behind a password you hand out. One password, no "who is logged in", no sign-up.

The stance. Don't build accounts for this. Hono's built-in basicAuth is one line, zero dependencies, zero storage — the browser's native login box gating whatever routes you mount it on. The moment you need per-user identity (logout, roles, "signed in as…"), this is the wrong pattern — jump to unfolder.auth-sessions or unfolder.auth-accounts.

Wire the credential through config

Username is non-secret (a variable); password is sensitive (a secret the owner sets in the browser, never in code):

// in run_code, once. Then the owner sets ADMIN_PASSWORD's value in settings, then deploy.
config.setVariable({ name: "ADMIN_USER", value: "admin" });
config.declareSecret({ name: "ADMIN_PASSWORD" });

Declare these before your first publish; after that, a changed value (the owner pasting a new password in settings) re-primes the live site immediately — no redeploy needed. See secrets.

The worker

import { Hono } from "hono";
import { basicAuth } from "hono/basic-auth";
import { DurableObject } from "cloudflare:workers";

export class App extends DurableObject {
  app = new Hono();
  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    // Gate just /admin/*; the public site stays open. Mount on "*" to lock everything.
    this.app.use("/admin/*", basicAuth({ username: env.ADMIN_USER, password: env.ADMIN_PASSWORD }));
    this.app.get("/admin/*", (c) => c.text("secret dashboard"));
    this.app.get("/", (c) => c.text("public homepage"));
  }
  async fetch(request: Request) { return this.app.fetch(request); }
}

basicAuth ships with Hono — no extra dependency to declare. Credentials travel base64 on every request, so it leans on HTTPS (every space already has it).

The honest limits

  • It's a shared secret, not identity: no logout, no per-user state, no audit of who entered.
  • Rotating the password is an owner action on the settings page (the new value re-primes the live site immediately).
  • Don't put a real account system behind it.

That's the trade you're making for one line and no storage. When you outgrow it, unfolder.auth-sessions (own a tiny users/sessions table) is the next step up.