/* * Copyright (c) 2016 AsyncHttpClient Project. All rights reserved. * * This program is licensed to you under the Apache License Version 2.0, * and you may not use this file except in compliance with the Apache License Version 2.0. * You may obtain a copy of the Apache License Version 2.0 at * http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, * software distributed under the Apache License Version 2.0 is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. */ package org.asynchttpclient.oauth; import static io.netty.handler.codec.http.HttpHeaderNames.AUTHORIZATION; import static org.asynchttpclient.Dsl.*; import static org.testng.Assert.*; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.asynchttpclient.Param; import org.asynchttpclient.Request; import org.testng.annotations.Test; /** * Tests the OAuth signature behavior. * * See <a href= "https://oauth.googlecode.com/svn/code/javascript/example/signature.html" >Signature Tester</a> for an online oauth signature checker. * */ public class OAuthSignatureCalculatorTest { private static final String CONSUMER_KEY = "dpf43f3p2l4k3l03"; private static final String CONSUMER_SECRET = "kd94hf93k423kf44"; public static final String TOKEN_KEY = "nnch734d00sl2jdk"; public static final String TOKEN_SECRET = "pfkkdhi9sl3r4s00"; public static final String NONCE = "kllo9940pd9333jh"; final static long TIMESTAMP = 1191242096; // sample from RFC https://tools.ietf.org/html/rfc5849#section-3.4.1 private void testSignatureBaseString(Request request) throws NoSuchAlgorithmException { String signatureBaseString = new OAuthSignatureCalculatorInstance()// .signatureBaseString(// new ConsumerKey("9djdj82h48djs9d2", CONSUMER_SECRET),// new RequestToken("kkk9d7dh3k39sjv7", TOKEN_SECRET),// request,// 137131201,// "7d8f3e4a").toString(); assertEquals(signatureBaseString, "POST&" // + "http%3A%2F%2Fexample.com%2Frequest" // + "&a2%3Dr%2520b%26"// + "a3%3D2%2520q%26" + "a3%3Da%26"// + "b5%3D%253D%25253D%26"// + "c%2540%3D%26"// + "c2%3D%26"// + "oauth_consumer_key%3D9djdj82h48djs9d2%26"// + "oauth_nonce%3D7d8f3e4a%26"// + "oauth_signature_method%3DHMAC-SHA1%26"// + "oauth_timestamp%3D137131201%26"// + "oauth_token%3Dkkk9d7dh3k39sjv7%26"// + "oauth_version%3D1.0"); } // fork above test with an OAuth token that requires encoding private void testSignatureBaseStringWithEncodableOAuthToken(Request request) throws NoSuchAlgorithmException { String signatureBaseString = new OAuthSignatureCalculatorInstance()// .signatureBaseString(// new ConsumerKey("9djdj82h48djs9d2", CONSUMER_SECRET),// new RequestToken("kkk9d7dh3k39sjv7", TOKEN_SECRET),// request,// 137131201,// "ZLc92RAkooZcIO/0cctl0Q==").toString(); assertEquals(signatureBaseString, "POST&" // + "http%3A%2F%2Fexample.com%2Frequest" // + "&a2%3Dr%2520b%26"// + "a3%3D2%2520q%26" + "a3%3Da%26"// + "b5%3D%253D%25253D%26"// + "c%2540%3D%26"// + "c2%3D%26"// + "oauth_consumer_key%3D9djdj82h48djs9d2%26"// + "oauth_nonce%3DZLc92RAkooZcIO%252F0cctl0Q%253D%253D%26"// + "oauth_signature_method%3DHMAC-SHA1%26"// + "oauth_timestamp%3D137131201%26"// + "oauth_token%3Dkkk9d7dh3k39sjv7%26"// + "oauth_version%3D1.0"); } @Test public void testSignatureBaseStringWithProperlyEncodedUri() throws NoSuchAlgorithmException { Request request = post("http://example.com/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b")// .addFormParam("c2", "")// .addFormParam("a3", "2 q")// .build(); testSignatureBaseString(request); testSignatureBaseStringWithEncodableOAuthToken(request); } @Test public void testSignatureBaseStringWithRawUri() throws NoSuchAlgorithmException { // note: @ is legal so don't decode it into %40 because it won't be // encoded back // note: we don't know how to fix a = that should have been encoded as // %3D but who would be stupid enough to do that? Request request = post("http://example.com/request?b5=%3D%253D&a3=a&c%40=&a2=r b")// .addFormParam("c2", "")// .addFormParam("a3", "2 q")// .build(); testSignatureBaseString(request); testSignatureBaseStringWithEncodableOAuthToken(request); } // based on the reference test case from // http://oauth.pbwiki.com/TestCases @Test public void testGetCalculateSignature() throws NoSuchAlgorithmException, InvalidKeyException { Request request = get("http://photos.example.net/photos")// .addQueryParam("file", "vacation.jpg")// .addQueryParam("size", "original")// .build(); String signature = new OAuthSignatureCalculatorInstance()// .calculateSignature(new ConsumerKey(CONSUMER_KEY, CONSUMER_SECRET),// new RequestToken(TOKEN_KEY, TOKEN_SECRET),// request,// TIMESTAMP,// NONCE); assertEquals(signature, "tR3+Ty81lMeYAr/Fid0kMTYa/WM="); } @Test public void testPostCalculateSignature() throws UnsupportedEncodingException { StaticOAuthSignatureCalculator calc = // new StaticOAuthSignatureCalculator(// new ConsumerKey(CONSUMER_KEY, CONSUMER_SECRET),// new RequestToken(TOKEN_KEY, TOKEN_SECRET),// NONCE,// TIMESTAMP); final Request req = post("http://photos.example.net/photos")// .addFormParam("file", "vacation.jpg")// .addFormParam("size", "original")// .setSignatureCalculator(calc)// .build(); // From the signature tester, POST should look like: // normalized parameters: // file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original // signature base string: // POST&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal // signature: wPkvxykrw+BTdCcGqKr+3I+PsiM= // header: OAuth // realm="",oauth_version="1.0",oauth_consumer_key="dpf43f3p2l4k3l03",oauth_token="nnch734d00sl2jdk",oauth_timestamp="1191242096",oauth_nonce="kllo9940pd9333jh",oauth_signature_method="HMAC-SHA1",oauth_signature="wPkvxykrw%2BBTdCcGqKr%2B3I%2BPsiM%3D" String authHeader = req.getHeaders().get(AUTHORIZATION); Matcher m = Pattern.compile("oauth_signature=\"(.+?)\"").matcher(authHeader); assertEquals(m.find(), true); String encodedSig = m.group(1); String sig = URLDecoder.decode(encodedSig, "UTF-8"); assertEquals(sig, "wPkvxykrw+BTdCcGqKr+3I+PsiM="); } @Test public void testGetWithRequestBuilder() throws UnsupportedEncodingException { StaticOAuthSignatureCalculator calc = // new StaticOAuthSignatureCalculator(// new ConsumerKey(CONSUMER_KEY, CONSUMER_SECRET),// new RequestToken(TOKEN_KEY, TOKEN_SECRET),// NONCE,// TIMESTAMP); final Request req = get("http://photos.example.net/photos")// .addQueryParam("file", "vacation.jpg")// .addQueryParam("size", "original")// .setSignatureCalculator(calc)// .build(); final List<Param> params = req.getQueryParams(); assertEquals(params.size(), 2); // From the signature tester, the URL should look like: // normalized parameters: // file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original // signature base string: // GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal // signature: tR3+Ty81lMeYAr/Fid0kMTYa/WM= // Authorization header: OAuth // realm="",oauth_version="1.0",oauth_consumer_key="dpf43f3p2l4k3l03",oauth_token="nnch734d00sl2jdk",oauth_timestamp="1191242096",oauth_nonce="kllo9940pd9333jh",oauth_signature_method="HMAC-SHA1",oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D" String authHeader = req.getHeaders().get(AUTHORIZATION); Matcher m = Pattern.compile("oauth_signature=\"(.+?)\"").matcher(authHeader); assertEquals(m.find(), true); String encodedSig = m.group(1); String sig = URLDecoder.decode(encodedSig, "UTF-8"); assertEquals(sig, "tR3+Ty81lMeYAr/Fid0kMTYa/WM="); assertEquals(req.getUrl(), "http://photos.example.net/photos?file=vacation.jpg&size=original"); } @Test public void testGetWithRequestBuilderAndQuery() throws UnsupportedEncodingException { StaticOAuthSignatureCalculator calc = // new StaticOAuthSignatureCalculator(// new ConsumerKey(CONSUMER_KEY, CONSUMER_SECRET),// new RequestToken(TOKEN_KEY, TOKEN_SECRET),// NONCE,// TIMESTAMP); final Request req = get("http://photos.example.net/photos?file=vacation.jpg&size=original")// .setSignatureCalculator(calc)// .build(); final List<Param> params = req.getQueryParams(); assertEquals(params.size(), 2); // From the signature tester, the URL should look like: // normalized parameters: // file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original // signature base string: // GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal // signature: tR3+Ty81lMeYAr/Fid0kMTYa/WM= // Authorization header: OAuth // realm="",oauth_version="1.0",oauth_consumer_key="dpf43f3p2l4k3l03",oauth_token="nnch734d00sl2jdk",oauth_timestamp="1191242096",oauth_nonce="kllo9940pd9333jh",oauth_signature_method="HMAC-SHA1",oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D" String authHeader = req.getHeaders().get(AUTHORIZATION); Matcher m = Pattern.compile("oauth_signature=\"(.+?)\"").matcher(authHeader); assertTrue(m.find()); String encodedSig = m.group(1); String sig = URLDecoder.decode(encodedSig, "UTF-8"); assertEquals(sig, "tR3+Ty81lMeYAr/Fid0kMTYa/WM="); assertEquals(req.getUrl(), "http://photos.example.net/photos?file=vacation.jpg&size=original"); assertEquals( authHeader, "OAuth oauth_consumer_key=\"dpf43f3p2l4k3l03\", oauth_token=\"nnch734d00sl2jdk\", oauth_signature_method=\"HMAC-SHA1\", oauth_signature=\"tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D\", oauth_timestamp=\"1191242096\", oauth_nonce=\"kllo9940pd9333jh\", oauth_version=\"1.0\""); } @Test public void testWithNullRequestToken() throws NoSuchAlgorithmException { final Request request = get("http://photos.example.net/photos?file=vacation.jpg&size=original").build(); String signatureBaseString = new OAuthSignatureCalculatorInstance()// .signatureBaseString(// new ConsumerKey("9djdj82h48djs9d2", CONSUMER_SECRET),// new RequestToken(null, null),// request,// 137131201,// "ZLc92RAkooZcIO/0cctl0Q==").toString(); assertEquals(signatureBaseString, "GET&" + // "http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26" + // "oauth_consumer_key%3D9djdj82h48djs9d2%26" + // "oauth_nonce%3DZLc92RAkooZcIO%252F0cctl0Q%253D%253D%26" + // "oauth_signature_method%3DHMAC-SHA1%26" + // "oauth_timestamp%3D137131201%26" + // "oauth_version%3D1.0%26size%3Doriginal"); } @Test public void testWithStarQueryParameterValue() throws NoSuchAlgorithmException { final Request request = get("http://term.ie/oauth/example/request_token.php?testvalue=*").build(); String signatureBaseString = new OAuthSignatureCalculatorInstance()// .signatureBaseString(// new ConsumerKey("key", "secret"),// new RequestToken(null, null),// request,// 1469019732,// "6ad17f97334700f3ec2df0631d5b7511").toString(); assertEquals(signatureBaseString, "GET&" + // "http%3A%2F%2Fterm.ie%2Foauth%2Fexample%2Frequest_token.php&"// + "oauth_consumer_key%3Dkey%26"// + "oauth_nonce%3D6ad17f97334700f3ec2df0631d5b7511%26"// + "oauth_signature_method%3DHMAC-SHA1%26"// + "oauth_timestamp%3D1469019732%26"// + "oauth_version%3D1.0%26"// + "testvalue%3D%252A"); } @Test public void testSignatureGenerationWithAsteriskInPath() throws InvalidKeyException, NoSuchAlgorithmException { ConsumerKey consumerKey = new ConsumerKey("key", "secret"); RequestToken requestToken = new RequestToken(null, null); String nonce = "6ad17f97334700f3ec2df0631d5b7511"; long timestamp = 1469019732; final Request request = get("http://example.com/oauth/example/*path/wi*th/asterisks*").build(); String expectedSignature = "cswi/v3ZqhVkTyy5MGqW841BxDA="; String actualSignature = new OAuthSignatureCalculatorInstance().calculateSignature(consumerKey, requestToken, request, timestamp, nonce); assertEquals(actualSignature, expectedSignature); String generatedAuthHeader = new OAuthSignatureCalculatorInstance().constructAuthHeader(consumerKey, requestToken, actualSignature, nonce, timestamp); assertTrue(generatedAuthHeader.contains("oauth_signature=\"cswi%2Fv3ZqhVkTyy5MGqW841BxDA%3D\"")); } }