Created
May 29, 2010 11:34
-
-
Save veritech/418233 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// FROAuthRequest.m | |
// | |
// Created by Jonathan Dalrymple on 12/04/2010. | |
// Copyright 2010 Float:Right. All rights reserved. | |
// | |
#import "FROAuthRequest.h" | |
@interface FROAuthRequest(private) | |
- (void) prepare; | |
- (NSString *)signatureBaseString; | |
- (NSString*) signatureBaseStringForURL:(NSURL*) pURL | |
withMethod:(NSString*) method | |
withParams:(NSDictionary*) dictionary; | |
- (NSString *)timestamp; | |
- (NSString *)nonce; | |
@end | |
@interface ASIFormDataRequest(private) | |
-(id) postData; | |
@end | |
@implementation FROAuthRequest | |
@synthesize token = _token; | |
@synthesize consumer = _consumer; | |
@synthesize signatureProvider = _signatureProvider; | |
#pragma mark - | |
#pragma mark Factory Methods | |
+(id) requestWithURL: (NSURL *)newURL | |
consumer: (OAConsumer*) consumer | |
token: (OAToken*) token | |
realm: (NSString*) realm | |
signatureProvider: (id<OASignatureProviding>) provider | |
{ | |
return [[[FROAuthRequest alloc] initWithURL: newURL | |
consumer: consumer | |
token: token | |
realm: realm | |
signatureProvider: provider | |
] autorelease]; | |
} | |
#pragma mark - | |
#pragma mark Init Methods | |
-(id) initWithURL: (NSURL *)newURL | |
consumer: (OAConsumer*) consumer | |
token: (OAToken*) token | |
realm: (NSString*) realm | |
signatureProvider: (id<OASignatureProviding>) provider | |
{ | |
if( self = [super initWithURL: newURL] ){ | |
//Alter this after the request has been created; | |
//[self setRequestMethod:@"POST"]; | |
[self setConsumer: consumer]; | |
if( token == nil ){ | |
self.token = [[OAToken alloc] init]; | |
} | |
else{ | |
self.token = token; | |
} | |
_realm = realm ? [realm retain] : @""; | |
if( provider == nil ){ | |
self.signatureProvider = [[OAHMAC_SHA1SignatureProvider alloc] init]; | |
} | |
else{ | |
self.signatureProvider = provider; | |
} | |
_nonce = nil; | |
_timestamp = nil; | |
} | |
return self; | |
} | |
#pragma mark - | |
#pragma mark Start Methods | |
//Overload start ASync | |
-(void) startAsynchronous{ | |
[self prepare]; | |
[super startAsynchronous]; | |
} | |
-(void) startSynchronous{ | |
[self prepare]; | |
[super startSynchronous]; | |
} | |
#pragma mark - | |
#pragma mark Overloaded ASIFormDataRequest | |
- (void)buildPostBody | |
{ | |
//If we want to do anything other than GET, build a body | |
if(![[self requestMethod] isEqualToString:@"GET"]){ | |
[super buildPostBody]; | |
} | |
} | |
/* | |
- (void)requestFinished{ | |
NSLog(@"[FROAuthRequest] Request Finished"); | |
[super requestFinished]; | |
} | |
*/ | |
- (void)requestFailed:(FROAuthRequest *) pRequest{ | |
#if DEBUG | |
NSLog(@"[FROAuthRequest requestFailed] %@", [pRequest error]); | |
#endif | |
//[super requestFinished]; | |
} | |
//-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*- | |
// OAuth Methods | |
//-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*- | |
#pragma mark - | |
#pragma mark Request Token | |
+(OAToken*) _requestTokenFromProvider:(NSURL*) requestURL | |
withConsumer:(OAConsumer*) pConsumer | |
forObject:(id) pDelegate | |
{ | |
#if DEBUG | |
NSLog(@"[FROAuthRequest requestToken]"); | |
#endif | |
FROAuthRequest* tokenRequest; | |
tokenRequest = [FROAuthRequest requestWithURL: requestURL | |
consumer: pConsumer | |
token: nil | |
realm: nil | |
signatureProvider: nil | |
]; | |
[tokenRequest startSynchronous]; | |
return [FROAuthRequest _didRequestToken: tokenRequest forObject:pDelegate]; | |
} | |
/* | |
Request token callback | |
*/ | |
+(OAToken*) _didRequestToken:(FROAuthRequest*) pRequest | |
forObject:pDelegate | |
{ | |
OAToken *tempToken; | |
//Parse out the token | |
if( [pRequest responseString] && [pRequest responseStatusCode] == 200 ){ | |
tempToken = [[[OAToken alloc] initWithHTTPResponseBody: [pRequest responseString]] autorelease]; | |
#if DEBUG | |
NSLog(@"[FROAuthRequest didRequestToken] Found a response \r\n%@", [pRequest responseString]); | |
NSLog(@"[FROAuthRequest didRequestToken] New Request Token Created key:%@ secret:%@",tempToken.key,tempToken.secret); | |
#endif | |
//Authenticate the token | |
if( [tempToken key] != nil){ | |
if( pDelegate && [pDelegate respondsToSelector:@selector(authenticateToken:withProvider:)]){ | |
[pDelegate performSelector:@selector( authenticateToken:withProvider:) withObject: tempToken withObject: [pRequest url]]; | |
//[pDelegate authenticateToken: tempToken withProvider:[pRequest url]]; | |
} | |
return tempToken; | |
} | |
} | |
else{ | |
UIAlertView *alertView; | |
alertView = [[UIAlertView alloc] initWithTitle: NSLocalizedString(@"Could not connect",@"Could not connect") | |
message: NSLocalizedString(@"Could not to connect to the internet",@"Could not to connect to the internet") | |
delegate: nil | |
cancelButtonTitle: NSLocalizedString(@"ok",@"Ok") | |
otherButtonTitles: nil | |
]; | |
[alertView show]; | |
[alertView release]; | |
#if DEBUG | |
NSLog(@"[FROAuthRequest didRequestToken] failed"); | |
#endif | |
} | |
//Notify the parent that we failed | |
//[[self delegate] performSelectorOnMainThread:[self didFailSelector] withObject:self waitUntilDone:[NSThread isMainThread]]; | |
return nil; | |
} | |
#pragma mark - | |
#pragma mark Authorize Token | |
+(OAToken*) _accessTokenWithRequestToken:(OAToken*) pToken | |
fromProvider:(NSURL*) accessURL | |
forConsumer:(OAConsumer*) pConsumer | |
forObject:(id) pDelegate | |
{ | |
#if DEBUG | |
NSLog(@"[FROAuthRequest authorizeToken]"); | |
#endif | |
FROAuthRequest* authorizeRequest; | |
//Append the pin to the token | |
//NSString *URLStr = [accessURL absoluteString]; | |
//URLStr = [URLStr stringByAppendingString:[NSString stringWithFormat:@"?oauth_verifier=%s", pToken.pin]]; | |
authorizeRequest = [FROAuthRequest requestWithURL: accessURL //[NSURL URLWithString:URLStr] | |
consumer: pConsumer | |
token: pToken | |
realm: nil | |
signatureProvider: nil | |
]; | |
//We are already operating on a seperate thread so using this should be fine | |
[authorizeRequest startSynchronous]; | |
//This log can be split into callbacks | |
if( [authorizeRequest responseStatusCode] == 200 && [authorizeRequest responseString] ){ | |
#if DEBUG | |
NSLog(@"[FROAuthRequest] Created new Access Token! \r\n%@", [authorizeRequest responseString]); | |
#endif | |
//Set the access token | |
return [[[OAToken alloc] initWithHTTPResponseBody:[authorizeRequest responseString]] autorelease]; | |
} | |
else{ | |
#if DEBUG | |
NSLog(@"[FROAuthRequest] Failed to get Access Token!"); | |
#endif | |
return nil; | |
} | |
} | |
//Use xAuth to authorize a token | |
+(OAToken*) _accessTokenFromProvider:(NSURL*) accessURL | |
WithUsername:(NSString*) pUsername | |
password:(NSString*) pPassword | |
andConsumer:(OAConsumer*) pConsumer | |
{ | |
FROAuthRequest *accessRequest; | |
//Insure that it SSL | |
if( ![[accessURL scheme] isEqualToString:@"https"] ){ | |
#if DEBUG | |
NSLog(@"Not SSL :%@",[accessURL scheme]); | |
#endif | |
//return nil; | |
} | |
accessRequest = [FROAuthRequest requestWithURL: accessURL | |
consumer: pConsumer | |
token: nil | |
realm: nil | |
signatureProvider: nil | |
]; | |
[accessRequest setRequestMethod:@"POST"]; | |
[accessRequest setPostValue:pUsername forKey:@"x_auth_username"]; | |
[accessRequest setPostValue:pPassword forKey:@"x_auth_password"]; | |
[accessRequest setPostValue:@"client_auth" forKey:@"x_auth_mode"]; | |
[accessRequest startSynchronous]; | |
if( [accessRequest responseStatusCode] == 200 && [accessRequest responseString] ){ | |
#if DEBUG | |
NSLog(@"[FROAuthRequest xAuthToken] Success \r\n%@", [accessRequest responseString]); | |
#endif | |
return [[[OAToken alloc] initWithHTTPResponseBody:[accessRequest responseString]] autorelease]; | |
} | |
else{ | |
UIAlertView *alertView; | |
alertView = [[UIAlertView alloc] initWithTitle: @"" | |
message: @"Couldn't find " | |
delegate: nil | |
cancelButtonTitle: nil | |
otherButtonTitles: nil | |
]; | |
[alertView show]; | |
[alertView release]; | |
#if DEBUG | |
NSLog(@"[FROAuthRequest xAuthToken] Failure \r\n%@", [accessRequest error]); | |
#endif | |
return nil; | |
} | |
} | |
//-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*- | |
// Utilites | |
//-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*- | |
/* | |
URL encode a string | |
*/ | |
- (NSString *) URLEncodedString: (NSString *) string { | |
CFStringRef preprocessedString = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef) string, CFSTR(""), kCFStringEncodingUTF8); | |
NSString *result = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, | |
preprocessedString, | |
NULL, | |
CFSTR("!*'();:@&=+$,/?%#[]"), | |
kCFStringEncodingUTF8); | |
[result autorelease]; | |
//NSLog(@"RETAIN COUNT %d",CFGetRetainCount( preprocessedString)); | |
if( preprocessedString != NULL ){ | |
CFRelease(preprocessedString); | |
} | |
//Handle Spaces | |
result = [result stringByReplacingOccurrencesOfString:@"%20" withString:@"%2520"]; | |
//Handle Hashes | |
result = [result stringByReplacingOccurrencesOfString:@"%23" withString:@"%2523"]; | |
//Handle Colons | |
//result = [result stringByReplacingOccurrencesOfString:@"%3A" withString:@"%253A"]; | |
#if DEBUG | |
NSLog(@"String encoded \r\nin:%@ \r\nout:%@", string, result); | |
#endif | |
return result; | |
} | |
//Create a url string | |
- (NSString *)URLStringWithoutQueryFromURL: (NSURL *) pURL | |
{ | |
NSArray *parts = [[pURL absoluteString] componentsSeparatedByString:@"?"]; | |
return [parts objectAtIndex:0]; | |
} | |
/* | |
Generate a timestamp on demand | |
*/ | |
- (NSString*)timestamp | |
{ | |
if(!_timestamp) { | |
_timestamp = [NSString stringWithFormat:@"%d", time(NULL)]; | |
} | |
return _timestamp; | |
} | |
//Generate Nonce on demand | |
- (NSString*)nonce | |
{ | |
if( !_nonce ){ | |
CFUUIDRef theUUID = CFUUIDCreate(NULL); | |
CFStringRef string = CFUUIDCreateString(NULL, theUUID); | |
CFRelease(theUUID); | |
_nonce = (NSString *)string; | |
} | |
return _nonce; | |
} | |
//Create BaseString | |
- (NSString *)signatureBaseString | |
{ | |
NSMutableDictionary *params; | |
NSString *queryStr; | |
//NSArray *queryParams; | |
params = [NSMutableDictionary dictionaryWithObjectsAndKeys: | |
[[self consumer] key], | |
@"oauth_consumer_key", | |
[[self token] key] ? [[self token] key] : @"", //Stops nil from being entered and causing a failure | |
@"oauth_token", | |
[[self signatureProvider] name], | |
@"oauth_signature_method", | |
[self timestamp], | |
@"oauth_timestamp", | |
[self nonce], | |
@"oauth_nonce", | |
@"1.0", | |
@"oauth_version", | |
nil | |
]; | |
//Find out if the params to the query | |
if( queryStr = [[self url] query] ){ | |
//Break the query up into pairs and add them to the dictionary | |
for( NSString *keyValPairStr in [queryStr componentsSeparatedByString:@"&"]){ | |
//NSLog(@"KeyVal Pair %@", keyValPairStr ); | |
NSArray *keyValPair = [keyValPairStr componentsSeparatedByString:@"="]; | |
[params setObject: [keyValPair objectAtIndex:1] forKey: [keyValPair objectAtIndex:0]]; | |
} | |
} | |
//If this is post request find out | |
if( [self isKindOfClass:[ASIFormDataRequest class]] && [self respondsToSelector:@selector(postData)]){ | |
for( NSString *key in [self postData] ){ | |
[params setValue:[[self postData] objectForKey:key] forKey:key]; | |
} | |
} | |
return [self signatureBaseStringForURL: [self url] | |
withMethod: [self requestMethod] | |
withParams: (NSDictionary*)params | |
]; | |
} | |
//Create a signature base string | |
-(NSString*) signatureBaseStringForURL:(NSURL*) pURL | |
withMethod:(NSString*) method | |
withParams:(NSDictionary*) dictionary{ | |
NSMutableArray *pairs; | |
NSArray *sortedPairs; | |
NSString *key, *tmp; | |
NSString *baseString, *normalizedString; | |
if( [dictionary count] < 6 ){ | |
@throw [NSException exceptionWithName:@"InvalidParameterCount" | |
reason:[NSString stringWithFormat:@"Passed Dictionary contains too few entries (6 Min vs %d found)", [dictionary count]] | |
userInfo:[NSDictionary dictionaryWithObjectsAndKeys: dictionary,@"dictionary",self,@"request",nil]]; | |
} | |
pairs = [[NSMutableArray alloc] init]; | |
for( key in dictionary ){ | |
if( [[dictionary objectForKey:key] length] > 0){ | |
tmp = [NSString stringWithFormat:@"%@=%@", key, [dictionary objectForKey:key] ]; | |
tmp = [self URLEncodedString: tmp]; | |
[pairs addObject:tmp]; | |
} | |
else{ | |
#if DEBUG | |
NSLog(@"[FRORequest signatureBaseString] %@ was nil, skipping", key); | |
#endif | |
} | |
} | |
sortedPairs = [pairs sortedArrayUsingSelector:@selector(compare:)]; | |
normalizedString = [sortedPairs componentsJoinedByString:@"&"]; | |
baseString = [NSString stringWithFormat:@"%@&%@&%@", | |
[method uppercaseString], | |
[self URLEncodedString:[self URLStringWithoutQueryFromURL:pURL]], | |
[self URLEncodedString: normalizedString] | |
]; | |
//Cleanup | |
[pairs release]; | |
#if DEBUG | |
NSLog(@"Basestring \r\n%@", baseString); | |
#endif | |
return baseString; | |
} | |
//Add Auth header to this request | |
- (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 | |
NSString *consumerSecret, *tokenSecret, *signature, *oauthToken, *oauthHeader; | |
consumerSecret = [self URLEncodedString: self.consumer.secret]; | |
tokenSecret = [self URLEncodedString: self.token.secret]; | |
signature = [self.signatureProvider signClearText:[self signatureBaseString] | |
withSecret:[NSString stringWithFormat:@"%@&%@", consumerSecret, tokenSecret]]; | |
// set OAuth headers | |
if ([self.token.key isEqualToString:@""]){ | |
oauthToken = @""; // not used on Request Token transactions | |
} | |
else{ | |
oauthToken = [NSString stringWithFormat:@"oauth_token=\"%@\", ", [self URLEncodedString: self.token.key]]; | |
} | |
oauthHeader = [NSString stringWithFormat: | |
@"OAuth realm=\"%@\", oauth_consumer_key=\"%@\", %@oauth_signature_method=\"%@\", oauth_signature=\"%@\", oauth_timestamp=\"%@\", oauth_nonce=\"%@\", oauth_version=\"1.0\"", | |
[self URLEncodedString: _realm], | |
[self URLEncodedString: [self.consumer key]], | |
oauthToken, | |
[self URLEncodedString: [self.signatureProvider name]], | |
[self URLEncodedString: signature], | |
[self timestamp], | |
[self nonce] | |
]; | |
if (self.token.pin.length) oauthHeader = [oauthHeader stringByAppendingFormat: @", oauth_verifier=\"%@\"", self.token.pin]; //added for the Twitter OAuth implementation | |
#if DEBUG | |
NSLog(@"[FROAuthRequest prepare] \r\nAuthentication Header %@", oauthHeader); | |
#endif | |
[self addRequestHeader:@"Authorization" value: oauthHeader]; | |
} | |
#pragma mark - | |
#pragma mark Dealloc | |
-(void) dealloc{ | |
/* | |
[self.requestToken release]; | |
*/ | |
[_realm release]; | |
[_nonce release]; | |
// Cause a crash if if a nil check is done | |
//[_timestamp release]; | |
[_consumer release]; | |
[self.signatureProvider release]; | |
[self.token release]; | |
[super dealloc]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment