From 4ba8f8de2c2d54e0f8ea5533871f49d53d6e1d55 Mon Sep 17 00:00:00 2001 From: Gerson Alexander Pardo Gamez Date: Sun, 23 Mar 2014 07:21:31 -0500 Subject: [PATCH 1/6] optional information I tried login with test accounts and sometimes users can not have certain fields like username, email or verified not having these fields cause an error in the application --- Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m b/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m index 10d356d..731272c 100644 --- a/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m +++ b/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m @@ -161,8 +161,8 @@ - (NSDictionary *)dictionaryWithAccount:(NSDictionary *)account accessToken:(NSD // User info NSMutableDictionary *user = [NSMutableDictionary new]; - user[@"nickname"] = account[@"username"]; - user[@"email"] = account[@"email"]; + user[@"nickname"] = account[@"username"] ? account[@"username"] : @""; + user[@"email"] = account[@"email"] ? account[@"email"] : @""; user[@"name"] = account[@"name"]; user[@"first_name"] = account[@"first_name"]; user[@"last_name"] = account[@"last_name"]; @@ -170,7 +170,7 @@ - (NSDictionary *)dictionaryWithAccount:(NSDictionary *)account accessToken:(NSD if (location) { user[@"location"] = location; } - user[@"verified"] = account[@"verified"]; + user[@"verified"] = account[@"verified"] ? account[@"verified"] : @"" ; user[@"urls"] = @{ @"Facebook" : account[@"link"], }; From b9f382c04cef019eb1c7eae35fbff09428c2effb Mon Sep 17 00:00:00 2001 From: Gerson Alexander Pardo Gamez Date: Sun, 23 Mar 2014 08:07:52 -0500 Subject: [PATCH 2/6] Update SimpleAuthFaceBookWebProvider.m From Facebook: On the web, basic_info is implied with every request and isn't required, although the best practice is to include it. Passing basic_info is required on iOS and Android with every permission request. A person's public profile refers to the following properties on the user object by default: id name first_name last_name link username gender locale age_range Other public information https://developers.facebook.com/docs/facebook-login/permissions/ --- Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m b/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m index 731272c..a159c9d 100644 --- a/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m +++ b/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m @@ -40,7 +40,7 @@ + (NSDictionary *)defaultOptions { dictionary[SimpleAuthPresentInterfaceBlockKey] = presentBlock; dictionary[SimpleAuthDismissInterfaceBlockKey] = dismissBlock; dictionary[SimpleAuthRedirectURIKey] = @"https://www.facebook.com/connect/login_success.html"; - dictionary[@"permissions"] = @[ @"email" ]; + dictionary[@"permissions"] = @[ @"email", @"basic_info" ]; return dictionary; } @@ -157,11 +157,11 @@ - (NSDictionary *)dictionaryWithAccount:(NSDictionary *)account accessToken:(NSD NSString *avatar = [NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large", account[@"id"]]; // Location - NSString *location = account[@"location"][@"name"]; + NSString *location = account[@"location"] && account[@"location"][@"name"] ? account[@"location"][@"name"] : @""; // User info NSMutableDictionary *user = [NSMutableDictionary new]; - user[@"nickname"] = account[@"username"] ? account[@"username"] : @""; + user[@"nickname"] = account[@"username"]; user[@"email"] = account[@"email"] ? account[@"email"] : @""; user[@"name"] = account[@"name"]; user[@"first_name"] = account[@"first_name"]; From 85d92c2e030180058987e989a847d46180b8392c Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Fri, 25 Apr 2014 10:47:17 -0700 Subject: [PATCH 3/6] Cleanup and simplify logic. --- Providers/Facebook/SimpleAuthFacebookProvider.m | 8 +++++--- Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m | 10 ++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Providers/Facebook/SimpleAuthFacebookProvider.m b/Providers/Facebook/SimpleAuthFacebookProvider.m index 15bddbe..eb5d85d 100644 --- a/Providers/Facebook/SimpleAuthFacebookProvider.m +++ b/Providers/Facebook/SimpleAuthFacebookProvider.m @@ -25,7 +25,7 @@ + (NSString *)type { + (NSDictionary *)defaultOptions { return @{ @"permissions" : @[ @"email" ], - @"audience" : @[ACFacebookAudienceOnlyMe] + @"audience" : @[ ACFacebookAudienceOnlyMe ] }; } @@ -124,7 +124,9 @@ - (NSDictionary *)dictionaryWithRemoteAccount:(NSDictionary *)remoteAccount syst // User info NSMutableDictionary *user = [NSMutableDictionary new]; user[@"nickname"] = remoteAccount[@"username"]; - user[@"email"] = remoteAccount[@"email"]; + if (remoteAccount[@"email"]) { + user[@"email"] = remoteAccount[@"email"]; + } user[@"name"] = remoteAccount[@"name"]; user[@"first_name"] = remoteAccount[@"first_name"]; user[@"last_name"] = remoteAccount[@"last_name"]; @@ -132,7 +134,7 @@ - (NSDictionary *)dictionaryWithRemoteAccount:(NSDictionary *)remoteAccount syst if (location) { user[@"location"] = location; } - user[@"verified"] = remoteAccount[@"verified"]; + user[@"verified"] = remoteAccount[@"verified"] ?: @NO; user[@"urls"] = @{ @"Facebook" : remoteAccount[@"link"], }; diff --git a/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m b/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m index a159c9d..63d3e6d 100644 --- a/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m +++ b/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m @@ -40,7 +40,7 @@ + (NSDictionary *)defaultOptions { dictionary[SimpleAuthPresentInterfaceBlockKey] = presentBlock; dictionary[SimpleAuthDismissInterfaceBlockKey] = dismissBlock; dictionary[SimpleAuthRedirectURIKey] = @"https://www.facebook.com/connect/login_success.html"; - dictionary[@"permissions"] = @[ @"email", @"basic_info" ]; + dictionary[@"permissions"] = @[ @"email" ]; return dictionary; } @@ -157,12 +157,14 @@ - (NSDictionary *)dictionaryWithAccount:(NSDictionary *)account accessToken:(NSD NSString *avatar = [NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large", account[@"id"]]; // Location - NSString *location = account[@"location"] && account[@"location"][@"name"] ? account[@"location"][@"name"] : @""; + NSString *location = account[@"location"][@"name"]; // User info NSMutableDictionary *user = [NSMutableDictionary new]; user[@"nickname"] = account[@"username"]; - user[@"email"] = account[@"email"] ? account[@"email"] : @""; + if (account[@"email"]) { + user[@"email"] = account[@"email"]; + } user[@"name"] = account[@"name"]; user[@"first_name"] = account[@"first_name"]; user[@"last_name"] = account[@"last_name"]; @@ -170,7 +172,7 @@ - (NSDictionary *)dictionaryWithAccount:(NSDictionary *)account accessToken:(NSD if (location) { user[@"location"] = location; } - user[@"verified"] = account[@"verified"] ? account[@"verified"] : @"" ; + user[@"verified"] = account[@"verified"] ?: @NO; user[@"urls"] = @{ @"Facebook" : account[@"link"], }; From bc913bdf994846f37c7354d96b987db7ddc8e339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lui=CC=81s=20Portela=20Afonso?= Date: Sun, 2 Mar 2014 11:33:01 +0000 Subject: [PATCH 4/6] Added Feedly web configuration * Created web view controller based on simpleauth base * Added demo --- .../SimpleAuthFeedlyWebLoginViewController.h | 13 ++ .../SimpleAuthFeedlyWebLoginViewController.m | 35 ++++ .../FeedlyWeb/SimpleAuthFeedlyWebProvider.h | 13 ++ .../FeedlyWeb/SimpleAuthFeedlyWebProvider.m | 155 ++++++++++++++++++ SimpleAuth.xcodeproj/project.pbxproj | 20 +++ SimpleAuthDemo/SADAppDelegate.m | 8 + .../SADProviderListViewController.m | 3 +- 7 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.h create mode 100644 Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.m create mode 100644 Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.h create mode 100644 Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m diff --git a/Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.h b/Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.h new file mode 100644 index 0000000..fbcfe43 --- /dev/null +++ b/Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.h @@ -0,0 +1,13 @@ +// +// SimpleAuthFeedlyWebLoginViewController.h +// SimpleAuth +// +// Created by Luís Portela Afonso on 26/02/14. +// Copyright (c) 2014 Byliner, Inc. All rights reserved. +// + +#import "SimpleAuthWebViewController.h" + +@interface SimpleAuthFeedlyWebLoginViewController : SimpleAuthWebViewController + +@end diff --git a/Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.m b/Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.m new file mode 100644 index 0000000..0bc569b --- /dev/null +++ b/Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.m @@ -0,0 +1,35 @@ +// +// SimpleAuthFeedlyWebLoginViewController.m +// SimpleAuth +// +// Created by Luís Portela Afonso on 26/02/14. +// Copyright (c) 2014 Byliner, Inc. All rights reserved. +// + +#import "SimpleAuthFeedlyWebLoginViewController.h" + + +@implementation SimpleAuthFeedlyWebLoginViewController + +- (id)initWithOptions:(NSDictionary *)options requestToken:(NSDictionary *)requestToken { + if ((self = [super initWithOptions:options requestToken:requestToken])) { + self.title = @"Feedly"; + } + return self; +} + + +- (NSURLRequest *)initialRequest { + NSDictionary *parameters = @{ + @"response_type" : @"code", + @"client_id" : self.options[@"client_id"], + @"redirect_uri" : self.options[@"redirect_uri"], + @"scope" : (self.options[@"scope"] != nil ? self.options[@"code"] : @"https://cloud.feedly.com/subscriptions"), + @"state" : (self.options[@"state"] != nil ? self.options[@"state"] : @"state.passed.in") + }; + NSString *URLString = [NSString stringWithFormat:@"http://feedly.com/v3/auth/auth?%@", [CMDQueryStringSerialization queryStringWithDictionary:parameters]]; + + return [NSURLRequest requestWithURL:[NSURL URLWithString:URLString]]; +} + +@end diff --git a/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.h b/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.h new file mode 100644 index 0000000..7f5e0b1 --- /dev/null +++ b/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.h @@ -0,0 +1,13 @@ +// +// SimpleAuthFeedlyWebProvider.h +// SimpleAuth +// +// Created by Luís Portela Afonso on 26/02/14. +// Copyright (c) 2014 Byliner, Inc. All rights reserved. +// + +#import "SimpleAuthProvider.h" + +@interface SimpleAuthFeedlyWebProvider : SimpleAuthProvider + +@end diff --git a/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m b/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m new file mode 100644 index 0000000..159c275 --- /dev/null +++ b/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m @@ -0,0 +1,155 @@ +// +// SimpleAuthFeedlyWebProvider.m +// SimpleAuth +// +// Created by Luís Portela Afonso on 26/02/14. +// Copyright (c) 2014 Byliner, Inc. All rights reserved. +// + +#import "SimpleAuthFeedlyWebProvider.h" +#import "SimpleAuthFeedlyWebLoginViewController.h" + +#import "UIViewController+SimpleAuthAdditions.h" +#import + +@implementation SimpleAuthFeedlyWebProvider + ++ (NSString *)type +{ + return @"feedly-web"; +} + ++ (NSDictionary *)defaultOptions +{ + // Default present block + SimpleAuthInterfaceHandler presentBlock = ^(UIViewController *controller) { + + UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:controller]; + navigation.modalPresentationStyle = UIModalPresentationFormSheet; + UIViewController *presented = [UIViewController SimpleAuth_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; + return dictionary; +} + +- (void)authorizeWithCompletion:(SimpleAuthRequestHandler)completion +{ + [[[self authenticationCode] flattenMap:^(id response) { + NSArray *signals = @[ + [self exchangeCodeForRefreshAndAccess:response], + [RACSignal return:response] + ]; + return signals[0]; + }] + subscribeNext:^(id response) { + completion(response, nil); + } + error:^(NSError *error) { + completion(nil, error); + }]; +} + +#pragma mark - Private Methods + +- (RACSignal*)authenticationCode +{ + return [RACSignal createSignal:^RACDisposable *(id subscriber) { + dispatch_async(dispatch_get_main_queue(), ^{ + + SimpleAuthFeedlyWebLoginViewController *login = [[SimpleAuthFeedlyWebLoginViewController alloc] initWithOptions:self.options]; + + login.completion = ^(UIViewController *controller, NSURL *URL, NSError *error) { + SimpleAuthInterfaceHandler block = self.options[SimpleAuthDismissInterfaceBlockKey]; + block(controller); + + // Parse URL + NSDictionary *dictionary = [CMDQueryStringSerialization dictionaryWithQueryString:[URL query]]; + + id apiError = dictionary[@"error"]; + + // Check for error + if (apiError != nil) + { + [subscriber sendError:error]; + return; + } + + NSString *code = dictionary[@"code"]; + NSString *state = dictionary[@"state"]; + + // Send completion + [subscriber sendNext:@{@"code": code, @"state": state}]; + [subscriber sendCompleted]; + }; + + SimpleAuthInterfaceHandler block = self.options[SimpleAuthPresentInterfaceBlockKey]; + block(login); + + }); + + return nil; + }]; +} + +- (RACSignal*)exchangeCodeForRefreshAndAccess:(NSDictionary *)codeState +{ + return [RACSignal createSignal:^RACDisposable *(id subscriber) { + + NSDictionary *parameters = @{ + @"code" : codeState[@"code"], + @"client_id" : self.options[@"client_id"], + @"client_secret" : self.options[@"client_secret"], + @"redirect_uri" : self.options[@"redirect_uri"], + @"grant_type" : @"authorization_code", + @"state" : (codeState[@"state"] != nil ? codeState[@"state"] : @"state.passed.in") + }; + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://feedly.com/v3/auth/token"]]; + + [request setHTTPMethod:@"POST"]; + [request setValue:@"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:@"Content-Type"]; + [request setHTTPBody:[[CMDQueryStringSerialization queryStringWithDictionary:parameters] dataUsingEncoding:NSUTF8StringEncoding]]; + + [NSURLConnection sendAsynchronousRequest:request queue:self.operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { + + if (connectionError != nil) + { + [subscriber sendError:connectionError]; + } + else + { + + NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 99)]; + NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; + if ([indexSet containsIndex:statusCode] && data) + { + __weak NSError *parseError = nil; + __weak NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&parseError]; + + if (parseError != nil) + { + [subscriber sendError:parseError]; + } + else + { + [subscriber sendNext:dictionary]; + [subscriber sendCompleted]; + } + } + } + }]; + + return nil; + }]; +} + +@end diff --git a/SimpleAuth.xcodeproj/project.pbxproj b/SimpleAuth.xcodeproj/project.pbxproj index 49d25fd..e18617e 100644 --- a/SimpleAuth.xcodeproj/project.pbxproj +++ b/SimpleAuth.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 1B766AE718C34D2400F77FA8 /* SimpleAuthFeedlyWebLoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B766AE418C34D2400F77FA8 /* SimpleAuthFeedlyWebLoginViewController.m */; }; + 1B766AE818C34D2400F77FA8 /* SimpleAuthFeedlyWebProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B766AE618C34D2400F77FA8 /* SimpleAuthFeedlyWebProvider.m */; }; 3B35369A1894C5710044EA0E /* ACAccountStore+SimpleAuth.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B3536991894C5710044EA0E /* ACAccountStore+SimpleAuth.m */; }; 3B52BB911887088400C73329 /* SimpleAuthTwitterProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B52BB901887088400C73329 /* SimpleAuthTwitterProvider.m */; }; 3B52BB9518871F6200C73329 /* SimpleAuthFacebookProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B52BB9418871F6200C73329 /* SimpleAuthFacebookProvider.m */; }; @@ -58,6 +60,10 @@ /* Begin PBXFileReference section */ 0D4998CFA03844FB99EE78AA /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 0FCEF85A832046CDB0CCBF25 /* Pods.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.xcconfig; path = Pods/Pods.xcconfig; sourceTree = ""; }; + 1B766AE318C34D2400F77FA8 /* SimpleAuthFeedlyWebLoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAuthFeedlyWebLoginViewController.h; sourceTree = ""; }; + 1B766AE418C34D2400F77FA8 /* SimpleAuthFeedlyWebLoginViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleAuthFeedlyWebLoginViewController.m; sourceTree = ""; }; + 1B766AE518C34D2400F77FA8 /* SimpleAuthFeedlyWebProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAuthFeedlyWebProvider.h; sourceTree = ""; }; + 1B766AE618C34D2400F77FA8 /* SimpleAuthFeedlyWebProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SimpleAuthFeedlyWebProvider.m; sourceTree = ""; }; 3B3536981894C5710044EA0E /* ACAccountStore+SimpleAuth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ACAccountStore+SimpleAuth.h"; sourceTree = ""; }; 3B3536991894C5710044EA0E /* ACAccountStore+SimpleAuth.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ACAccountStore+SimpleAuth.m"; sourceTree = ""; }; 3B52BB8F1887088400C73329 /* SimpleAuthTwitterProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleAuthTwitterProvider.h; sourceTree = ""; }; @@ -151,9 +157,21 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 1B766AE218C34D2400F77FA8 /* FeedlyWeb */ = { + isa = PBXGroup; + children = ( + 1B766AE318C34D2400F77FA8 /* SimpleAuthFeedlyWebLoginViewController.h */, + 1B766AE418C34D2400F77FA8 /* SimpleAuthFeedlyWebLoginViewController.m */, + 1B766AE518C34D2400F77FA8 /* SimpleAuthFeedlyWebProvider.h */, + 1B766AE618C34D2400F77FA8 /* SimpleAuthFeedlyWebProvider.m */, + ); + path = FeedlyWeb; + sourceTree = ""; + }; 3B52BB8D1887088400C73329 /* Providers */ = { isa = PBXGroup; children = ( + 1B766AE218C34D2400F77FA8 /* FeedlyWeb */, 3B52BB9218871F6200C73329 /* Facebook */, 3B66656918903F4000F3BF58 /* Facebook Web */, 3B52BB8E1887088400C73329 /* Twitter */, @@ -543,6 +561,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1B766AE818C34D2400F77FA8 /* SimpleAuthFeedlyWebProvider.m in Sources */, 3BC5FC701891D86A00523166 /* SimpleAuthDropboxWebProvider.m in Sources */, 3B8C408E188792A9007DC578 /* SimpleAuthTwitterWebProvider.m in Sources */, 81A50A4C189264C500D5D8AC /* SimpleAuthLinkedInWebLoginViewController.m in Sources */, @@ -552,6 +571,7 @@ 3B8C409118879347007DC578 /* SimpleAuthTwitterWebLoginViewController.m in Sources */, 3B69D1FA18C0340200E3D415 /* UIWindow+SimpleAuthAdditions.m in Sources */, 3B6584631888ABFB00D59100 /* SimpleAuthTumblrLoginViewController.m in Sources */, + 1B766AE718C34D2400F77FA8 /* SimpleAuthFeedlyWebLoginViewController.m in Sources */, 3B52BB9518871F6200C73329 /* SimpleAuthFacebookProvider.m in Sources */, 3B9AB064182AC2710011FB9E /* SimpleAuthProvider.m in Sources */, 3B52BB9B188731A300C73329 /* SimpleAuthInstagramLoginViewController.m in Sources */, diff --git a/SimpleAuthDemo/SADAppDelegate.m b/SimpleAuthDemo/SADAppDelegate.m index 8b4c39a..b9ed549 100644 --- a/SimpleAuthDemo/SADAppDelegate.m +++ b/SimpleAuthDemo/SADAppDelegate.m @@ -70,6 +70,14 @@ - (void)configureAuthorizaionProviders { // client_id and client_secret are required SimpleAuth.configuration[@"sinaweibo-web"] = @{}; + + // client_id, client_secret and redirect_uri are required + // scope it is an optional paramenter and https://cloud.feedly.com/subscriptions is used if none specified + SimpleAuth.configuration[@"feedly-web"] = @{ + @"client_id":@"client_id", + @"client_secret":@"client_secret", + @"redirect_uri":@"redirect_uri", + }; } diff --git a/SimpleAuthDemo/SADProviderListViewController.m b/SimpleAuthDemo/SADProviderListViewController.m index 604ec1d..0a9c446 100644 --- a/SimpleAuthDemo/SADProviderListViewController.m +++ b/SimpleAuthDemo/SADProviderListViewController.m @@ -53,7 +53,8 @@ + (NSArray *)providers { @"foursquare-web", @"dropbox-web", @"linkedin-web", - @"sinaweibo-web" + @"sinaweibo-web", + @"feedly-web" ]; }); return array; From 858e5ebd7af15262f2f4e36cbc4d185b9af8527b Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Fri, 25 Apr 2014 11:18:41 -0700 Subject: [PATCH 5/6] Clean up feedly provider implementation. #49 --- .../SimpleAuthFeedlyWebLoginViewController.m | 13 +- .../FeedlyWeb/SimpleAuthFeedlyWebProvider.m | 182 +++++++++--------- 2 files changed, 99 insertions(+), 96 deletions(-) diff --git a/Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.m b/Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.m index 0bc569b..283889f 100644 --- a/Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.m +++ b/Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.m @@ -8,7 +8,6 @@ #import "SimpleAuthFeedlyWebLoginViewController.h" - @implementation SimpleAuthFeedlyWebLoginViewController - (id)initWithOptions:(NSDictionary *)options requestToken:(NSDictionary *)requestToken { @@ -21,14 +20,12 @@ - (id)initWithOptions:(NSDictionary *)options requestToken:(NSDictionary *)reque - (NSURLRequest *)initialRequest { NSDictionary *parameters = @{ - @"response_type" : @"code", - @"client_id" : self.options[@"client_id"], - @"redirect_uri" : self.options[@"redirect_uri"], - @"scope" : (self.options[@"scope"] != nil ? self.options[@"code"] : @"https://cloud.feedly.com/subscriptions"), - @"state" : (self.options[@"state"] != nil ? self.options[@"state"] : @"state.passed.in") - }; + @"response_type" : @"code", + @"client_id" : self.options[@"client_id"], + @"redirect_uri" : self.options[@"redirect_uri"], + @"scope" : self.options[@"scope"] + }; NSString *URLString = [NSString stringWithFormat:@"http://feedly.com/v3/auth/auth?%@", [CMDQueryStringSerialization queryStringWithDictionary:parameters]]; - return [NSURLRequest requestWithURL:[NSURL URLWithString:URLString]]; } diff --git a/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m b/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m index 159c275..cdd326f 100644 --- a/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m +++ b/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m @@ -8,19 +8,21 @@ #import "SimpleAuthFeedlyWebProvider.h" #import "SimpleAuthFeedlyWebLoginViewController.h" - #import "UIViewController+SimpleAuthAdditions.h" + #import @implementation SimpleAuthFeedlyWebProvider -+ (NSString *)type -{ +#pragma mark - SimpleAuthProvider + ++ (NSString *)type { return @"feedly-web"; } -+ (NSDictionary *)defaultOptions -{ + ++ (NSDictionary *)defaultOptions { + // Default present block SimpleAuthInterfaceHandler presentBlock = ^(UIViewController *controller) { @@ -38,118 +40,122 @@ + (NSDictionary *)defaultOptions NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithDictionary:[super defaultOptions]]; dictionary[SimpleAuthPresentInterfaceBlockKey] = presentBlock; dictionary[SimpleAuthDismissInterfaceBlockKey] = dismissBlock; + dictionary[@"scope"] = @"https://cloud.feedly.com/subscriptions"; return dictionary; } -- (void)authorizeWithCompletion:(SimpleAuthRequestHandler)completion -{ - [[[self authenticationCode] flattenMap:^(id response) { - NSArray *signals = @[ - [self exchangeCodeForRefreshAndAccess:response], - [RACSignal return:response] - ]; - return signals[0]; - }] - subscribeNext:^(id response) { - completion(response, nil); - } - error:^(NSError *error) { - completion(nil, error); - }]; + +- (void)authorizeWithCompletion:(SimpleAuthRequestHandler)completion { + [[[[self authenticationCode] + flattenMap:^(NSString *code) { + return [self accessTokenWithAuthenticationCode:code]; + }] + flattenMap:^(NSDictionary *accessToken) { + NSArray *signals = @[ + [RACSignal return:accessToken], + [self accountWithAccessToken:accessToken] + ]; + return [self rac_liftSelector:@selector(dictionaryWithAccessTokenResponse:accountResponse:) withSignalsFromArray:signals]; + }] + subscribeNext:^(NSDictionary *dictionary) { + completion(dictionary, nil); + } + error:^(NSError *error) { + completion(nil, error); + }]; } -#pragma mark - Private Methods -- (RACSignal*)authenticationCode -{ +#pragma mark - Private + +- (RACSignal*)authenticationCode { return [RACSignal createSignal:^RACDisposable *(id subscriber) { dispatch_async(dispatch_get_main_queue(), ^{ - SimpleAuthFeedlyWebLoginViewController *login = [[SimpleAuthFeedlyWebLoginViewController alloc] initWithOptions:self.options]; - login.completion = ^(UIViewController *controller, NSURL *URL, NSError *error) { SimpleAuthInterfaceHandler block = self.options[SimpleAuthDismissInterfaceBlockKey]; block(controller); // Parse URL - NSDictionary *dictionary = [CMDQueryStringSerialization dictionaryWithQueryString:[URL query]]; - - id apiError = dictionary[@"error"]; + NSString *query = [URL query]; + NSDictionary *dictionary = [CMDQueryStringSerialization dictionaryWithQueryString:query]; + id code = dictionary[@"code"]; - // Check for error - if (apiError != nil) - { - [subscriber sendError:error]; - return; + // Check for error + if (!code) { + [subscriber sendError:error]; + return ; } - - NSString *code = dictionary[@"code"]; - NSString *state = dictionary[@"state"]; - + // Send completion - [subscriber sendNext:@{@"code": code, @"state": state}]; - [subscriber sendCompleted]; + [subscriber sendNext:@"code"]; + [subscriber sendCompleted]; }; SimpleAuthInterfaceHandler block = self.options[SimpleAuthPresentInterfaceBlockKey]; block(login); - }); - return nil; }]; } -- (RACSignal*)exchangeCodeForRefreshAndAccess:(NSDictionary *)codeState -{ - return [RACSignal createSignal:^RACDisposable *(id subscriber) { - - NSDictionary *parameters = @{ - @"code" : codeState[@"code"], - @"client_id" : self.options[@"client_id"], - @"client_secret" : self.options[@"client_secret"], - @"redirect_uri" : self.options[@"redirect_uri"], - @"grant_type" : @"authorization_code", - @"state" : (codeState[@"state"] != nil ? codeState[@"state"] : @"state.passed.in") - }; - - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://feedly.com/v3/auth/token"]]; - - [request setHTTPMethod:@"POST"]; - [request setValue:@"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:@"Content-Type"]; - [request setHTTPBody:[[CMDQueryStringSerialization queryStringWithDictionary:parameters] dataUsingEncoding:NSUTF8StringEncoding]]; + +- (RACSignal *)accessTokenWithAuthenticationCode:(NSString *)code { + return [RACSignal createSignal:^RACDisposable *(id subscriber) { - [NSURLConnection sendAsynchronousRequest:request queue:self.operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { - - if (connectionError != nil) - { - [subscriber sendError:connectionError]; - } - else - { - - NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 99)]; - NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; - if ([indexSet containsIndex:statusCode] && data) - { - __weak NSError *parseError = nil; - __weak NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&parseError]; - - if (parseError != nil) - { - [subscriber sendError:parseError]; - } - else - { - [subscriber sendNext:dictionary]; - [subscriber sendCompleted]; - } - } - } - }]; + // Build request + NSDictionary *parameters = @{ + @"code" : code, + @"client_id" : self.options[@"client_id"], + @"client_secret" : self.options[@"client_secret"], + @"redirect_uri" : self.options[@"redirect_uri"], + @"grant_type" : @"authorization_code", + }; + NSData *POSTBody = [NSJSONSerialization dataWithJSONObject:parameters options:kNilOptions error:nil]; + NSURL *URL = [NSURL URLWithString:@"http://feedly.com/v3/auth/token"]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; + [request setHTTPMethod:@"POST"]; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + [request setHTTPBody:POSTBody]; + // Run request + [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) { + [subscriber sendNext:dictionary]; + [subscriber sendCompleted]; + } + else { + [subscriber sendError:parseError]; + } + } + else { + [subscriber sendError:connectionError]; + } + }]; + + // Return return nil; - }]; + }]; +} + + +- (RACSignal *)accountWithAccessToken:(NSDictionary *)accessToken { + return [RACSignal createSignal:^RACDisposable *(id subscriber) { + [subscriber sendNext:nil]; + [subscriber sendCompleted]; + return nil; + }]; +} + + +- (NSDictionary *)dictionaryWithAccessTokenResponse:(NSDictionary *)accessToken accountResponse:(NSDictionary *)account { + return accessToken; } @end From 44ec9fcd0921a5f9510804337312a6b799e68b4d Mon Sep 17 00:00:00 2001 From: Caleb Davenport Date: Fri, 25 Apr 2014 11:23:40 -0700 Subject: [PATCH 6/6] Start work on building omniauth style response. #49 --- .../FeedlyWeb/SimpleAuthFeedlyWebProvider.m | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m b/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m index cdd326f..0d3e0cc 100644 --- a/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m +++ b/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m @@ -155,7 +155,30 @@ - (RACSignal *)accountWithAccessToken:(NSDictionary *)accessToken { - (NSDictionary *)dictionaryWithAccessTokenResponse:(NSDictionary *)accessToken accountResponse:(NSDictionary *)account { - return accessToken; + NSMutableDictionary *dictionary = [NSMutableDictionary new]; + + // Provider + dictionary[@"provider"] = [[self class] type]; + + // Credentials + NSTimeInterval expiresAtInterval = [accessToken[@"expires_in"] doubleValue]; + NSDate *expiresAtDate = [NSDate dateWithTimeIntervalSinceNow:expiresAtInterval]; + dictionary[@"credentials"] = @{ + @"token" : accessToken[@"access_token"], + @"expires_at" : expiresAtDate, + @"type" : accessToken[@"token_type"], + @"refresh" : accessToken[@"refresh_token"] + }; + + // User ID + dictionary[@"uid"] = accessToken[@"id"]; + + // User info + NSMutableDictionary *user = [NSMutableDictionary new]; + user[@"plan"] = accessToken[@"plan"]; + dictionary[@"info"] = user; + + return dictionary; } @end