Issue for Twitter applications with callback URL set #79

Closed
albertodebortoli opened this Issue Jul 24, 2011 · 19 comments

Comments

Projects
None yet
10 participants
@albertodebortoli

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?

@bengottlieb

This comment has been minimized.

Show comment
Hide comment
@bengottlieb

bengottlieb Jul 24, 2011

Owner

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?

Owner

bengottlieb commented Jul 24, 2011

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?

@albertodebortoli

This comment has been minimized.

Show comment
Hide comment
@albertodebortoli

albertodebortoli Jul 24, 2011

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.

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.

@bengottlieb

This comment has been minimized.

Show comment
Hide comment
@bengottlieb

bengottlieb Jul 24, 2011

Owner

If you remove the callback, does it work? I can't this problem to repro here.

Owner

bengottlieb commented Jul 24, 2011

If you remove the callback, does it work? I can't this problem to repro here.

@albertodebortoli

This comment has been minimized.

Show comment
Hide comment
@albertodebortoli

albertodebortoli Jul 24, 2011

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...

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...

@dvdh

This comment has been minimized.

Show comment
Hide comment
@dvdh

dvdh Sep 29, 2011

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.

dvdh commented Sep 29, 2011

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.

@bengottlieb

This comment has been minimized.

Show comment
Hide comment
@bengottlieb

bengottlieb Sep 29, 2011

Owner

Yes, I think the callback URL confuses the issue, since it's trying to call out to an app.

Owner

bengottlieb commented Sep 29, 2011

Yes, I think the callback URL confuses the issue, since it's trying to call out to an app.

@dvdh

This comment has been minimized.

Show comment
Hide comment
@dvdh

dvdh Sep 29, 2011

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:

#79 (comment)

dvdh commented Sep 29, 2011

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:

#79 (comment)

@newacct

This comment has been minimized.

Show comment
Hide comment
@newacct

newacct Oct 20, 2011

Other libraries like ShareKit allow you to specify the callback URL along with the key and secret, and it works perfectly.

newacct commented Oct 20, 2011

Other libraries like ShareKit allow you to specify the callback URL along with the key and secret, and it works perfectly.

@albertodebortoli

This comment has been minimized.

Show comment
Hide comment
@albertodebortoli

albertodebortoli Nov 9, 2011

ShareKit is dead. Last commit on November 23, 2010.
Actually Twitter-OAuth-iPhone is the best choice out there.

ShareKit is dead. Last commit on November 23, 2010.
Actually Twitter-OAuth-iPhone is the best choice out there.

@difficultashish

This comment has been minimized.

Show comment
Hide comment
@difficultashish

difficultashish Mar 20, 2012

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?

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?

@albertodebortoli

This comment has been minimized.

Show comment
Hide comment
@albertodebortoli

albertodebortoli Apr 1, 2012

My patch worked for me, but I'm not so confident with OAuth to be sure that is correct.

My patch worked for me, but I'm not so confident with OAuth to be sure that is correct.

@sek

This comment has been minimized.

Show comment
Hide comment
@sek

sek Apr 27, 2012

+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 commented Apr 27, 2012

+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;
    }
}
@onederrsiva

This comment has been minimized.

Show comment
Hide comment
@onederrsiva

onederrsiva Aug 25, 2012

@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!

@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!

@lenhhoxung86

This comment has been minimized.

Show comment
Hide comment
@lenhhoxung86

lenhhoxung86 May 1, 2013

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?

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?

@riyazgit

This comment has been minimized.

Show comment
Hide comment
@riyazgit

riyazgit May 1, 2013

Hey Guys , its stop working for me also. Anyone knows how to fix it. Please reply early. My live app on appstore stop working.

riyazgit commented May 1, 2013

Hey Guys , its stop working for me also. Anyone knows how to fix it. Please reply early. My live app on appstore stop working.

@riyazahemad

This comment has been minimized.

Show comment
Hide comment
@riyazahemad

riyazahemad May 2, 2013

@alberto De Bortoli have any fix for this problem again. @bengottlieb Can you fix this probelm.

@alberto De Bortoli have any fix for this problem again. @bengottlieb Can you fix this probelm.

@lenhhoxung86

This comment has been minimized.

Show comment
Hide comment
@lenhhoxung86

lenhhoxung86 May 3, 2013

Finally, I found the solution:

  • 1st step: Check this repo: https://github.com/adriaant/OAuthWithCallback
  • 2nd: Modify method: - (BOOL) webView: shouldStartLoadWithRequest:navigationType: as in the above source
  • 3rd: Override class OAMutableURLRequest with methods and variable:
    @interface OAMutableURLRequestOverride : OAMutableURLRequest
    {
    NSMutableDictionary *extraOAuthParameters;//Added for additional parameter
    }
    //Override super method
  • (void)prepare;
    //Override super method
  • (void)setOAuthParameterName:(NSString_)parameterName withValue:(NSString_)parameterValue;
    @EnD
    Hope this help.

