Skip to content

Navigation Menu

Sign in
Sign up

Security in the Document

wiki edited this page Sep 4, 2026 · 1 revision

Security in the document

The generator documents authentication without importing the security extension. It reads two things: the scheme registry from the DI container, and interfaces on routes.

Schemes

The security extension registers a rextension.SchemeRegistry in the container during its OnInitialize; the generator resolves it in OnStart. If it is absent — no security extension configured — nothing is documented and nothing fails.

"components": {
 "securitySchemes": {
 "jwt": {
 "type": "http",
 "scheme": "bearer",
 "bearerFormat": "JWT",
 "description": "Access token issued by the identity provider",
 "x-roles-claim": "realm_access.roles"
 },
 "session": {
 "type": "apiKey",
 "in": "cookie",
 "name": "session_id"
 }
 }
}
Emitted from Read through
type, description SecuritySchemeAccessor
name, in ParameterizedSchemeParamName(), Location()
bearerFormat BearerFormatProvider
x-roles-claim RoleClaimProvider

Each is optional and type-asserted, so a scheme that implements none is still documented with the basics.

Location() returns a plain string on purpose. It used to return a named type, which is why the generator had to reach it with reflect.MethodByName("Location") and format the result with %s — a named string type cannot satisfy an interface declaring Location() string.

Per-operation security

A route implementing SecuredRouteAccessor gets a security block:

func (r *AdminRoute) RequiredSchemes() []string { return []string{"jwt"} }
"security": [{"jwt": []}]

Scopes

func (r *AdminRoute) RequiredScopes() map[string][]string {
	return map[string][]string{"jwt": {"users.write", "users.read"}}
}
"security": [{"jwt": ["users.write", "users.read"]}]

The scopes go into the security requirement itself, which is where OpenAPI puts them — rather than emitting an empty slice and losing the information.

Roles

Roles are not an OpenAPI concept, so they are emitted as a vendor extension:

func (r *AdminRoute) RequiredRoles() map[string][]string {
	return map[string][]string{"jwt": {"admin"}}
}
"x-required-roles": {"jwt": ["admin"]}

rextension-swagger reads this and renders a Required Authorization panel on the operation, so someone reading the UI can see that an endpoint needs admin before they try it.

x-roles-claim on the scheme tells the same UI where in the token those roles live.

The map keys must match

Both RequiredRoles and RequiredScopes are keyed by scheme name, and each key must appear in RequiredSchemes. The security extension makes a mismatch a startup error; the generator would simply emit an entry nobody enforces.

No package-level registry

The generator resolves the registry through the rextension.SchemeRegistry interface, from the container.

It used to read a package-level slice in rextension, written by RegisterSecuritySchemes — which replaced rather than appended and had no unregister. Two Rex instances in one process clobbered each other's schemes, and state leaked between tests in the same binary. Same decoupling, without the process-global state.

Documenting an unsecured API

If no security extension is registered, components.securitySchemes is absent and no operation carries a security block. That is correct for an API with no authentication — and a good reason to check the generated document in review: an operation you expected to be secured that has no security block is a route that is not secured.

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /