Possible Duplicate:
Can't get Twitter OAuth callback authentication to work in Cocoa application
I have an application that sends Twitter updates (tweets). The current version does OAuth authentication using the OOB process:
(1) Send request token request:
consumer = [[OAConsumer alloc] initWithKey: kOAuthConsumerKey secret:kOAuthConsumerSecret];
OAMutable开发者_运维知识库URLRequest *request = [[[OAMutableURLRequest alloc]
initWithURL: [NSURL URLWithString:kOAuthTwitterRequestTokenURL]
consumer: self.consumer
token: nil realm: nil signatureProvider: nil] autorelease];
[request setHTTPMethod:@"POST"];
OADataFetcher *fetcher = [[[OADataFetcher alloc] init] autorelease];
[fetcher fetchDataWithRequest:request delegate:self didFinishSelector:@selector(setRequestToken:withData:) didFailSelector:@selector(failRequestToken:data:)];
(2) Then assemble the authentication URL and open a browser window:
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
self.requestToken = [[[OAToken alloc] initWithHTTPResponseBody:dataString] autorelease];
NSString *urlString = [NSString stringWithFormat: @"%@?oauth_token=%@", kOAuthTwitterAuthorizeURL, self.requestToken.key];
NSURL *requestURL = [NSURL URLWithString: urlString];
[[NSWorkspace sharedWorkspace] openURL: requestURL];
(3) Pop up an Alert Window with a text field, and get the PIN number from the user, then:
self.requestToken.secret = [input stringValue];
OAMutableURLRequest *request = [[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:kOAuthTwitterAccessTokenURL] consumer: self.consumer token: self.requestToken realm: nil signatureProvider: nil];
[request setHTTPMethod:@"POST"];
OADataFetcher *fetcher = [[[OADataFetcher alloc] init] autorelease];
[fetcher fetchDataWithRequest:request delegate:self didFinishSelector:@selector(setAccessToken:withData:) didFailSelector:@selector(failAccessToken:data:)];
(4) Save the return credentials:
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
accessToken = [[OAToken alloc] initWithHTTPResponseBody:dataString];
[accessToken storeInUserDefaultsWithServiceProviderName: kOAuthTwitterDefaultsDomain prefix: kOAuthTwitterDefaultsPrefix];
(Memory Management and error checking removed for brevity)
This all works fine, but I really hate the process of redirecting to a browser and then making the user copy and paste the PIN number. I wanted to stop using the OOB method and go to the callback method. That takes some finagling for a Desktop application. So I switched to using a WebView, and I do steps 1 and 2 the same, except 2 does this instead:
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
self.requestToken = [[[OAToken alloc] initWithHTTPResponseBody:dataString] autorelease];
OAMutableURLRequest* requestURL = [[[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:kOAuthTwitterAuthorizeURL] consumer:nil token:self.requestToken realm:nil signatureProvider:nil] autorelease];
[requestURL setParameters:[NSArray arrayWithObject:[[[OARequestParameter alloc] initWithName:@"oauth_token" value: self.requestToken.key] autorelease]]];
[[webview mainFrame] loadRequest: requestURL];
[NSApp beginSheet: webSheet modalForWindow: [NSApp mainWindow] modalDelegate:self didEndSelector: @selector(webSheetDidEnd:returnCode:contextInfo:) contextInfo:nil];
I use the WebView PolicyDelegate to look for the redirect (to my callback URL) after the user has authorized using the Twitter webpage:
- (void)webView: (WebView *)webView decidePolicyForNavigationAction: (NSDictionary *)actionInformation request: (NSURLRequest *)request frame: (WebFrame *)frame
decisionListener: (id<WebPolicyDecisionListener>)listener
{
NSString *urlString = [[actionInformation objectForKey:@"WebActionOriginalURLKey"] absoluteString];
if ([urlString rangeOfString:@"oauth_verifier"].location != NSNotFound)
{
// parse out oauth_token and oauth_verifier from the URL
}
self.requestToken.secret = oauthVerifier;
[listener ignore];
[NSApp endSheet:webSheet returnCode: 0];
OAMutableURLRequest *request = [[[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:kOAuthTwitterAccessTokenURL] consumer: self.consumer token: self.requestToken realm: nil signatureProvider: nil] autorelease];
[request setHTTPMethod:@"POST"];
OADataFetcher *fetcher = [[[OADataFetcher alloc] init] autorelease];
[fetcher fetchDataWithRequest:request delegate:self didFinishSelector:@selector(setAccessToken:withData:) didFailSelector:@selector(failAccessToken:data:)];
}
As far as I can tell, this should authenticate exactly the same. The only difference, as far as Twitter is concerned, is that I'm using the oauth_verifier from the callback URL instead of the one presented to the user (and provided back to my application). But if this was a true server-to-server implementation, that's the value I'd be using anyway.
What actually happens is that this last step (getting the access token) fails with:
failAccessToken: 'Error Domain=NSURLErrorDomain Code=-1012 "The operation couldn’t be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x101b3e780 {NSErrorFailingURLStringKey=http://api.twitter.com/oauth/access_token, NSUnderlyingError=0x101bacf40 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1012.)", NSErrorFailingURLKey=http://api.twitter.com/oauth/access_token}'
Any ideas on what's causing this authentication to fail?
joe
I finally gave up on the OAuthConsumer framework and switched to the Google GTMOAuth framework. That works fine.
精彩评论