LinkedIn scribe OAuth library unable to allow access to LinkedIn account
I'm writing a proof-of-concept app to connect to LinkedIn using the scribe OAuth library.
I can get a request Token, but am unable to exchange that for an access token. I am repeatedly receiving the oauth_problem=permission_unknown
response, despite clicking the 'allow' on LinkedIn that I am redirected to when I am asked to authenticate.
I've registered a callback so that when LinkedIn redirects the user to my callback, my authentication activity is resumed and in onResume I extract the request token and PIN code to use to get an access token and this part at least seems to be working as intended.
My code is here:
package com.rockrobot.example.scribelinkedin;
import org.scribe.builder.ServiceBuilder;
import org.scribe.builder.api.LinkedInApi;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verb;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuthService;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Toast;
public class loginexample extends Activity {
private static OAuthService service;
private Token requestToken;
private Verifier verifier;
private static final String PROTECTED_RESOURCE_URL = "http://api.linkedin.com/v1/people/~/connections:(id,last-name)";
private static Boolean authInProgress = false;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if ( ! authInProgress ) {
authInProgress = true;
service = new ServiceBuilder()
.provider(LinkedInApi.class)
.apiKey("XXXXX")
.apiSecret("YYYYY")
.callback("callback://whodunit")
.build();
System.out.println("=== LinkedIn's OAuth Workflow ===");
System.out.println();
// Obtain the Request Token
System.out.println("Fetching the Request Token...");
requestToken = service.getRequestToken();
System.out.println("Got the Request Token!");
System.out.println();
System.out.println("Now go and authorize Scribe here:");
System.out.println(service.getAuthorizationUrl(requestToken));
Toast.makeText(this, "Please authorize " + getString(R.string.app_name), Toast.LENGTH_LONG).show();
startActivity (new Intent ( Intent.ACTION_VIEW, Uri.parse(service.getAuthorizationUrl(requestToken))));
}
}
@Override
protected void onResume() {
super.onResume();
if (this.getIntent()!=null && this.getIntent().getData()!=null){
Uri uri = this.getIntent().getData();
if (uri != null && uri.toString().startsWith("callback://whodunit")) {
verifier = new Verifier ( uri.getQueryParameter("oauth_verifier") );
// Trade the Request Token and Verfier for the Access Token
requestToken = service.getRequestToken();
System.out.println("Trading the Request Token for an Access Token...");
System.out.println(" ---> Request Token: " + requestToken.getToken());
System.out.println(" ---> Request Token Secret: " + requestToken.getSecret());
System.out.println(" ---> Verifier: " + verifier.getValue());
Token accessToken = service.getAccessToken(requestToken, verifier);
System.out.println("Got the Access Token!");
System.out.println("(if you're curious it looks like this: " + accessToken + " )");
System.out.println();
// Now let's go and ask for a protected resource!
System.out.println("Now we're going to access a protected resource...");
OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL);
service.signRequest(accessToken, request);
Response response = request.send();
System.out.println("Got it! Lets see what we found...");
System.out.println();
System.out.println(response.getBody());
System.out.println();
System.out.println("Thats it man! Go and build something awesome with Scribe! :)");
}
}
}
}
The relevant excerpt from my Manifest is here:
<activity android:name=".loginexample"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.BROWSABLE"></category>
<category android:name="android.intent.category.DEFAULT"></category>
<data android:host="whodunit" android:scheme="callback"></data>
</intent-filter>
</activity>
Finally, my logcat is here:
07-05 23:39:58.204: INFO/System.out(300): === LinkedIn's OAuth Workflow ===
07-05 23:39:58.204: INFO/System.out(300): Fetching the Request Token...
07-05 23:39:59.284: INFO/System.out(300): Body = oauth_token=88d888e9-f05b-4b3e-ae64-97c4878cbf06&oauth_token_secret=d5685691-0ff7-4c60-954c-62b9cf16028b&oauth_callback_confirmed=true&xoauth_request_auth_url=https%3A%2F%2Fapi.linkedin.com%2Fuas%2Foauth%2Fauthorize&oauth_expires_in=599
07-05 23:39:59.284: INFO/System.out(300): Got the Request Token!
07-05 23:39:59.284: INFO/System.out(300): Now go and authorize Scribe here:
07-05 23:39:59.294: INFO/System.out(300): https://api.linkedin.com/uas/oauth/authorize?oauth_token=88d888e9-f05b-4b3e-ae64-97c4878cbf06
07-05 23:39:59.354: INFO/ActivityManager(59): Starting activity: Intent { act=android.intent.action.VIEW dat=https://api.linkedin.com/uas/oauth/authorize?oauth_token=88d888e9-f05b-4b3e-ae64-97c4878cbf06 cmp=com.android.browser/.BrowserActivity }
07-05 23:39:59.484: INFO/ActivityManager(59): Start proc com.android.browser for activity com.android.browser/.BrowserActivity: pid=306 uid=10019 gids={3003, 1015}
07-05 23:39:59.964: INFO/ActivityManager(59): Displayed activity com.rockrobot.example.scribelinkedin/.loginexample: 3404 ms (total 3404 ms)
07-05 23:40:00.894: INFO/ActivityThread(306): Publishing provider browser: com.android.browser.BrowserProvider
07-05 23:40:03.914: INFO/ActivityManager(59): Displayed activity com.android.browser/.BrowserActivity: 4440 ms (total 4440 ms)
07-05 23:40:05.744: INFO/ActivityManager(59): Starting activity: Intent { act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=https://www.linkedin.com/uas/oauth/authorize?oauth_token=88d888e9-f05b-4b3e-ae64-97c4878cbf06 cmp=com.android.browser/.BrowserActivity }
07-05 23:40:14.414: INFO/ActivityManager(59): Process com.android.email (pid 245) has died.
07-05 23:40:19.294: INFO/ActivityManager(59): Starting activity: Intent { act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=callback://whodunit?oauth_token=88d888e9-f05b-4b3e-ae64-97c4878cbf06&oauth_verifier=41304 cmp=com.rockrobot.example.scribelinkedin/.loginexample }
07-05 23:40:20.564: INFO/System.out(300): Body = oauth_token=5362f57e-d5b5-4007-a166-d02d21e80a4d&oauth_token_secret=b4d263ec-f29f-4e8b-876b-5c7a6280900开发者_JS百科f&oauth_callback_confirmed=true&xoauth_request_auth_url=https%3A%2F%2Fapi.linkedin.com%2Fuas%2Foauth%2Fauthorize&oauth_expires_in=599
07-05 23:40:20.564: INFO/System.out(300): Trading the Request Token for an Access Token...
07-05 23:40:20.564: INFO/System.out(300): ---> Request Token: 5362f57e-d5b5-4007-a166-d02d21e80a4d
07-05 23:40:20.564: INFO/System.out(300): ---> Request Token Secret: b4d263ec-f29f-4e8b-876b-5c7a6280900f
07-05 23:40:20.564: INFO/System.out(300): ---> Verifier: 41304
07-05 23:40:21.464: INFO/System.out(300): Body = oauth_problem=permission_unknown
07-05 23:40:21.514: ERROR/AndroidRuntime(300): FATAL EXCEPTION: main
07-05 23:40:21.514: ERROR/AndroidRuntime(300): java.lang.RuntimeException: Unable to resume activity {com.rockrobot.example.scribelinkedin/com.rockrobot.example.scribelinkedin.loginexample}: org.scribe.exceptions.OAuthException: Response body is incorrect. Can't extract token and secret from this: 'oauth_problem=permission_unknown'
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3128)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3143)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2684)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at android.app.ActivityThread.access$2300(ActivityThread.java:125)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at android.os.Handler.dispatchMessage(Handler.java:99)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at android.os.Looper.loop(Looper.java:123)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at android.app.ActivityThread.main(ActivityThread.java:4627)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at java.lang.reflect.Method.invokeNative(Native Method)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at java.lang.reflect.Method.invoke(Method.java:521)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at dalvik.system.NativeStart.main(Native Method)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): Caused by: org.scribe.exceptions.OAuthException: Response body is incorrect. Can't extract token and secret from this: 'oauth_problem=permission_unknown'
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at org.scribe.extractors.TokenExtractorImpl.extract(TokenExtractorImpl.java:42)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at org.scribe.extractors.TokenExtractorImpl.extract(TokenExtractorImpl.java:27)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at org.scribe.oauth.OAuth10aServiceImpl.getAccessToken(OAuth10aServiceImpl.java:67)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at com.rockrobot.example.scribelinkedin.loginexample.onResume(loginexample.java:78)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1149)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at android.app.Activity.performResume(Activity.java:3823)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3118)
07-05 23:40:21.514: ERROR/AndroidRuntime(300): ... 12 more
07-05 23:40:21.684: WARN/ActivityManager(59): Force finishing activity com.rockrobot.example.scribelinkedin/.loginexample
07-05 23:40:21.684: WARN/ActivityManager(59): Force finishing activity com.android.browser/.BrowserActivity
07-05 23:40:22.454: WARN/ActivityManager(59): Activity pause timeout for HistoryRecord{44f81be8 com.rockrobot.example.scribelinkedin/.loginexample}
07-05 23:40:24.675: INFO/ActivityManager(59): Process com.rockrobot.example.scribelinkedin (pid 300) has died.
07-05 23:40:24.694: INFO/WindowManager(59): WIN DEATH: Window{450717b8 com.rockrobot.example.scribelinkedin/com.rockrobot.example.scribelinkedin.loginexample paused=false}
07-05 23:40:24.914: WARN/InputManagerService(59): Starting input on non-focused client com.android.internal.view.IInputMethodClient$Stub$Proxy@4509ce80 (uid=10019 pid=306)
As you can see in the logcat, I'm getting an oauth token and token_secret, but I am unable to exchange these for the access token but keep getting the oauth_problem=permission_unknown
error.
I know LinkedIn claim to be rather strict with their implementation of OAuth, I just can't seem to see where I'm going wrong - maybe I've been staring at it for too long already, which is where you guys come in.
Does anybody have any ideas? OAuth isn't exactly simple, but I can't believe it's this complicated either. I'm probably missing something obvious, but just can't work out what it is.
In your onResume
method you're getting a new request token but, the verification code is from the request token you got in the onCreate
method. So, you're trying to get an access token using the second request token but the verification code corresponds to the first request token - hence, you're not issued an access token. Make sense?
If you take out requestToken = service.getRequestToken();
from the onResume method then this should work as everything else looks correct.
精彩评论