Skip to content

[csharp] Fix HTTP signature auth failure on .NET 8 when query params contain special characters#23714

Merged
wing328 merged 1 commit intoOpenAPITools:masterfrom
sagadira:fix/csharp-http-signing-query-param-url-encoding
May 8, 2026
Merged

[csharp] Fix HTTP signature auth failure on .NET 8 when query params contain special characters#23714
wing328 merged 1 commit intoOpenAPITools:masterfrom
sagadira:fix/csharp-http-signing-query-param-url-encoding

Conversation

@sagadira
Copy link
Copy Markdown
Contributor

@sagadira sagadira commented May 7, 2026

Summary

Replace compile-time Mustache {{#net90OrLater}} conditional with runtime .NET version detection for URL-encoding query parameter keys in HTTP signature computation.

Problem

When targeting net8.0, the {{#net90OrLater}} Mustache conditional evaluates to false, causing HttpUtility.UrlEncode() to be completely omitted from the generated HttpSigningConfiguration.cs. The generated code becomes:

string key = framework.StartsWith(".NET 9") ? parameter.Key : parameter.Key; // no-op!

Meanwhile, RestSharp 112+ always URL-encodes query parameters on the wire. This mismatch means the HTTP signature is computed over unencoded keys (e.g. $filter) while the actual request sends encoded keys (e.g. %24filter), resulting in HTTP 401 on every request with special characters in query parameter names (OData $filter, $top, $orderby, etc.).

Solution:

Use RuntimeInformation.FrameworkDescription to detect the .NET major version at runtime:

.NET 9+: Skip explicit URL-encoding (HttpUtility.ParseQueryString already encodes keys internally on .NET 9+, so encoding again would cause double-encoding)
.NET 8 and earlier: Explicitly call HttpUtility.UrlEncode(parameter.Key) so the signature matches what RestSharp sends

string framework = RuntimeInformation.FrameworkDescription;
bool skipUrlEncode = framework.StartsWith(".NET ") &&
    int.TryParse(framework.Substring(5).Split('.')[0], out int fwMajor) && fwMajor >= 9;
// ...
string key = skipUrlEncode ? parameter.Key : HttpUtility.UrlEncode(parameter.Key);

PR checklist
Read the contribution guidelines.
Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
Run the following to build the project and update samples:

./mvnw clean package -DskipTests
./bin/generate-samples.sh bin/configs/csharp-restsharp*.yaml bin/configs/csharp-httpclient*.yaml
./bin/utils/export_docs_generators.sh

File the PR against the correct branch: master
If your PR is targeting a particular programming language, @mention the technical committee members for that language.
@muttleyxd @devhl-labs @lucasheim @shibayan (C# generators)


Summary by cubic

Fixes HTTP signature auth failures on .NET 8 and ensures correct behavior on .NET 9+ when query parameter names contain special characters by aligning URL-encoding with the actual request. We now detect the .NET version at runtime to decide whether to URL-encode query keys.

  • Bug Fixes
    • Replaced Mustache {{#net90OrLater}} with runtime detection via RuntimeInformation.FrameworkDescription.
    • .NET 9+: skip UrlEncode to avoid double-encoding; .NET 8 and earlier: UrlEncode keys to match RestSharp 112+ requests.
    • Updated HttpSigningConfiguration template and regenerated C# samples (httpclient, restsharp, unityWebRequest).

Written for commit b56455a. Summary will update on new commits.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 12 files

@sagadira sagadira force-pushed the fix/csharp-http-signing-query-param-url-encoding branch from 98c1c14 to 76934f8 Compare May 7, 2026 08:01
@wing328 wing328 added this to the 7.23.0 milestone May 7, 2026
@wing328
Copy link
Copy Markdown
Member

wing328 commented May 7, 2026

thanks for the PR

cc @mandrean (2017/08) @shibayan (2020/02) @Blackclaws (2021/03) @lucamazzanti (2021/05) @iBicha (2023/07)

// characters (e.g. '$' in OData params like $filter) are encoded the same way
// in the signature as they are in the outgoing HTTP request.
string framework = RuntimeInformation.FrameworkDescription;
bool skipUrlEncode = framework.StartsWith(".NET ") &&
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about declaring this boolean flag similar to SigningAlgorithm (a property of this class) and initialize it in the constructor to avoid computing the value every time this function is called?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactored skipUrlEncode from a local variable in GetHttpSignedHeader to a private readonly bool _skipUrlEncode field initialized once in the constructor. This avoids recomputing the runtime version check on every call.

…ncoding in HTTP signature

The HttpSigningConfiguration template used the Mustache conditional
net90OrLater to decide whether to URL-encode query parameter keys.
When targeting net8.0, this conditional evaluates to false and the
UrlEncode call is omitted entirely, producing an unencoded key in the
signature base string.  However, RestSharp 112+ always sends
URL-encoded query parameters on the wire, causing a signature mismatch
and HTTP 401 on every request that contains special characters in query
parameter names (e.g. OData \, \, \).

Replace the compile-time Mustache conditional with runtime detection
using RuntimeInformation.FrameworkDescription.  On .NET 9+ the key is
left as-is (ParseQueryString already encodes internally); on .NET 8 and
earlier, HttpUtility.UrlEncode is called explicitly so the signature
matches the actual request.
@sagadira sagadira force-pushed the fix/csharp-http-signing-query-param-url-encoding branch from 76934f8 to b56455a Compare May 7, 2026 17:10
@wing328 wing328 merged commit 7e41eaf into OpenAPITools:master May 8, 2026
73 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants