Reputation: 79
I'm writing a test program to learn to use OkHttp an Scribe. I have managed to use Scribe to retrieve my token and token secret. I of course already have the consumer key and consumer secret. Now I'm trying to reach a Twitter endpoint. I'm getting error code 401 which I know means I'm not authenticating my request properly. I've looked at many sites dealing with error code 401 but most of them are about retrieving the tokens which I've already done. I look through the code on several sites and can't see what I'm doing different. I've also been on this site oauth signature checker
and everything checks out fine. So I'm stuck. Any help would be appreciated.
package com.clarkgarrett.testokhttp;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Base64;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.SecureRandom;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import static com.clarkgarrett.testokhttp.Utility.CONSUMER_API_KEY;
import static com.clarkgarrett.testokhttp.Utility.CONSUMER_API_SECRET;
import static com.clarkgarrett.testokhttp.Utility.accessTokenSecretString;
import static com.clarkgarrett.testokhttp.Utility.accessTokenString;
public class NextActivity2 extends AppCompatActivity {
private OkHttpClient client = new OkHttpClient();
//Picasso picasso = new Picasso.Builder(this).downloader(new OkHttp3Downloader(client)).build();
private String mCount = "25";
private long mTimeStamp;
private String mNonce, mSignatureString, mUrlString;
private static final String BASE_URL_STRING = "https://api.twitter.com/1.1/statuses/home_timeline.json";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_next);
OkHttpClient client = new OkHttpClient();
HttpUrl.Builder urlBuilder = HttpUrl.parse(BASE_URL_STRING).newBuilder();
urlBuilder.addQueryParameter("count", mCount);
urlBuilder.addQueryParameter("since_id", "1");
mUrlString = urlBuilder.build().toString();
mNonce = nonceGenerator();
mTimeStamp = System.currentTimeMillis()/1000L;
String signatureBaseString = createSignatureBaseString();
mSignatureString = generateSignature(signatureBaseString, CONSUMER_API_SECRET, accessTokenSecretString);
String header = createHeader();
Request request = new Request.Builder()
.header("Authorization" , header)
.url(mUrlString)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
if (!response.isSuccessful()) {
//Haven't even tried to do anything here yet.
throw new IOException("Unexpected code " + response +" network Responce= " + response.networkResponse());
}else{
}
}
});
}
private String nonceGenerator (){
SecureRandom random = new SecureRandom();
String s;
s = java.util.UUID.randomUUID().toString().replace("-","");
return s.substring(0,32);
}
private String encode(String s){
String es = "";
try {
es = URLEncoder.encode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return es;
}
private String createSignatureBaseString(){
StringBuilder builder = new StringBuilder();
builder.append("&count=");
builder.append(mCount);
builder.append("&oauth_consumer_key=");
builder.append(encode(CONSUMER_API_KEY));
builder.append("&oauth_nonce=");
builder.append(mNonce);
builder.append("&oauth_signature_method=HMAC-SHA1&oauth_timestamp=");
builder.append(mTimeStamp);
builder.append("&oauth_token=");
builder.append(encode(accessTokenString));
builder.append("&oauth_version=");
builder.append(encode("1.0"));
builder.append("&since_id=1");
String intermediateString = encode(builder.toString());
builder = new StringBuilder();
builder.append("GET&");
builder.append(encode(BASE_URL_STRING));
builder.append("&");
builder.append(intermediateString);
return builder.toString();
}
private String createHeader(){
StringBuilder builder = new StringBuilder();
builder.append("OAuth oauth_consumer_key=\"");
builder.append(encode(CONSUMER_API_KEY));
builder.append("\", oauth_nonce=\"");
builder.append(mNonce);
builder.append("\", oauth_signature=\"");
builder.append(encode(mSignatureString));
builder.append("\", oauth_signature_method=\"HMAC-SHA1\", ");
builder.append("oauth_timestamp=\"");
builder.append(mTimeStamp);
builder.append("\", oauth_token=\"");
builder.append(encode(accessTokenString));
builder.append("\", oauth_version=\"");
builder.append(encode("1.0"));
builder.append("\"");
return builder.toString();
}
private String generateSignature(String signatueBaseStr, String oAuthConsumerSecret, String oAuthTokenSecret) {
byte[] byteHMAC = null;
try {
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec spec;
if (null == oAuthTokenSecret) {
String signingKey = encode(oAuthConsumerSecret) + '&';
spec = new SecretKeySpec(signingKey.getBytes(), "HmacSHA1");
} else {
String signingKey = encode(oAuthConsumerSecret) + '&' + encode(oAuthTokenSecret);
spec = new SecretKeySpec(signingKey.getBytes(), "HmacSHA1");
}
mac.init(spec);
byteHMAC = mac.doFinal(signatueBaseStr.getBytes("UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}
return Base64.encodeToString(byteHMAC,Base64.NO_WRAP);
}
}
logcat output:
05-08 16:14:34.694: I/art(14938): Late-enabling -Xcheck:jni
05-08 16:14:34.755: W/System(14938): ClassLoader referenced unknown path: /data/app/com.clarkgarrett.testokhttp-2/lib/arm
05-08 16:14:34.791: I/## My Info ##(14938): OauthActivity accessString= 2651797417-agpNu35sZbs5pI5yjHRibMfCezVjpuVuzzWxhAV
05-08 16:14:34.874: I/WebViewFactory(14938): Loading com.google.android.webview version 50.0.2661.86 (code 266108600)
05-08 16:14:34.947: I/cr_LibraryLoader(14938): Time to load native libraries: 3 ms (timestamps 648-651)
05-08 16:14:34.948: I/cr_LibraryLoader(14938): Expected native library version number "50.0.2661.86", actual native library version number "50.0.2661.86"
05-08 16:14:34.974: V/WebViewChromiumFactoryProvider(14938): Binding Chromium to main looper Looper (main, tid 1) {9910e04}
05-08 16:14:34.974: I/cr_LibraryLoader(14938): Expected native library version number "50.0.2661.86", actual native library version number "50.0.2661.86"
05-08 16:14:34.975: I/chromium(14938): [INFO:library_loader_hooks.cc(143)] Chromium logging enabled: level = 0, default verbosity = 0
05-08 16:14:34.990: I/cr_BrowserStartup(14938): Initializing chromium process, singleProcess=true
05-08 16:14:34.999: E/ApkAssets(14938): Error while loading asset assets/natives_blob_64.bin: java.io.FileNotFoundException: assets/natives_blob_64.bin
05-08 16:14:35.000: E/ApkAssets(14938): Error while loading asset assets/snapshot_blob_64.bin: java.io.FileNotFoundException: assets/snapshot_blob_64.bin
05-08 16:14:35.014: I/Adreno-EGL(14938): <qeglDrvAPI_eglInitialize:379>: QUALCOMM Build: 10/21/15, 369a2ea, I96aee987eb
05-08 16:14:35.068: W/cr_media(14938): Requires BLUETOOTH permission
05-08 16:14:35.107: D/cr_Ime(14938): [InputMethodManagerWrapper.java:30] Constructor
05-08 16:14:35.117: W/cr_AwContents(14938): onDetachedFromWindow called when already detached. Ignoring
05-08 16:14:35.119: D/cr_Ime(14938): [InputMethodManagerWrapper.java:59] isActive: false
05-08 16:14:35.175: D/OpenGLRenderer(14938): Use EGL_SWAP_BEHAVIOR_PRESERVED: true
05-08 16:14:35.232: I/OpenGLRenderer(14938): Initialized EGL, version 1.4
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938): Callback failure for call to https://api.twitter.com/...
**05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938): java.io.IOException: Unexpected code Response{protocol=h2, code=401, message=, url=https://api.twitter.com/1.1/statuses/home_timeline.json?count=25&since_id=1} network Responce= Response{protocol=h2, code=401, message=, url=https://api.twitter.com/1.1/statuses/home_timeline.json?count=25&since_id=1}**
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938): at com.clarkgarrett.testokhttp.NextActivity2$1.onResponse(NextActivity2.java:71)
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938): at okhttp3.RealCall$AsyncCall.execute(RealCall.java:133)
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938): at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938): at java.lang.Thread.run(Thread.java:818)
05-08 16:14:35.502: I/cr_Ime(14938): ImeThread is not enabled.
05-08 16:14:35.568: E/libEGL(14938): validate_display:255 error 3008 (EGL_BAD_DISPLAY)
05-08 16:14:36.133: W/cr_BindingManager(14938): Cannot call determinedVisibility() - never saw a connection for the pid: 14938
05-08 16:14:36.135: D/cr_Ime(14938): [InputMethodManagerWrapper.java:59] isActive: false
05-08 16:14:36.155: I/chromium(14938): [INFO:CONSOLE(7)] "The value "device-width;" for key "width" is invalid, and has been ignored.", source: https://api.twitter.com/oauth/authorize?oauth_token=zLafmwAAAAAAu_IxAAABVJKmrbA (7)
05-08 16:14:36.155: I/chromium(14938): [INFO:CONSOLE(7)] "The value "1.0;" for key "initial-scale" was truncated to its numeric prefix.", source: https://api.twitter.com/oauth/authorize?oauth_token=zLafmwAAAAAAu_IxAAABVJKmrbA (7)
05-08 16:14:36.155: I/chromium(14938): [INFO:CONSOLE(7)] "The value "1.0;" for key "maximum-scale" was truncated to its numeric prefix.", source: https://api.twitter.com/oauth/authorize?oauth_token=zLafmwAAAAAAu_IxAAABVJKmrbA (7)
05-08 16:14:36.155: I/chromium(14938): [INFO:CONSOLE(7)] "Error parsing a meta element's content: ';' is not a valid key-value pair separator. Please use ',' instead.", source: https://api.twitter.com/oauth/authorize?oauth_token=zLafmwAAAAAAu_IxAAABVJKmrbA (7)
05-08 16:14:42.132: I/art(14938): Debugger is no longer active
Upvotes: 0
Views: 2225
Reputation: 79
I found two errors. First the line
builder.append("&count=");
in the createSignatureBaseString() method shouldn't have the &. It should read
builder.append("count=");
This was a cut and paste error. Second, the method generateSignature() needs to return a UTF-8 string. The return statement
return Base64.encodeToString(byteHMAC,Base64.NO_WRAP);
Has been modified as follows:
String s ="";
try {
s = new String(Base64.encode(byteHMAC, Base64.NO_WRAP), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
};
return s;
Now I'm am retrieving json data from Twitter.
Upvotes: 1