Created
September 16, 2012 15:42
-
-
Save danpalmer/3732904 to your computer and use it in GitHub Desktop.
Sign OAuth Requests
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
// | |
// OAuthRequestSigner.m | |
// ----- | |
// | |
// Created by Dan Palmer on 29/07/2012. | |
// Copyright (c) 2012 Dan Palmer. All rights reserved. | |
// | |
#import "OAuthRequestSigner.h" | |
#import "RVURLParser.h" | |
#import "NSData+Base64.h" | |
#import "NSString+URLEncoding.h" | |
#import <CommonCrypto/CommonCrypto.h> | |
@implementation OAuthRequestSigner | |
+ (NSURLRequest *)signedRequestForOAuthRequest:(NSURLRequest *)urlRequest withOAuthParameters:(NSDictionary *)oAuthParameters consumerSecret:(NSString *)consumerSecret tokenSecret:(NSString *)tokenSecret { | |
NSMutableDictionary *unsortedParameters = [NSMutableDictionary dictionaryWithDictionary:oAuthParameters]; | |
NSMutableURLRequest *request = [urlRequest mutableCopy]; | |
NSMutableArray *parameters = [[NSMutableArray alloc] init]; | |
NSString *bodyString = [[NSString alloc] initWithData:[request HTTPBody] encoding:NSUTF8StringEncoding]; | |
[unsortedParameters addEntriesFromDictionary:[RVURLParser parseURLQueryParameters:bodyString]]; | |
NSString *queryString = [[request URL] query]; | |
[unsortedParameters addEntriesFromDictionary:[RVURLParser parseURLQueryParameters:queryString]]; | |
NSArray *sortedHeaderKeys = [[unsortedParameters allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; | |
[sortedHeaderKeys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { | |
NSString *parameter = [unsortedParameters objectForKey:obj]; | |
if (!parameter) { | |
[parameters addObject:[NSString stringWithFormat:@"%@=true", obj]]; | |
return; | |
} | |
[parameters addObject:[NSString stringWithFormat:@"%@=%@", obj, parameter]]; | |
}]; | |
NSString *urlString = [[request URL] absoluteString]; | |
if ([[request URL] query]) { | |
urlString = [[urlString stringByReplacingOccurrencesOfString:[[request URL] query] withString:@""] stringByReplacingOccurrencesOfString:@"?" withString:@""]; | |
} | |
NSMutableString *baseString = [[NSMutableString alloc] init]; | |
[baseString appendString:[[request HTTPMethod] uppercaseString]]; | |
[baseString appendString:@"&"]; | |
[baseString appendString:[urlString urlEncode]]; | |
[baseString appendString:@"&"]; | |
[baseString appendString:[[parameters componentsJoinedByString:@"&"] urlEncode]]; | |
NSString *signingKey = [NSString stringWithFormat:@"%@&%@", | |
consumerSecret, | |
tokenSecret]; | |
const char *key = [signingKey cStringUsingEncoding:NSASCIIStringEncoding]; | |
const char *data = [baseString cStringUsingEncoding:NSASCIIStringEncoding]; | |
unsigned char mac[CC_SHA1_DIGEST_LENGTH]; | |
CCHmac(kCCHmacAlgSHA1, key, strlen(key), data, strlen(data), mac); | |
NSData *hmacData = [NSData dataWithBytes:mac length:sizeof(mac)]; | |
NSString *signature = [hmacData base64EncodedString]; | |
NSMutableDictionary *authorisationHeader = [oAuthParameters mutableCopy]; | |
[authorisationHeader setObject:[signature urlEncode] forKey:@"oauth_signature"]; | |
NSMutableArray *oAuthHeaderFields = [NSMutableArray array]; | |
[[[authorisationHeader allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] enumerateObjectsUsingBlock:^(id key, NSUInteger idx, BOOL *stop) { | |
NSString *param = [NSString stringWithFormat:@"%@=\"%@\"", [key urlEncode], authorisationHeader[key]]; | |
[oAuthHeaderFields addObject:param]; | |
}]; | |
[request setValue:[NSString stringWithFormat:@"OAuth %@", [oAuthHeaderFields componentsJoinedByString:@","]] forHTTPHeaderField:@"Authorization"]; | |
return request; | |
} | |
@end |
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
// | |
// OAuthRequestSignerTests.m | |
// ----- | |
// | |
// Created by Dan Palmer on 29/07/2012. | |
// Copyright (c) 2012 Dan Palmer. All rights reserved. | |
// | |
#import "OAuthRequestSignerTests.h" | |
#import "OAuthRequestSigner.h" | |
@implementation OAuthRequestSignerTests | |
- (void)setUp { | |
[super setUp]; | |
} | |
- (void)tearDown { | |
[super tearDown]; | |
} | |
- (void)testGenerateOAuthSignature { | |
NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/1/statuses/update.json?include_entities=true"]; | |
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; | |
[request setHTTPMethod:@"POST"]; | |
[request setHTTPBody:[@"status=Hello%20Ladies%20%2B%20Gentlemen%2C%20a%20signed%20OAuth%20request%21" dataUsingEncoding:NSUTF8StringEncoding]]; | |
NSDictionary *parameters = @{ | |
@"oauth_consumer_key" : @"xvz1evFS4wEEPTGEFPHBog", | |
@"oauth_nonce" : @"kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", | |
@"oauth_signature_method" : @"HMAC-SHA1", | |
@"oauth_timestamp" : @"1318622958", | |
@"oauth_token" : @"370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb", | |
@"oauth_version" : @"1.0" | |
}; | |
NSString *consumerSecret = @"kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw"; | |
NSString *tokenSecret = @"LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE"; | |
NSURLRequest *signedRequest = [OAuthRequestSigner signedRequestForOAuthRequest:request withOAuthParameters:parameters consumerSecret:consumerSecret tokenSecret:tokenSecret]; | |
NSString *expectedAuthHeader = @"OAuth oauth_consumer_key=\"xvz1evFS4wEEPTGEFPHBog\",oauth_nonce=\"kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg\",oauth_signature=\"tnnArxj06cWHq44gCs1OSKk%2FjLY%3D\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"1318622958\",oauth_token=\"370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb\",oauth_version=\"1.0\""; | |
STAssertEqualObjects([[signedRequest allHTTPHeaderFields] objectForKey:@"Authorization"], expectedAuthHeader, @"Auth header matches expected header value"); | |
} | |
- (void)testGenerateOAuthSignatureForRequestToken { | |
NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/oauth/request_token"]; | |
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; | |
[request setHTTPMethod:@"POST"]; | |
[request setHTTPBody:[@"" dataUsingEncoding:NSUTF8StringEncoding]]; | |
NSDictionary *parameters = @{ | |
@"oauth_callback" : @"http%3A%2F%2Flocalhost%2Fsign-in-with-twitter%2F", | |
@"oauth_consumer_key" : @"cChZNFj6T5R0TigYB9yd1w", | |
@"oauth_nonce" : @"ea9ec8429b68d6b77cd5600adbbb0456", | |
@"oauth_signature_method" : @"HMAC-SHA1", | |
@"oauth_timestamp" : @"1318467427", | |
@"oauth_version" : @"1.0" | |
}; | |
NSString *consumerSecret = @"L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg"; | |
NSURLRequest *signedRequest = [OAuthRequestSigner signedRequestForOAuthRequest:request withOAuthParameters:parameters consumerSecret:consumerSecret tokenSecret:@""]; | |
NSString *expectedHeader = @"OAuth oauth_callback=\"http%3A%2F%2Flocalhost%2Fsign-in-with-twitter%2F\",oauth_consumer_key=\"cChZNFj6T5R0TigYB9yd1w\",oauth_nonce=\"ea9ec8429b68d6b77cd5600adbbb0456\",oauth_signature=\"F1Li3tvehgcraF8DMJ7OyxO4w9Y%3D\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"1318467427\",oauth_version=\"1.0\""; | |
STAssertEqualObjects([[signedRequest allHTTPHeaderFields] objectForKey:@"Authorization"], expectedHeader, @"Auth header matches expected header value"); | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment