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.