Skip to content

Commit ca233ce

Browse files
Fix OAuth1 double-encoding issue for special characters in path segments
Co-authored-by: alexeyzimarev <[email protected]>
1 parent 2468e98 commit ca233ce

File tree

2 files changed

+77
-1
lines changed

2 files changed

+77
-1
lines changed

src/RestSharp/Authenticators/OAuth/OAuthTools.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,12 @@ static string ConstructRequestUrl(Uri url) {
154154
var secure = url is { Scheme: "https", Port: 443 };
155155
var port = basic || secure ? "" : $":{url.Port}";
156156

157-
return $"{url.Scheme}://{url.Host}{port}{url.AbsolutePath}";
157+
// Decode the path to avoid double-encoding when the path contains already-encoded characters
158+
// For example, if the path contains "%21" (encoded !), we decode it back to "!" here,
159+
// and it will be properly encoded again in UrlEncodeRelaxed
160+
var decodedPath = Uri.UnescapeDataString(url.AbsolutePath);
161+
162+
return $"{url.Scheme}://{url.Host}{port}{decodedPath}";
158163
}
159164

160165
/// <summary>

test/RestSharp.Tests/Auth/OAuth1SignatureTests.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,75 @@ public void Generates_correct_signature_base() {
4949
"POST&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fstatuses%2Fupdate.json&include_entities%3Dtrue%26oauth_consumer_key%3Dxvz1evFS4wEEPTGEFPHBog%26oauth_nonce%3DkYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1318622958%26oauth_token%3D370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb%26oauth_version%3D1.0%26status%3DHello%2520Ladies%2520%252B%2520Gentlemen%252C%2520a%2520signed%2520OAuth%2520request%2521"
5050
);
5151
}
52+
53+
[Fact]
54+
public void Handles_path_with_exclamation_mark() {
55+
// Test that a path segment with ! is encoded correctly in the signature base
56+
var client = new RestClient("https://api.example.com");
57+
var request = new RestRequest("path/with!exclamation/resource", Method.Get);
58+
59+
const string method = "GET";
60+
var url = client.BuildUri(request).ToString();
61+
var parameters = new WebPairCollection();
62+
63+
_workflow.RequestUrl = url;
64+
var oauthParameters = _workflow.BuildProtectedResourceSignature(method, parameters);
65+
66+
var signatureBase = OAuthTools.ConcatenateRequestElements(method, url, oauthParameters.Parameters);
67+
68+
// The URL should be encoded with ! as %21 in the signature base
69+
signatureBase.Should().Contain("path%2Fwith%21exclamation%2Fresource");
70+
}
71+
72+
[Theory]
73+
[InlineData("path/with!exclamation", "%21")]
74+
[InlineData("path/with*asterisk", "%2A")]
75+
[InlineData("path/with'apostrophe", "%27")]
76+
[InlineData("path/with(paren", "%28")]
77+
[InlineData("path/with)paren", "%29")]
78+
public void Encodes_RFC3986_special_chars_in_path(string path, string encodedChar) {
79+
// Test that RFC 3986 special characters are properly encoded in path segments
80+
var client = new RestClient("https://api.example.com");
81+
var request = new RestRequest(path, Method.Get);
82+
83+
const string method = "GET";
84+
var url = client.BuildUri(request).ToString();
85+
var parameters = new WebPairCollection();
86+
87+
_workflow.RequestUrl = url;
88+
var oauthParameters = _workflow.BuildProtectedResourceSignature(method, parameters);
89+
90+
var signatureBase = OAuthTools.ConcatenateRequestElements(method, url, oauthParameters.Parameters);
91+
92+
// The URL should contain the encoded character in the signature base
93+
signatureBase.Should().Contain(encodedChar);
94+
}
95+
96+
[Theory]
97+
[InlineData("with!exclamation")]
98+
[InlineData("with*asterisk")]
99+
[InlineData("with'apostrophe")]
100+
[InlineData("with(paren")]
101+
[InlineData("with)paren")]
102+
public void Handles_url_segment_with_RFC3986_special_chars(string segmentValue) {
103+
// Test that URL segment parameters with RFC 3986 special characters don't get double-encoded
104+
var client = new RestClient("https://api.example.com");
105+
var request = new RestRequest("path/{segment}/resource", Method.Get);
106+
request.AddUrlSegment("segment", segmentValue);
107+
108+
const string method = "GET";
109+
var url = client.BuildUri(request).ToString();
110+
var parameters = new WebPairCollection();
111+
112+
_workflow.RequestUrl = url;
113+
var oauthParameters = _workflow.BuildProtectedResourceSignature(method, parameters);
114+
115+
var signatureBase = OAuthTools.ConcatenateRequestElements(method, url, oauthParameters.Parameters);
116+
117+
// The signature base should NOT contain double-encoded characters like %2521 (which is %25 + 21)
118+
signatureBase.Should().NotContain("%25");
119+
120+
// But it should contain properly encoded special chars
121+
signatureBase.Should().MatchRegex("%2[0-9A-F]");
122+
}
52123
}

0 commit comments

Comments
 (0)