Skip to content

Instantly share code, notes, and snippets.

@danpalmer
Created September 16, 2012 15:42
Show Gist options
  • Save danpalmer/3732904 to your computer and use it in GitHub Desktop.
Save danpalmer/3732904 to your computer and use it in GitHub Desktop.
Sign OAuth Requests
//
// 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
//
// 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