Finally, I found the solution:

  • 1st step: Check this repo: https://github.com/adriaant/OAuthWithCallback
  • 2nd: Modify method: - (BOOL) webView: shouldStartLoadWithRequest:navigationType: as in the above source
  • 3rd: Override class OAMutableURLRequest with methods and variable:
    @interface OAMutableURLRequestOverride : OAMutableURLRequest
    {
    NSMutableDictionary *extraOAuthParameters;//Added for additional parameter
    }
    //Override super method
  • (void)prepare;
    //Override super method
  • (void)setOAuthParameterName:(NSString_)parameterName withValue:(NSString_)parameterValue;
    @EnD
    Hope this help.
@riyazahemad

This comment has been minimized.

Show comment
Hide comment
@riyazahemad

riyazahemad May 6, 2013

@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.

@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.

@lenhhoxung86

This comment has been minimized.

Show comment
Hide comment
@lenhhoxung86

lenhhoxung86 May 6, 2013

  • Your callback URL should be valid, and may be you forgot to replace the const string (callback url) in the source of above repo.

  • In SA_OAuthTwitterController.m (Twitter_OAuth_CallbackURL is your callback URL):

  • Remove Else Branch in - (void) webViewDidFinishLoad: (UIWebView *) webView;

  • (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;
    }

  • In SA_OAuthTwitterEngine.m:
    //Added for callback url

  • (void) requestAccessToken:(NSString *)callback {
    [self requestURL: self.accessTokenURL token: _requestToken onSuccess: @selector(setAccessToken:withData:) onFail: @selector(outhTicketFailed:data:) withCallback:callback];
    }

  • (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];
    }

  • I also override class OAMutableURLRequest:
    File OAMutableURLRequestOverride.h
    #import "OAMutableURLRequest.h"

@interface OAMutableURLRequestOverride : OAMutableURLRequest
{
NSMutableDictionary *extraOAuthParameters;//Added for additional parameter
}
//Override super method

  • (void)prepare;
    //Override super method
  • (void)setOAuthParameterName:(NSString_)parameterName withValue:(NSString_)parameterValue;
    @EnD

File OAMutableURLRequestOverride.m:
//
// OAMutableURLRequestOverride.m
//
//

#import "OAMutableURLRequestOverride.h"
#import "NSURL+Base.h"

@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;
    }

  • (void)dealloc {
    [extraOAuthParameters release];
    [super dealloc];
    }
    @EnD

  • And some files in OAuthConsumer (NSURL+Base.h e.g) will need to replace by copying corresponding files from https://github.com/adriaant/OAuthWithCallback

  • The key thing is that we added parameters for OAMutableURLRequest as described in post:
    https://dev.twitter.com/docs/api/1/post/oauth/access_token

  • Your callback URL should be valid, and may be you forgot to replace the const string (callback url) in the source of above repo.

  • In SA_OAuthTwitterController.m (Twitter_OAuth_CallbackURL is your callback URL):

  • Remove Else Branch in - (void) webViewDidFinishLoad: (UIWebView *) webView;

  • (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;
    }

  • In SA_OAuthTwitterEngine.m:
    //Added for callback url

  • (void) requestAccessToken:(NSString *)callback {
    [self requestURL: self.accessTokenURL token: _requestToken onSuccess: @selector(setAccessToken:withData:) onFail: @selector(outhTicketFailed:data:) withCallback:callback];
    }

  • (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];
    }

  • I also override class OAMutableURLRequest:
    File OAMutableURLRequestOverride.h
    #import "OAMutableURLRequest.h"

@interface OAMutableURLRequestOverride : OAMutableURLRequest
{
NSMutableDictionary *extraOAuthParameters;//Added for additional parameter
}
//Override super method

  • (void)prepare;
    //Override super method
  • (void)setOAuthParameterName:(NSString_)parameterName withValue:(NSString_)parameterValue;
    @EnD

File OAMutableURLRequestOverride.m:
//
// OAMutableURLRequestOverride.m
//
//

#import "OAMutableURLRequestOverride.h"
#import "NSURL+Base.h"

@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;
    }

  • (void)dealloc {
    [extraOAuthParameters release];
    [super dealloc];
    }
    @EnD

  • And some files in OAuthConsumer (NSURL+Base.h e.g) will need to replace by copying corresponding files from https://github.com/adriaant/OAuthWithCallback

  • The key thing is that we added parameters for OAMutableURLRequest as described in post:
    https://dev.twitter.com/docs/api/1/post/oauth/access_token

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment