OpenAPITools / OpenAPITools/openapi-generator
[BUG][C#][generichost] All schemes from OR-alternative security requirements are applied — apiKey "in: query" leaks the secret into the URL
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator? (
openapi-generator-cli validate→ "No validation issues detected.") - Have you tested with the latest master to confirm the issue still exists? (reproduced on latest released 7.23.0; also present on 7.13.0 — not built from master)
- Have you searched for related issues/PRs?
- What's the actual output vs expected output?
- [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
When the top-level (or operation-level) security lists multiple Security Requirement Objects, those are alternatives — per the OpenAPI Specification, the array items are combined with logical OR, and "only one of the Security Requirement Objects needs to be satisfied to authorize a request."
The csharp / generichost generator does not honor this. Instead of satisfying a single requirement object, the generated operation applies the union of every scheme across all alternatives. For a spec offering "apiKey-in-header OR apiKey-in-query", the generated method sends the key both as a header and as a query parameter on every request.
To be fair, this is arguably not a hard spec violation: OR doesn't forbid also satisfying the other alternatives, so sending both is technically a superset that still satisfies the requirement, and auth succeeds. The problem is what it costs you. When one of the alternatives is an apiKey with in: query, the generator unconditionally writes the secret into the request URL via UseInQuery(...). API keys in query strings routinely end up in server/proxy access logs, gateway telemetry, browser history, and referrer headers — so this turns a harmless-looking "we accept the key either way" spec into an unintended, hard-to-notice secret leak, even though a single header alternative would have fully authorized the request. The user has no way to tell the generator "just use the header alternative."
openapi-generator version
7.23.0 (latest stable release). Also reproduces on 7.13.0, so it is not a recent regression.
OpenAPI declaration file content or url
openapi: 3.0.3
info:
title: Security OR repro
version: 1.0.0
servers:
- url: https://api.example.com
security:
- apiKeyHeader: []
- apiKeyQuery: []
paths:
/ping:
get:
operationId: ping
responses:
'200':
description: OK
components:
securitySchemes:
apiKeyHeader:
type: apiKey
in: header
name: X-API-Key
apiKeyQuery:
type: apiKey
in: query
name: api-key
Generation Details
openapi-generator-cli generate \
-i security-or.yaml \
-g csharp \
--library generichost \
--additional-properties=packageName=Repro,targetFramework=net8.0 \
-o ./out
Steps to reproduce
- Save the spec above as
security-or.yaml. openapi-generator-cli validate -i security-or.yaml→ "No validation issues detected."- Generate with the command above.
- Open
out/src/Repro/Api/DefaultApi.csand look at the generatedpingoperation. Both schemes are applied — the query apiKey and the header apiKey:
ApiKeyToken apiKeyTokenLocalVar1 = (ApiKeyToken) await ApiKeyProvider.GetAsync("api-key", cancellationToken).ConfigureAwait(false);
tokenBaseLocalVars.Add(apiKeyTokenLocalVar1);
apiKeyTokenLocalVar1.UseInQuery(httpRequestMessageLocalVar, uriBuilderLocalVar, parseQueryStringLocalVar); // <-- secret written into the URL
uriBuilderLocalVar.Query = parseQueryStringLocalVar.ToString();
ApiKeyToken apiKeyTokenLocalVar2 = (ApiKeyToken) await ApiKeyProvider.GetAsync("X-API-Key", cancellationToken).ConfigureAwait(false);
tokenBaseLocalVars.Add(apiKeyTokenLocalVar2);
apiKeyTokenLocalVar2.UseInHeader(httpRequestMessageLocalVar);
Actual: every request carries the key in both locations, including ?api-key=<secret> in the URL.
Expected: a single Security Requirement Object is satisfied (e.g., the first listed alternative → header only), so the query apiKey is not emitted. Ideally, expose a way to choose which alternative to apply.
Related issues/PRs
Same class of bug already reported for other generators (alternatives applied as if AND / all-at-once), which suggests shared root logic rather than a C#-only quirk:
- [BUG][DART] Multiple security options are all being added instead of only one — #7057
- [BUG][PYTHON] Multiple authentication schemes are used, only want to use one — #3844
Suggest a fix
The generichost operation template emits one ApiKeyProvider.GetAsync(...) + UseInQuery/UseInHeader per security scheme, iterating the flattened union of all schemes across every Security Requirement Object. It should instead satisfy a single requirement object — the natural default being the first one in the security list — so OR alternatives are not all applied at once. A configurable "preferred security requirement" (or skipping in: query apiKeys when a non-query alternative exists) would also address the leak. The relevant logic is where the operation template renders the per-scheme auth block (the GetAsync(...).UseInQuery(...) / .UseInHeader(...) sequence).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Generate the provided security-or YAML with csharp and the generichost library, then inspect out/src/Repro/Api/DefaultApi.cs and the ping operation. Trace the operation template's per-scheme authentication block, especially the GetAsync(...), UseInQuery(...), and UseInHeader(...) sequence. Done means one security alternative is applied and the query apiKey is not emitted when the header alternative is selected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, security, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100