API key (single scheme)
The shortest path to a secured endpoint. Mirrors the
go-swagger/examples/authentication
example, in untyped form.
Spec
Wiring
doc, _ := loads.Spec("swagger.yml")
api := untyped.NewAPI(doc).WithJSONDefaults()
// 1. Authenticator: token → principal
api.RegisterAuth("key", security.APIKeyAuth(
"X-Token", "header",
func(token string) (any, error) {
// Use subtle.ConstantTimeCompare to avoid leaking the
// expected token byte-by-byte via response timing.
if subtle.ConstantTimeCompare([]byte(token), []byte("abcdefuvwxyz")) == 1 {
return "alice", nil
}
return nil, errors.New(http.StatusUnauthorized, "invalid api key")
},
))
// 2. Authorizer: every authenticated principal allowed.
// (Skip this line if you have no business-rule gating.)
api.RegisterAuthorizer(security.Authorized())
// 3. Operation handlers (one per spec operation)
api.RegisterOperation("get", "/customers/{id}", runtime.OperationHandlerFunc(
func(_ any) (any, error) {
// params is the bound parameter struct;
// principal is on r.Context() via middleware.SecurityPrincipalFrom
return map[string]string{"id": "42"}, nil
},
))
handler := middleware.Serve(doc, api)
log.Fatal(http.ListenAndServe(":35307", handler))Full source: docs/examples/auth/apikey/main.go
The scheme name passed to RegisterAuth ("key") must match the
key under securityDefinitions in the spec.
Exercise
Variations
- Query param instead of header: change
in:toqueryin the spec and the second arg ofAPIKeyAuthto"query". The token comes from?api_key=…. - Context-aware lookup (DB call honouring request cancellation):
use
security.APIKeyAuthCtxinstead — same idea, the callback gets the requestcontext.Context. - Per-operation override: a route can opt out by setting
security: []; opt into a different scheme by replacing the list.