Closed albertodebortoli closed 13 years ago
I just pulled down the latest and tried assigning a call-back URL to my app. It had no problems parsing. Does the demo app work for you?
The demo app is not working with any Twitter app with a callback url set.
A friend of mine has just (maybe correctly) fixed the bug. There is no need for out-of-band/PIN authentication step in the flow in his opinion. I've replace the entire else branch in webViewDidFinishLoad
in SA_OAuthTwitterEngine.m
with this:
[_engine requestAccessToken];
if ([_delegate respondsToSelector: @selector(OAuthTwitterController:authenticatedWithUsername:)])
[_delegate OAuthTwitterController: self authenticatedWithUsername: _engine.username];
[self performSelector: @selector(dismissModalViewControllerAnimated:) withObject: (id) kCFBooleanTrue afterDelay: 1.0];
or better
[self dismissModalViewControllerAnimated:YES];
since there is no reason to stare at the callback url page for 1 second.
No gotPin
method needs to be called.
If you remove the callback, does it work? I can't this problem to repro here.
Works like a charm to me now with and without callback URL set (in the application on http://dev.twitter.com).
I get the access_token
, the refresh_token
, the token_type
and the expires_in
keys in a JSON dictionary.
So I suppose the OAuth flow is quite done also without the OOB step, maybe weird...
I'm also having this issue, when the twitter app have a callback url set, login flow gets stuck at the "Select and Copy the PIN" page, this is on the iPhone 4.3 Simulator. When callback url is removed from the twitter app settings, login proceeds without any issue.
Yes, I think the callback URL confuses the issue, since it's trying to call out to an app.
I tested with just a default website set on twitter, no oauth callback URL supplied. On Sep 28, 2011 6:41 PM, "Ben Gottlieb" < reply@reply.github.com> wrote:
Yes, I think the callback URL confuses the issue, since it's trying to call out to an app.
Reply to this email directly or view it on GitHub:
https://github.com/bengottlieb/Twitter-OAuth-iPhone/issues/79#issuecomment-2233106
Other libraries like ShareKit allow you to specify the callback URL along with the key and secret, and it works perfectly.
ShareKit is dead. Last commit on November 23, 2010. Actually Twitter-OAuth-iPhone is the best choice out there.
Having the same issue. The page redirects to the callback URL and the authenticatedWithUsername: method never gets called in the delegate. I replaced the else code in webViewDidFinishLoad in SA_OAuthTwitterViewController (not in SA_OAuthTwitterEngine) like @albertodebortoli said and its working fine for me. Has this worked well for you @albertodebortoli so far?
My patch worked for me, but I'm not so confident with OAuth to be sure that is correct.
+1 on alertodebortoll and difficultashish's solution:
This is an essential fix if you want to have other clients (who use callback urls as intended) to authenticate against the same Twitter App as your iOS app using Twitter-OAuth-iPhone
Here's what the entire method looks like for me now in SA_OAuthTwitterViewController.m (for anyone who wants to cut and paste a whole method)
- (void) webViewDidFinishLoad: (UIWebView *) webView {
_loading = NO;
//[self performInjection];
if (_firstLoad) {
[_webView performSelector: @selector(stringByEvaluatingJavaScriptFromString:) withObject: @"window.scrollBy(0,200)" afterDelay: 0];
_firstLoad = NO;
} else {
// This else clause modified to work with twitter apps that have the callback URL set: https://dev.twitter.com/apps/
// Bug details: https://github.com/bengottlieb/Twitter-OAuth-iPhone/issues/79
[_engine requestAccessToken];
if ([_delegate respondsToSelector: @selector(OAuthTwitterController:authenticatedWithUsername:)])
[_delegate OAuthTwitterController: self authenticatedWithUsername: _engine.username];
[self dismissModalViewControllerAnimated:YES];
}
[UIView beginAnimations: nil context: nil];
_blockerView.alpha = 0.0;
[UIView commitAnimations];
if ([_webView isLoading]) {
_webView.alpha = 0.0;
} else {
_webView.alpha = 1.0;
}
}
@sek Working Seamlessly, Thank you so much. BTW, when we enter wrong credentials, it dismisses the login view. Do you have any fix for this? Thanks!
Hi everyone, I used the patch above in my code and my app run well with callback url is set for a long time. However, several days ago, I cannot login Twitter from my app. The patch seems to be inapplicable anymore. Does anyone know how to fix it?
Hey Guys , its stop working for me also. Anyone knows how to fix it. Please reply early. My live app on appstore stop working.
@Alberto De Bortoli have any fix for this problem again. @bengottlieb Can you fix this probelm.
Finally, I found the solution:
@lenhhoxung86 its not working can you please share sample code or some more details. Even if the repo you refer is not working with my api key and secret key with call back url.
(BOOL) webView: (UIWebView ) webView shouldStartLoadWithRequest: (NSURLRequest ) request navigationType: (UIWebViewNavigationType) navigationType {
//Added for url callback NSURL *requestURL = [request URL];
if ([[requestURL host] isEqualToString:Twitter_OAuth_CallbackURL]) { [_engine performSelector:@selector(requestAccessToken:) withObject:[requestURL absoluteString] afterDelay:0.1]; //Dismiss modal view controller here [self performSelector: @selector(dismissModalViewControllerAnimated:) withObject: (id) kCFBooleanTrue afterDelay: 1.0]; return NO; }
NSData data = [request HTTPBody]; char raw = data ? (char *) [data bytes] : "";
if (raw && strstr(raw, "cancel=")) { [self denied]; return NO; } if (navigationType != UIWebViewNavigationTypeOther) _webView.alpha = 0.1; return YES; }
(void) requestURL: (NSURL ) url token: (OAToken ) token onSuccess: (SEL) success onFail: (SEL) fail withCallback:(NSString )callback { OAMutableURLRequestOverride request = [[[OAMutableURLRequestOverride alloc] initWithURL: url consumer: self.consumer token:token realm:nil signatureProvider: nil] autorelease]; if (!request) return;
if (self.pin.length) token.pin = self.pin; [request setHTTPMethod: @"POST"];
NSArray parts = [callback componentsSeparatedByString:@"?"]; NSArray pairs = [[parts objectAtIndex:1] componentsSeparatedByString:@"&"]; for (NSString pair in pairs) { NSArray elements = [pair componentsSeparatedByString:@"="]; [request setOAuthParameterName:[elements objectAtIndex:0] withValue:[elements objectAtIndex:1]]; }
OADataFetcher *fetcher = [[[OADataFetcher alloc] init] autorelease]; [fetcher fetchDataWithRequest: request delegate: self didFinishSelector: success didFailSelector: fail]; }
@interface OAMutableURLRequestOverride : OAMutableURLRequest { NSMutableDictionary *extraOAuthParameters;//Added for additional parameter } //Override super method
File OAMutableURLRequestOverride.m: // // OAMutableURLRequestOverride.m // //
@implementation OAMutableURLRequestOverride
(void)setOAuthParameterName:(NSString)parameterName withValue:(NSString)parameterValue { assert(parameterName && parameterValue);
if (extraOAuthParameters == nil) { extraOAuthParameters = [NSMutableDictionary new]; }
[extraOAuthParameters setObject:parameterValue forKey:parameterName]; }
(void)prepare { // sign // Secrets must be urlencoded before concatenated with '&' // TODO: if later RSA-SHA1 support is added then a little code redesign is needed signature = [signatureProvider signClearText:[self _signatureBaseString] withSecret:[NSString stringWithFormat:@"%@&%@", [consumer.secret URLEncodedString], [token.secret URLEncodedString]]];
// set OAuth headers NSString *oauthToken; if ([token.key isEqualToString:@""]) oauthToken = @""; // not used on Request Token transactions else oauthToken = [NSString stringWithFormat:@"oauth_token=\"%@\", ", [token.key URLEncodedString]];
NSMutableString *extraParameters = [NSMutableString string];
// Adding the optional parameters in sorted order isn't required by the OAuth spec, but it makes it possible to hard-code expected values in the unit tests. for(NSString *parameterName in [[extraOAuthParameters allKeys] sortedArrayUsingSelector:@selector(compare:)]) { [extraParameters appendFormat:@", %@=\"%@\"", [parameterName URLEncodedString], [[extraOAuthParameters objectForKey:parameterName] URLEncodedString]]; }
NSString *oauthHeader = [NSString stringWithFormat:@"OAuth realm=\"%@\", oauth_consumer_key=\"%@\", %@oauth_signature_method=\"%@\", oauth_signature=\"%@\", oauth_timestamp=\"%@\", oauth_nonce=\"%@\", oauth_version=\"1.0\"%@", [realm URLEncodedString], [consumer.key URLEncodedString], oauthToken, [[signatureProvider name] URLEncodedString], [signature URLEncodedString], timestamp, nonce, extraParameters];
[self setValue:oauthHeader forHTTPHeaderField:@"Authorization"]; }
(NSString )_signatureBaseString { // OAuth Spec, Section 9.1.1 "Normalize Request Parameters" // build a sorted array of both request parameters and OAuth header parameters NSMutableArray parameterPairs = [NSMutableArray arrayWithCapacity:(7 + [[self parameters] count])]; // 6 being the number of OAuth params in the Signature Base String
[parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_consumer_key" value:consumer.key] URLEncodedNameValuePair]]; [parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_signature_method" value:[signatureProvider name]] URLEncodedNameValuePair]]; [parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_timestamp" value:timestamp] URLEncodedNameValuePair]]; [parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_nonce" value:nonce] URLEncodedNameValuePair]]; [parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_version" value:@"1.0"] URLEncodedNameValuePair]];
if (![token.key isEqualToString:@""]) { [parameterPairs addObject:[[OARequestParameter requestParameterWithName:@"oauth_token" value:token.key] URLEncodedNameValuePair]]; }
for (OARequestParameter *param in [self parameters]) { [parameterPairs addObject:[param URLEncodedNameValuePair]]; }
NSArray sortedPairs = [parameterPairs sortedArrayUsingSelector:@selector(compare:)]; NSString normalizedRequestParameters = [sortedPairs componentsJoinedByString:@"&"];
// OAuth Spec, Section 9.1.2 "Concatenate Request Elements" NSString *ret = [NSString stringWithFormat:@"%@&%@&%@", [self HTTPMethod], [[[self URL] URLStringWithoutQuery] URLEncodedString], [normalizedRequestParameters URLEncodedString]];
return ret; }
If the Twitter application has a callback URL set, the authorization process got stuck in the view with the title "Select and Copy the PIN" and nothing can be done to complete the authentication. The view is blank.
I think the problem regards the modification of the method "locateAuthPinInWebView" in the last commit "New Javascript code for parsing for PIN results, from olamn2k." because it can't get the PIN.
Can you solve this please?