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 9d24989..ca8f1cb 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 */; }; @@ -56,6 +58,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 = ""; }; @@ -149,9 +155,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 */, @@ -530,6 +548,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 */, @@ -538,6 +557,7 @@ 81A50A491892649300D5D8AC /* SimpleAuthLinkedInWebProvider.m in Sources */, 3B8C409118879347007DC578 /* SimpleAuthTwitterWebLoginViewController.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;