Skip to content

Commit d201d8f

Browse files
SvenSchindlerslandelle
authored andcommitted
Use percent encoding in OAuth according to rfc 5849 section 3.6 (AsyncHttpClient#1332)
* Use percent encoding according to rfc 5849 section 3.6 for oauth signature generation so as to allow url paths such as foo/*bar/ * add test for oauth calculator and asterisk in path * improve testability for oauth signature generation
1 parent 8809d53 commit d201d8f

File tree

4 files changed

+47
-11
lines changed

4 files changed

+47
-11
lines changed

client/src/main/java/org/asynchttpclient/oauth/OAuthSignatureCalculator.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,19 @@ private String encodedParams(long oauthTimestamp, String nonce, List<Param> form
9292
OAuthParameterSet allParameters = new OAuthParameterSet(allParametersSize);
9393

9494
// start with standard OAuth parameters we need
95-
allParameters.add(KEY_OAUTH_CONSUMER_KEY, Utf8UrlEncoder.encodeQueryElement(consumerAuth.getKey()));
96-
allParameters.add(KEY_OAUTH_NONCE, Utf8UrlEncoder.encodeQueryElement(nonce));
95+
allParameters.add(KEY_OAUTH_CONSUMER_KEY, Utf8UrlEncoder.percentEncodeQueryElement(consumerAuth.getKey()));
96+
allParameters.add(KEY_OAUTH_NONCE, Utf8UrlEncoder.percentEncodeQueryElement(nonce));
9797
allParameters.add(KEY_OAUTH_SIGNATURE_METHOD, OAUTH_SIGNATURE_METHOD);
9898
allParameters.add(KEY_OAUTH_TIMESTAMP, String.valueOf(oauthTimestamp));
9999
if (userAuth.getKey() != null) {
100-
allParameters.add(KEY_OAUTH_TOKEN, Utf8UrlEncoder.encodeQueryElement(userAuth.getKey()));
100+
allParameters.add(KEY_OAUTH_TOKEN, Utf8UrlEncoder.percentEncodeQueryElement(userAuth.getKey()));
101101
}
102102
allParameters.add(KEY_OAUTH_VERSION, OAUTH_VERSION_1_0);
103103

104104
if (formParams != null) {
105105
for (Param param : formParams) {
106106
// formParams are not already encoded
107-
allParameters.add(Utf8UrlEncoder.encodeQueryElement(param.getName()), Utf8UrlEncoder.encodeQueryElement(param.getValue()));
107+
allParameters.add(Utf8UrlEncoder.percentEncodeQueryElement(param.getName()), Utf8UrlEncoder.percentEncodeQueryElement(param.getValue()));
108108
}
109109
}
110110
if (queryParams != null) {
@@ -164,11 +164,11 @@ StringBuilder signatureBaseString(Request request, long oauthTimestamp, String n
164164
StringBuilder sb = StringUtils.stringBuilder();
165165
sb.append(request.getMethod()); // POST / GET etc (nothing to URL encode)
166166
sb.append('&');
167-
Utf8UrlEncoder.encodeAndAppendQueryElement(sb, baseUrl);
167+
Utf8UrlEncoder.encodeAndAppendPercentEncoded(sb, baseUrl);
168168

169169
// and all that needs to be URL encoded (... again!)
170170
sb.append('&');
171-
Utf8UrlEncoder.encodeAndAppendQueryElement(sb, encodedParams);
171+
Utf8UrlEncoder.encodeAndAppendPercentEncoded(sb, encodedParams);
172172
return sb;
173173
}
174174

@@ -182,7 +182,7 @@ String calculateSignature(Request request, long oauthTimestamp, String nonce) {
182182
return Base64.encode(rawSignature);
183183
}
184184

185-
private String constructAuthHeader(String signature, String nonce, long oauthTimestamp) {
185+
String constructAuthHeader(String signature, String nonce, long oauthTimestamp) {
186186
StringBuilder sb = StringUtils.stringBuilder();
187187
sb.append("OAuth ");
188188
sb.append(KEY_OAUTH_CONSUMER_KEY).append("=\"").append(consumerAuth.getKey()).append("\", ");
@@ -193,23 +193,23 @@ private String constructAuthHeader(String signature, String nonce, long oauthTim
193193

194194
// careful: base64 has chars that need URL encoding:
195195
sb.append(KEY_OAUTH_SIGNATURE).append("=\"");
196-
Utf8UrlEncoder.encodeAndAppendQueryElement(sb, signature).append("\", ");
196+
Utf8UrlEncoder.encodeAndAppendPercentEncoded(sb, signature).append("\", ");
197197
sb.append(KEY_OAUTH_TIMESTAMP).append("=\"").append(oauthTimestamp).append("\", ");
198198

199199
// also: nonce may contain things that need URL encoding (esp. when using base64):
200200
sb.append(KEY_OAUTH_NONCE).append("=\"");
201-
Utf8UrlEncoder.encodeAndAppendQueryElement(sb, nonce);
201+
Utf8UrlEncoder.encodeAndAppendPercentEncoded(sb, nonce);
202202
sb.append("\", ");
203203

204204
sb.append(KEY_OAUTH_VERSION).append("=\"").append(OAUTH_VERSION_1_0).append("\"");
205205
return sb.toString();
206206
}
207207

208-
protected long generateTimestamp() {
208+
long generateTimestamp() {
209209
return System.currentTimeMillis() / 1000L;
210210
}
211211

212-
protected String generateNonce() {
212+
String generateNonce() {
213213
byte[] nonceBuffer = NONCE_BUFFER.get();
214214
ThreadLocalRandom.current().nextBytes(nonceBuffer);
215215
// let's use base64 encoding over hex, slightly more compact than hex or decimals

client/src/main/java/org/asynchttpclient/util/Utf8UrlEncoder.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,16 @@ public static StringBuilder encodeAndAppendFormElement(StringBuilder sb, CharSeq
142142
return appendEncoded(sb, input, FORM_URL_ENCODED_SAFE_CHARS, true);
143143
}
144144

145+
public static String percentEncodeQueryElement(String input) {
146+
StringBuilder sb = new StringBuilder(input.length() + 6);
147+
encodeAndAppendPercentEncoded(sb, input);
148+
return sb.toString();
149+
}
150+
151+
public static StringBuilder encodeAndAppendPercentEncoded(StringBuilder sb, CharSequence input) {
152+
return appendEncoded(sb, input, RFC3986_UNRESERVED_CHARS, false);
153+
}
154+
145155
private static StringBuilder lazyInitStringBuilder(CharSequence input, int firstNonUsAsciiPosition) {
146156
StringBuilder sb = new StringBuilder(input.length() + 6);
147157
for (int i = 0; i < firstNonUsAsciiPosition; i++) {

client/src/test/java/org/asynchttpclient/oauth/OAuthSignatureCalculatorTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,4 +309,22 @@ public void testWithStarQueryParameterValue() {
309309
+ "oauth_version%3D1.0%26"//
310310
+ "testvalue%3D%252A");
311311
}
312+
313+
@Test
314+
public void testSignatureGenerationWithAsteriskInPath() {
315+
ConsumerKey consumer = new ConsumerKey("key", "secret");
316+
String someNonce = "6ad17f97334700f3ec2df0631d5b7511";
317+
long someTimestamp = 1469019732;
318+
String urlWithAsterisksInPath = "http://example.com/oauth/example/*path/wi*th/asterisks*";
319+
320+
OAuthSignatureCalculator calc = new OAuthSignatureCalculator(consumer, new RequestToken(null, null));
321+
final Request request = get(urlWithAsterisksInPath).build();
322+
323+
String expectedSignature = "cswi/v3ZqhVkTyy5MGqW841BxDA=";
324+
String actualSignature = calc.calculateSignature(request, someTimestamp, someNonce);
325+
assertEquals(actualSignature, expectedSignature);
326+
327+
String generatedAuthHeader = calc.constructAuthHeader(actualSignature, someNonce, someTimestamp);
328+
assertTrue(generatedAuthHeader.contains("oauth_signature=\"cswi%2Fv3ZqhVkTyy5MGqW841BxDA%3D\""));
329+
}
312330
}

client/src/test/java/org/asynchttpclient/util/TestUTF8UrlCodec.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,12 @@ public void testBasics() {
2626
assertEquals(Utf8UrlEncoder.encodeQueryElement("a&b"), "a%26b");
2727
assertEquals(Utf8UrlEncoder.encodeQueryElement("a+b"), "a%2Bb");
2828
}
29+
30+
@Test(groups = "standalone")
31+
public void testPercentageEncoding() {
32+
assertEquals(Utf8UrlEncoder.percentEncodeQueryElement("foobar"), "foobar");
33+
assertEquals(Utf8UrlEncoder.percentEncodeQueryElement("foo*bar"), "foo%2Abar");
34+
assertEquals(Utf8UrlEncoder.percentEncodeQueryElement("foo~b_ar"), "foo~b_ar");
35+
}
36+
2937
}

0 commit comments

Comments
 (0)