Bug Report Checklist
Description
The generated context.rs authentication middleware in the rust-server generator uses swagger::auth::from_headers(headers) for isBasicBasic security scheme blocks. This function matches both Basic and Bearer Authorization headers (it parses the header value case-insensitively for either scheme prefix). Each matched block does an early return, short-circuiting evaluation of any later security scheme blocks.
This means that for any API defining multiple security schemes where HTTP Basic auth is listed before an API key (in-header) scheme, a request carrying Authorization: Bearer <token> will be greedily captured by the Basic auth block and returned immediately — the API key block is never reached.
Impact: In deployments where the Bearer token path has a permissive fallback (e.g. AllowAllAuthenticator when OAuth is not configured), this is a silent authorization bypass. Requests that should be authenticated via the API key header are instead authorized via the permissive Bearer fallback. The API key / client certificate is never evaluated.
This is a regression from the previous typed extraction approach which used swagger::auth::from_headers::<Basic>(headers) — a scheme-typed generic that only matched Basic headers.
openapi-generator version
Present across all 7.x releases
OpenAPI declaration file content or url
openapi: "3.0.3"
info:
title: Multi-Auth Example
version: "1.0.0"
paths:
/resource:
get:
operationId: getResource
security:
- BasicAuth: []
- ApiKeyAuth: []
- BearerAuth: []
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
properties:
data:
type: string
'403':
description: Forbidden
components:
securitySchemes:
BasicAuth:
type: http
scheme: basic
ApiKeyAuth:
type: apiKey
in: header
name: x-client-cert
BearerAuth:
type: http
scheme: bearer
Any spec defining both type: http, scheme: basic and type: apiKey, in: header security schemes on the same operation triggers this bug.
Generation Details
No special options required — the default rust-server generator reproduces the issue.
Steps to reproduce
- Generate a Rust server from the above spec using the
rust-server generator
- Open the generated
src/context.rs
- Observe the generated auth extraction in the
Service<Request<ReqBody>> impl for AddContext:
Actual output (generated context.rs):
// Block 1: intended for Basic auth only
{
use std::ops::Deref;
if let Some(auth) = swagger::auth::from_headers(headers) { // matches Basic OR Bearer!
let context = context.push(Some(auth));
return self.inner.call((request, context)) // early return
}
}
// Block 2: API key from header — NEVER REACHED for Bearer requests
{
use swagger::auth::api_key_from_header;
if let Some(header) = api_key_from_header(headers, "x-client-cert") {
let auth_data = AuthData::ApiKey(header);
let context = context.push(Some(auth_data));
return self.inner.call((request, context))
}
}
- Send a request with
Authorization: Bearer <token> and an x-client-cert header
- The Bearer token is captured by Block 1 and returned immediately — the API key block is never evaluated
Expected output (what the template should generate for Basic-only blocks):
// Block 1: Basic auth only — should not match Bearer
{
use swagger::auth::basic_from_headers; // or a typed/filtered variant
if let Some(auth) = swagger::auth::basic_from_headers(headers) { // matches Basic ONLY
let context = context.push(Some(auth));
return self.inner.call((request, context))
}
}
// Block 2: API key from header — now correctly reachable for Bearer requests
{
use swagger::auth::api_key_from_header;
if let Some(header) = api_key_from_header(headers, "x-client-cert") {
let auth_data = AuthData::ApiKey(header);
let context = context.push(Some(auth_data));
return self.inner.call((request, context))
}
}
Related issues/PRs
Suggest a fix
The context.mustache template needs to emit scheme-specific extraction for isBasicBasic blocks. Two possible approaches:
Option A: Add a basic_from_headers function to the swagger crate that only matches Basic:
pub fn basic_from_headers(headers: &HeaderMap) -> Option<AuthData> {
headers.get(AUTHORIZATION).and_then(|value| {
if let Ok(value_str) = value.to_str() {
if value_str.to_lowercase().starts_with("basic ") {
Basic::decode(value).map(|basic| {
AuthData::Basic(basic.username().to_string(), basic.password().to_string())
})
} else {
None
}
} else {
None
}
})
}
Then update the {{#isBasicBasic}} block in context.mustache to call basic_from_headers instead of from_headers.
Option B: Filter in the template itself — after calling from_headers, check that the returned AuthData variant matches the expected scheme before returning:
{{#isBasicBasic}}
{
if let Some(auth @ AuthData::Basic(..)) = swagger::auth::from_headers(headers) {
let context = context.push(Some(auth));
return self.inner.call((request, context))
}
}
{{/isBasicBasic}}
Similarly, the {{#isBasicBearer}} and {{#isOAuth}} blocks should filter for AuthData::Bearer(..) only.
Either approach restores the scheme-specific behavior that existed in swagger 5/6 and ensures API key / client certificate blocks remain reachable when a Bearer token is present.
Bug Report Checklist
Description
The generated
context.rsauthentication middleware in therust-servergenerator usesswagger::auth::from_headers(headers)forisBasicBasicsecurity scheme blocks. This function matches bothBasicandBearerAuthorization headers (it parses the header value case-insensitively for either scheme prefix). Each matched block does an earlyreturn, short-circuiting evaluation of any later security scheme blocks.This means that for any API defining multiple security schemes where HTTP Basic auth is listed before an API key (in-header) scheme, a request carrying
Authorization: Bearer <token>will be greedily captured by the Basic auth block and returned immediately — the API key block is never reached.Impact: In deployments where the Bearer token path has a permissive fallback (e.g.
AllowAllAuthenticatorwhen OAuth is not configured), this is a silent authorization bypass. Requests that should be authenticated via the API key header are instead authorized via the permissive Bearer fallback. The API key / client certificate is never evaluated.This is a regression from the previous typed extraction approach which used
swagger::auth::from_headers::<Basic>(headers)— a scheme-typed generic that only matchedBasicheaders.openapi-generator version
Present across all 7.x releases
OpenAPI declaration file content or url
Any spec defining both
type: http, scheme: basicandtype: apiKey, in: headersecurity schemes on the same operation triggers this bug.Generation Details
No special options required — the default
rust-servergenerator reproduces the issue.Steps to reproduce
rust-servergeneratorsrc/context.rsService<Request<ReqBody>>impl forAddContext:Actual output (generated
context.rs):Authorization: Bearer <token>and anx-client-certheaderExpected output (what the template should generate for Basic-only blocks):
Related issues/PRs
Suggest a fix
The
context.mustachetemplate needs to emit scheme-specific extraction forisBasicBasicblocks. Two possible approaches:Option A: Add a
basic_from_headersfunction to the swagger crate that only matches Basic:Then update the
{{#isBasicBasic}}block incontext.mustacheto callbasic_from_headersinstead offrom_headers.Option B: Filter in the template itself — after calling
from_headers, check that the returnedAuthDatavariant matches the expected scheme before returning:Similarly, the
{{#isBasicBearer}}and{{#isOAuth}}blocks should filter forAuthData::Bearer(..)only.Either approach restores the scheme-specific behavior that existed in swagger 5/6 and ensures API key / client certificate blocks remain reachable when a Bearer token is present.