diff --git a/Providers/Tumblr/SimpleAuthTumblrLoginViewController.h b/Providers/Tumblr/SimpleAuthTumblrLoginViewController.h new file mode 100644 index 0000000..20b3fc6 --- /dev/null +++ b/Providers/Tumblr/SimpleAuthTumblrLoginViewController.h @@ -0,0 +1,15 @@ +// +// SimpleAuthTumblrLoginViewController.h +// SimpleAuth +// +// Created by Caleb Davenport on 1/16/14. +// Copyright (c) 2014 Seesaw Decisions Corporation. All rights reserved. +// + +#import "SimpleAuthWebViewController.h" + +@interface SimpleAuthTumblrLoginViewController : SimpleAuthWebViewController + +- (instancetype)initWithOptions:(NSDictionary *)options requestToken:(NSDictionary *)requestToken; + +@end diff --git a/Providers/Tumblr/SimpleAuthTumblrLoginViewController.m b/Providers/Tumblr/SimpleAuthTumblrLoginViewController.m new file mode 100644 index 0000000..0580c4a --- /dev/null +++ b/Providers/Tumblr/SimpleAuthTumblrLoginViewController.m @@ -0,0 +1,49 @@ +// +// SimpleAuthTumblrLoginViewController.m +// SimpleAuth +// +// Created by Caleb Davenport on 1/16/14. +// Copyright (c) 2014 Seesaw Decisions Corporation. All rights reserved. +// + +#import "SimpleAuthTumblrLoginViewController.h" + +#import + +@interface SimpleAuthTumblrLoginViewController () + +@property (nonatomic, copy) NSDictionary *requestToken; + +@end + +@implementation SimpleAuthTumblrLoginViewController + +#pragma mark - UIViewController + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + + NSDictionary *parameters = @{ + @"oauth_token" : self.requestToken[@"oauth_token"], + }; + NSString *URLString = [NSString stringWithFormat: + @"http://www.tumblr.com/oauth/authorize?%@", + [parameters sam_stringWithFormEncodedComponents]]; + NSURL *URL = [NSURL URLWithString:URLString]; + + NSURLRequest *request = [NSURLRequest requestWithURL:URL]; + [self.webView loadRequest:request]; +} + + +#pragma mark - Public + +- (instancetype)initWithOptions:(NSDictionary *)options requestToken:(NSDictionary *)requestToken { + if ((self = [super initWithOptions:options])) { + self.requestToken = requestToken; + self.title = @"tumblr"; + } + return self; +} + +@end diff --git a/Providers/Tumblr/SimpleAuthTumblrProvider.h b/Providers/Tumblr/SimpleAuthTumblrProvider.h new file mode 100644 index 0000000..6cab224 --- /dev/null +++ b/Providers/Tumblr/SimpleAuthTumblrProvider.h @@ -0,0 +1,13 @@ +// +// SimpleAuthTumblrProvider.h +// SimpleAuth +// +// Created by Caleb Davenport on 1/16/14. +// Copyright (c) 2014 Seesaw Decisions Corporation. All rights reserved. +// + +#import "SimpleAuthProvider.h" + +@interface SimpleAuthTumblrProvider : SimpleAuthProvider + +@end diff --git a/Providers/Tumblr/SimpleAuthTumblrProvider.m b/Providers/Tumblr/SimpleAuthTumblrProvider.m new file mode 100644 index 0000000..fc2c719 --- /dev/null +++ b/Providers/Tumblr/SimpleAuthTumblrProvider.m @@ -0,0 +1,266 @@ +// +// SimpleAuthTumblrProvider.m +// SimpleAuth +// +// Created by Caleb Davenport on 1/16/14. +// Copyright (c) 2014 Seesaw Decisions Corporation. All rights reserved. +// + +#import "SimpleAuthTumblrProvider.h" +#import "SimpleAuthTumblrLoginViewController.h" + +#import "UIViewController+SimpleAuthAdditions.h" + +#import +#import +#import + +@implementation SimpleAuthTumblrProvider + +#pragma mark - NSObject + ++ (void)load { + @autoreleasepool { + [SimpleAuth registerProviderClass:self]; + } +} + + +#pragma mark - SimpleAuthProvider + ++ (NSString *)type { + return @"tumblr"; +} + + ++ (NSDictionary *)defaultOptions { + + // Default present block + SimpleAuthInterfaceHandler presentBlock = ^(UIViewController *controller) { + UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:controller]; + navigation.modalPresentationStyle = UIModalPresentationFormSheet; + UIViewController *presented = [UIViewController sa_presentedViewController]; + [presented presentViewController:navigation animated:YES completion:nil]; + }; + + // Default dismiss block + SimpleAuthInterfaceHandler dismissBlock = ^(id controller) { + [controller dismissViewControllerAnimated:YES completion:nil]; + }; + + NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithDictionary:[super defaultOptions]]; + dictionary[SimpleAuthPresentInterfaceBlockKey] = presentBlock; + dictionary[SimpleAuthDismissInterfaceBlockKey] = dismissBlock; + dictionary[SimpleAuthRedirectURIKey] = @"simple-auth://tumblr.auth"; + return dictionary; +} + + +- (void)authorizeWithCompletion:(SimpleAuthRequestHandler)completion { + [[[[[self requestToken] + flattenMap:^(NSDictionary *response) { + NSArray *signals = @[ + [RACSignal return:response], + [self authenticateWithRequestToken:response] + ]; + return [RACSignal zip:signals]; + }] + flattenMap:^(RACTuple *response) { + return [self accessTokenWithRequestToken:response.first authenticationResponse:response.second]; + }] + flattenMap:^(NSDictionary *response) { + NSArray *signals = @[ + [self accountWithAccessToken:response], + [RACSignal return:response] + ]; + return [self rac_liftSelector:@selector(dictionaryWithAccount:accessToken:) withSignalsFromArray:signals]; + }] + subscribeNext:^(id x) { + NSLog(@"%@", x); + } + error:^(NSError *error) { + NSLog(@"%@", error); + }]; +} + + +#pragma mark - Private + +- (RACSignal *)requestToken { + return [RACSignal createSignal:^RACDisposable *(id subscriber) { + NSDictionary *parameters = @{ @"oauth_callback" : self.options[SimpleAuthRedirectURIKey] }; + NSURLRequest *request = [GCOAuth + URLRequestForPath:@"/oauth/request_token" + POSTParameters:parameters + scheme:@"https" + host:@"www.tumblr.com" + consumerKey:self.options[@"consumer_key"] + consumerSecret:self.options[@"consumer_secret"] + accessToken:nil + tokenSecret:nil]; + [NSURLConnection sendAsynchronousRequest:request queue:self.operationQueue + completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { + NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 99)]; + NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; + if ([indexSet containsIndex:statusCode] && data) { + NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + NSDictionary *dictionary = [NSDictionary sam_dictionaryWithFormEncodedString:string]; + [subscriber sendNext:dictionary]; + [subscriber sendCompleted]; + } + else { + [subscriber sendError:connectionError]; + } + }]; + return nil; + }]; +} + + +- (RACSignal *)authenticateWithRequestToken:(NSDictionary *)requestToken { + return [RACSignal createSignal:^RACDisposable *(id subscriber) { + dispatch_async(dispatch_get_main_queue(), ^{ + SimpleAuthTumblrLoginViewController *login = [[SimpleAuthTumblrLoginViewController alloc] initWithOptions:self.options requestToken:requestToken]; + + login.completion = ^(UIViewController *controller, NSURL *URL, NSError *error) { + SimpleAuthInterfaceHandler block = self.options[SimpleAuthDismissInterfaceBlockKey]; + block(controller); + + // Parse URL + NSString *query = [URL query]; + NSDictionary *dictionary = [NSDictionary sam_dictionaryWithFormEncodedString:query]; + NSString *token = dictionary[@"oauth_token"]; + NSString *verifier = dictionary[@"oauth_verifier"]; + + // Check for error + if (![token length] || ![verifier length]) { + [subscriber sendError:nil]; + return; + } + + // Send completion + [subscriber sendNext:dictionary]; + [subscriber sendCompleted]; + }; + + SimpleAuthInterfaceHandler block = self.options[SimpleAuthPresentInterfaceBlockKey]; + block(login); + }); + return nil; + }]; +} + + +- (RACSignal *)accessTokenWithRequestToken:(NSDictionary *)requestToken authenticationResponse:(NSDictionary *)authenticationResponse { + return [RACSignal createSignal:^RACDisposable *(id subscriber) { + NSDictionary *parameters = @{ @"oauth_verifier" : authenticationResponse[@"oauth_verifier"] }; + NSURLRequest *request = [GCOAuth + URLRequestForPath:@"/oauth/access_token" + POSTParameters:parameters + scheme:@"https" + host:@"www.tumblr.com" + consumerKey:self.options[@"consumer_key"] + consumerSecret:self.options[@"consumer_secret"] + accessToken:authenticationResponse[@"oauth_token"] + tokenSecret:requestToken[@"oauth_token_secret"]]; + [NSURLConnection sendAsynchronousRequest:request queue:self.operationQueue + completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { + NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 99)]; + NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; + if ([indexSet containsIndex:statusCode] && data) { + NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + NSDictionary *dictinoary = [NSDictionary sam_dictionaryWithFormEncodedString:string]; + [subscriber sendNext:dictinoary]; + [subscriber sendCompleted]; + } + else { + [subscriber sendError:connectionError]; + } + }]; + return nil; + }]; +} + + +- (RACSignal *)accountWithAccessToken:(NSDictionary *)accessToken { + return [RACSignal createSignal:^RACDisposable *(id subscriber) { + NSURLRequest *request = [GCOAuth + URLRequestForPath:@"/v2/user/info" + GETParameters:nil + scheme:@"https" + host:@"api.tumblr.com" + consumerKey:self.options[@"consumer_key"] + consumerSecret:self.options[@"consumer_secret"] + accessToken:accessToken[@"oauth_token"] + tokenSecret:accessToken[@"oauth_token_secret"]]; + [NSURLConnection + sendAsynchronousRequest:request + queue:self.operationQueue + completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { + NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 99)]; + NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; + if ([indexSet containsIndex:statusCode] && data) { + NSError *parseError = nil; + NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&parseError]; + if (dictionary) { + dictionary = dictionary[@"response"][@"user"]; + [subscriber sendNext:dictionary]; + [subscriber sendCompleted]; + } + else { + [subscriber sendError:parseError]; + } + } + else { + [subscriber sendError:connectionError]; + } + }]; + return nil; + }]; +} + + +- (NSDictionary *)dictionaryWithAccount:(NSDictionary *)account accessToken:(NSDictionary *)accessToken { + NSMutableDictionary *dictionary = [NSMutableDictionary new]; + + // Provider + dictionary[@"provider"] = [[self class] type]; + + // Credentials + dictionary[@"credentials"] = @{ + @"token" : accessToken[@"oauth_token"], + @"secret" : accessToken[@"oauth_token_secret"] + }; + + // User ID + dictionary[@"uid"] = account[@"name"]; + + // Extra + dictionary[@"extra"] = @{ + @"raw_info" : account, + }; + + // Blogs + NSArray *blogs = account[@"blogs"]; + blogs = [[blogs.rac_sequence map:^(NSDictionary *dictionary) { + return [dictionary dictionaryWithValuesForKeys:@[ @"name", @"url", @"title" ]]; + }] array]; + + // Profile image + NSString *blogURLString = blogs[0][@"url"]; + NSURL *blogURL = [NSURL URLWithString:blogURLString]; + NSString *host = [blogURL host]; + NSString *avatar = [NSString stringWithFormat:@"https://api.tumblr.com/v2/blog/%@/avatar", host]; + + // User info + NSMutableDictionary *user = [NSMutableDictionary new]; + user[@"nickname"] = account[@"name"]; + user[@"name"] = account[@"name"]; + user[@"blogs"] = blogs; + user[@"image"] = avatar; + dictionary[@"info"] = user; + + return dictionary; +} + +@end diff --git a/Readme.markdown b/Readme.markdown index 58d8f02..1f28392 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -7,8 +7,9 @@ SimpleAuth currently has the following providers: - Facebook (system) - Twitter (system) - Instagram -- Twitter web +- Twitter (web) - Meetup +- Tumblr I would like to have: @@ -18,8 +19,7 @@ I would like to have: - Foursquare - Dropbox - App Dot Net -- Facebook web -- Tumblr web +- Facebook (web) ## Installing diff --git a/SimpleAuth.podspec b/SimpleAuth.podspec index de1a904..97e71eb 100644 --- a/SimpleAuth.podspec +++ b/SimpleAuth.podspec @@ -54,4 +54,13 @@ Pod::Spec.new do |s| ss.source_files = 'Providers/Meetup/**/*.{h,m}' ss.frameworks = 'UIKit' end + + s.subspec 'Tumblr' do |ss| + ss.dependency 'SimpleAuth/Core' + + ss.source_files = 'Providers/Tumblr/**/*.{h,m}' + ss.frameworks = 'UIKit' + + ss.dependency 'cocoa-oauth' + end end \ No newline at end of file diff --git a/SimpleAuth.xcodeproj/project.pbxproj b/SimpleAuth.xcodeproj/project.pbxproj index caf935f..17bc167 100644 --- a/SimpleAuth.xcodeproj/project.pbxproj +++ b/SimpleAuth.xcodeproj/project.pbxproj @@ -17,6 +17,8 @@ 3B65843D188892FE00D59100 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3B65843C188892FE00D59100 /* Images.xcassets */; }; 3B6584581888931F00D59100 /* SADProviderListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B6584571888931F00D59100 /* SADProviderListViewController.m */; }; 3B65845C188895E400D59100 /* libSimpleAuth.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BB32504182ABC8B00ACB555 /* libSimpleAuth.a */; }; + 3B6584601888A94200D59100 /* SimpleAuthTumblrProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B65845F1888A94200D59100 /* SimpleAuthTumblrProvider.m */; }; + 3B6584631888ABFB00D59100 /* SimpleAuthTumblrLoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B6584621888ABFB00D59100 /* SimpleAuthTumblrLoginViewController.m */; }; 3B8C408E188792A9007DC578 /* SimpleAuthTwitterWebProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B8C408D188792A9007DC578 /* SimpleAuthTwitterWebProvider.m */; }; 3B8C409118879347007DC578 /* SimpleAuthTwitterWebLoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B8C409018879347007DC578 /* SimpleAuthTwitterWebLoginViewController.m */; }; 3B9AB064182AC2710011FB9E /* SimpleAuthProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B9AB063182AC2710011FB9E /* SimpleAuthProvider.m */; }; @@ -65,6 +67,10 @@ 3B658443188892FE00D59100 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 3B6584571888931F00D59100 /* SADProviderListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SADProviderListViewController.m; sourceTree = ""; }; 3B6584591888933200D59100 /* SADProviderListViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SADProviderListViewController.h; sourceTree = ""; }; + 3B65845E1888A94200D59100 /* SimpleAuthTumblrProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAuthTumblrProvider.h; sourceTree = ""; }; + 3B65845F1888A94200D59100 /* SimpleAuthTumblrProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleAuthTumblrProvider.m; sourceTree = ""; }; + 3B6584611888ABFB00D59100 /* SimpleAuthTumblrLoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAuthTumblrLoginViewController.h; sourceTree = ""; }; + 3B6584621888ABFB00D59100 /* SimpleAuthTumblrLoginViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleAuthTumblrLoginViewController.m; sourceTree = ""; }; 3B8C408C188792A9007DC578 /* SimpleAuthTwitterWebProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAuthTwitterWebProvider.h; sourceTree = ""; }; 3B8C408D188792A9007DC578 /* SimpleAuthTwitterWebProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleAuthTwitterWebProvider.m; sourceTree = ""; }; 3B8C408F18879347007DC578 /* SimpleAuthTwitterWebLoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAuthTwitterWebLoginViewController.h; sourceTree = ""; }; @@ -117,6 +123,7 @@ 3B8C408B18879296007DC578 /* TwitterWeb */, 3B52BB96188731A300C73329 /* Instagram */, 4D149A3E1889B38C0024639C /* Meetup */, + 3B65845D1888A93200D59100 /* Tumblr */, ); path = Providers; sourceTree = ""; @@ -174,6 +181,17 @@ name = "Supporting Files"; sourceTree = ""; }; + 3B65845D1888A93200D59100 /* Tumblr */ = { + isa = PBXGroup; + children = ( + 3B65845E1888A94200D59100 /* SimpleAuthTumblrProvider.h */, + 3B65845F1888A94200D59100 /* SimpleAuthTumblrProvider.m */, + 3B6584611888ABFB00D59100 /* SimpleAuthTumblrLoginViewController.h */, + 3B6584621888ABFB00D59100 /* SimpleAuthTumblrLoginViewController.m */, + ); + path = Tumblr; + sourceTree = ""; + }; 3B8C408B18879296007DC578 /* TwitterWeb */ = { isa = PBXGroup; children = ( @@ -410,10 +428,12 @@ 3B8C408E188792A9007DC578 /* SimpleAuthTwitterWebProvider.m in Sources */, 4D149A411889B3DF0024639C /* SimpleAuthMeetupProvider.m in Sources */, 3B8C409118879347007DC578 /* SimpleAuthTwitterWebLoginViewController.m in Sources */, + 3B6584631888ABFB00D59100 /* SimpleAuthTumblrLoginViewController.m in Sources */, 3B52BB9518871F6200C73329 /* SimpleAuthFacebookProvider.m in Sources */, 3B9AB064182AC2710011FB9E /* SimpleAuthProvider.m in Sources */, 3B52BB9B188731A300C73329 /* SimpleAuthInstagramLoginViewController.m in Sources */, 3C0A5393182C4217002C050C /* SimpleAuthWebViewController.m in Sources */, + 3B6584601888A94200D59100 /* SimpleAuthTumblrProvider.m in Sources */, 3B9AB06E182ACCBB0011FB9E /* SimpleAuthSystemProvider.m in Sources */, 4D149A441889B4390024639C /* SimpleAuthMeetupLoginViewController.m in Sources */, 3C611F48183535A400D87E45 /* UIViewController+SimpleAuthAdditions.m in Sources */, diff --git a/SimpleAuthDemo/SADAppDelegate.m b/SimpleAuthDemo/SADAppDelegate.m index 0318797..d8bac8b 100644 --- a/SimpleAuthDemo/SADAppDelegate.m +++ b/SimpleAuthDemo/SADAppDelegate.m @@ -51,9 +51,12 @@ - (void)configureAuthorizaionProviders { // app_id is required SimpleAuth.configuration[@"facebook"] = @{}; - + // client_id and redirect_uri are required SimpleAuth.configuration[@"meetup"] = @{}; + + // consumer_key and consumer_secret are required + SimpleAuth.configuration[@"tumblr"] = @{}; } diff --git a/SimpleAuthDemo/SADProviderListViewController.m b/SimpleAuthDemo/SADProviderListViewController.m index 67ba137..6fa4dc7 100644 --- a/SimpleAuthDemo/SADProviderListViewController.m +++ b/SimpleAuthDemo/SADProviderListViewController.m @@ -47,7 +47,8 @@ + (NSArray *)providers { @"twitter-web", @"facebook", @"instagram", - @"meetup" + @"meetup", + @"tumblr" ]; }); return array;