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 10d356d..63d3e6d 100644 --- a/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m +++ b/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m @@ -162,7 +162,9 @@ - (NSDictionary *)dictionaryWithAccount:(NSDictionary *)account accessToken:(NSD // User info NSMutableDictionary *user = [NSMutableDictionary new]; user[@"nickname"] = account[@"username"]; - user[@"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"]; + user[@"verified"] = account[@"verified"] ?: @NO; user[@"urls"] = @{ @"Facebook" : account[@"link"], }; 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..283889f --- /dev/null +++ b/Providers/FeedlyWeb/SimpleAuthFeedlyWebLoginViewController.m @@ -0,0 +1,32 @@ +// +// 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"] + }; + 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..0d3e0cc --- /dev/null +++ b/Providers/FeedlyWeb/SimpleAuthFeedlyWebProvider.m @@ -0,0 +1,184 @@ +// +// 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 + +#pragma mark - SimpleAuthProvider + ++ (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; + dictionary[@"scope"] = @"https://cloud.feedly.com/subscriptions"; + return dictionary; +} + + +- (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 + +- (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 + NSString *query = [URL query]; + NSDictionary *dictionary = [CMDQueryStringSerialization dictionaryWithQueryString:query]; + id code = dictionary[@"code"]; + + // Check for error + if (!code) { + [subscriber sendError:error]; + return ; + } + + // Send completion + [subscriber sendNext:@"code"]; + [subscriber sendCompleted]; + }; + + SimpleAuthInterfaceHandler block = self.options[SimpleAuthPresentInterfaceBlockKey]; + block(login); + }); + return nil; + }]; +} + + +- (RACSignal *)accessTokenWithAuthenticationCode:(NSString *)code { + return [RACSignal createSignal:^RACDisposable *(id subscriber) { + + // 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 { + 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 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;