📖 2 min read (~ 300 words).

HTTP Basic

Same shape as the API key example, but with username:password decoded by the runtime and a realm advertised on the failure response.

Spec

securityDefinitions:
  basicAuth:
    type: basic

security:
  - basicAuth: []

Wiring

api := untyped.NewAPI(doc).WithJSONDefaults()

api.RegisterAuth("basicAuth", security.BasicAuthRealmCtx(
	"petstore",
	func(ctx context.Context, user, pass string) (context.Context, any, error) {
		// request-scoped lookup — honours ctx cancellation
		principal, err := store.AuthenticateBasic(ctx, user, pass)
		if err != nil {
			return ctx, nil, errors.Unauthenticated("basic")
		}
		return ctx, principal, nil
	},
))

Full source: docs/examples/auth/basic/main.go

BasicAuthRealmCtx is the context-aware variant of BasicAuthRealm; the non-*Ctx form security.BasicAuthRealm("petstore", fn) takes a func(user, pass string) (any, error) instead.

Replying with WWW-Authenticate on 401

The runtime stashes the realm name in the request context when basic auth has been attempted and failed. Recover it from a custom error handler to render a proper challenge:

api.ServeError = func(w http.ResponseWriter, r *http.Request, err error) {
	if realm := security.FailedBasicAuthCtx(r.Context()); realm != "" {
		w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
	}
	errors.ServeError(w, r, err)
}

Full source: docs/examples/auth/basic/main.go

FailedBasicAuth(r) is the non-context spelling.

Exercise

# Valid credentials
curl -i -u alice:s3cret http://127.0.0.1:8080/pets

# Missing or wrong credentials → 401 with challenge
curl -i http://127.0.0.1:8080/pets
# HTTP/1.1 401 Unauthorized
# WWW-Authenticate: Basic realm="petstore"

When to combine with other schemes

Basic + Bearer is a common “either credential works” requirement. That’s the AND/OR composition case — see composed for how to declare and wire it.