Skip to content

User administration

Administration is the part of authentication most apps re-type worst: account lists that leak hashes, disable flows that leave stolen sessions alive, bootstrap scripts that bypass validation. authkit ships it once.

admin := authkit.NewAdmin(store)
mux.Handle("GET /api/users", auth.RequireSession(http.HandlerFunc(admin.List)))
mux.Handle("POST /api/users", auth.RequireSession(http.HandlerFunc(admin.Create)))
mux.Handle("PATCH /api/users/{id}", auth.RequireSession(http.HandlerFunc(admin.SetDisabled)))

NewAdmin takes an authkit.AdminStore, which is gouncer.Store plus ListUsers and SetUserDisabled. authkit/postgres implements it. The handlers read the target id from r.PathValue("id"), so they work on the standard mux and on routers that populate path values.

Three behaviors worth knowing:

  • Listings never carry password material. The Postgres store never even reads the hash column for a listing.
  • Disabling an account deletes every session that account holds, in the same transaction as the flag. Re-enabling later cannot resurrect a stolen session.
  • A signed-in admin cannot disable their own account. The guard runs against the request’s Identity.

Expired sessions miss on lookup either way, but the rows need collecting. The reaper sweeps on an interval until stopped:

reaper := authkit.NewReaper(store, authkit.ReaperConfig{})
reaper.Start()
defer reaper.Stop()

Stop cancels the loop and waits for an in-flight sweep to drain, so call it before closing your database pool. Interval, per-sweep timeout, and the logger are ReaperConfig fields with defaults.

Every fresh deployment faces the same chicken-and-egg: user creation sits behind a login. CreateAdmin is the answer, built for a subcommand of your binary:

err := authkit.CreateAdmin(ctx, store, email, name, os.Stdin, os.Stdout)

It prompts for the password on stdin, validates through gouncer.NewUser, and stores the account. Because it is the app binary itself, it works with docker compose exec in a distroless image, with no shell and no extra tooling. The operations contract shows the deployment shape.