Make /openapi.json reflect runtime auth configuration

The served OpenAPI spec now matches runtime behavior: when no bearer
tokens or policy are configured (open mode), the spec omits security
schemes and per-operation security requirements. When auth is active,
the full bearer_token security metadata is included.

Also fixes SecurityAddon to initialize components if absent, and
removes the redundant utoipa dev-dependency.

Adds 5 new tests covering open-mode vs auth-mode spec serving.

https://claude.ai/code/session_01NfoPVx21rZUQned1f7WpXY
This commit is contained in:
Claude 2026-04-11 16:31:48 +00:00
parent 859ec9faa8
commit 4c07d3c095
No known key found for this signature in database
2 changed files with 151 additions and 8 deletions

View file

@ -83,12 +83,13 @@ struct SecurityAddon;
impl utoipa::Modify for SecurityAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
if let Some(components) = openapi.components.as_mut() {
components.add_security_scheme(
openapi
.components
.get_or_insert_with(Default::default)
.add_security_scheme(
"bearer_token",
SecurityScheme::Http(Http::new(HttpAuthScheme::Bearer)),
);
}
}
}
@ -476,8 +477,35 @@ async fn server_health() -> Json<HealthOutput> {
})
}
async fn server_openapi() -> Json<utoipa::openapi::OpenApi> {
Json(ApiDoc::openapi())
async fn server_openapi(State(state): State<AppState>) -> Json<utoipa::openapi::OpenApi> {
let mut doc = ApiDoc::openapi();
if !state.requires_bearer_auth() {
strip_security(&mut doc);
}
Json(doc)
}
fn strip_security(doc: &mut utoipa::openapi::OpenApi) {
if let Some(components) = doc.components.as_mut() {
components.security_schemes.clear();
}
for path_item in doc.paths.paths.values_mut() {
for op in [
path_item.get.as_mut(),
path_item.post.as_mut(),
path_item.put.as_mut(),
path_item.delete.as_mut(),
path_item.options.as_mut(),
path_item.head.as_mut(),
path_item.patch.as_mut(),
path_item.trace.as_mut(),
]
.into_iter()
.flatten()
{
op.security = None;
}
}
}
async fn require_bearer_auth(