diff --git a/Example/SimpleAuth/Providers.plist b/Example/SimpleAuth/Providers.plist index efa2451..6c4aa1c 100644 --- a/Example/SimpleAuth/Providers.plist +++ b/Example/SimpleAuth/Providers.plist @@ -19,5 +19,6 @@ trello-web box-web onedrive-web + dribbble diff --git a/Example/SimpleAuth/SADAppDelegate.m b/Example/SimpleAuth/SADAppDelegate.m index 98a5cb2..362f506 100644 --- a/Example/SimpleAuth/SADAppDelegate.m +++ b/Example/SimpleAuth/SADAppDelegate.m @@ -88,6 +88,9 @@ - (void)configureAuthorizaionProviders { // client_id and client_secret are required SimpleAuth.configuration[@"onedrive-web"] = @{}; + + // client_id and client_secret are required + SimpleAuth.configuration[@"dribbble"] = @{}; } diff --git a/Pod/Providers/Dribbble/SimpleAuthDribbbleLoginViewController.h b/Pod/Providers/Dribbble/SimpleAuthDribbbleLoginViewController.h new file mode 100644 index 0000000..43d5336 --- /dev/null +++ b/Pod/Providers/Dribbble/SimpleAuthDribbbleLoginViewController.h @@ -0,0 +1,13 @@ +// +// SimpleAuthDribbbleLoginViewController.h +// SimpleAuth +// +// Created by Martin Pilch on 21/4/15. +// Copyright (c) 2015 Martin Pilch, All rights reserved. +// + +#import "SimpleAuthWebViewController.h" + +@interface SimpleAuthDribbbleLoginViewController : SimpleAuthWebViewController + +@end diff --git a/Pod/Providers/Dribbble/SimpleAuthDribbbleLoginViewController.m b/Pod/Providers/Dribbble/SimpleAuthDribbbleLoginViewController.m new file mode 100644 index 0000000..edeed23 --- /dev/null +++ b/Pod/Providers/Dribbble/SimpleAuthDribbbleLoginViewController.m @@ -0,0 +1,37 @@ +// +// SimpleAuthDribbbleLoginViewController.m +// SimpleAuthInstagram +// +// Created by Martin Pilch on 21/4/15. +// Copyright (c) 2015 Martin Pilch, All rights reserved. +// + +#import "SimpleAuthDribbbleLoginViewController.h" + +@implementation SimpleAuthDribbbleLoginViewController + +#pragma mark - SimpleAuthWebViewController + +- (instancetype)initWithOptions:(NSDictionary *)options requestToken:(NSDictionary *)requestToken { + if ((self = [super initWithOptions:options requestToken:requestToken])) { + self.title = @"Dribbble"; + } + return self; +} + +- (NSURLRequest *)initialRequest { + NSMutableDictionary *parameters = [NSMutableDictionary new]; + parameters[@"client_id"] = self.options[@"client_id"]; + parameters[@"redirect_uri"] = self.options[SimpleAuthRedirectURIKey]; + if (self.options[@"scope"]) { + parameters[@"scope"] = [self.options[@"scope"] componentsJoinedByString:@" "]; + } + NSString *URLString = [NSString stringWithFormat: + @"https://dribbble.com/oauth/authorize?%@", + [CMDQueryStringSerialization queryStringWithDictionary:parameters]]; + NSURL *URL = [NSURL URLWithString:URLString]; + + return [NSURLRequest requestWithURL:URL]; +} + +@end diff --git a/Pod/Providers/Dribbble/SimpleAuthDribbbleProvider.h b/Pod/Providers/Dribbble/SimpleAuthDribbbleProvider.h new file mode 100644 index 0000000..1ca32c2 --- /dev/null +++ b/Pod/Providers/Dribbble/SimpleAuthDribbbleProvider.h @@ -0,0 +1,13 @@ +// +// SimpleAuthDribbbleProvider.h +// SimpleAuth +// +// Created by Martin Pilch on 21/4/15. +// Copyright (c) 2015 Martin Pilch, All rights reserved. +// + +#import "SimpleAuthProvider.h" + +@interface SimpleAuthDribbbleProvider : SimpleAuthProvider + +@end diff --git a/Pod/Providers/Dribbble/SimpleAuthDribbbleProvider.m b/Pod/Providers/Dribbble/SimpleAuthDribbbleProvider.m new file mode 100644 index 0000000..52e144d --- /dev/null +++ b/Pod/Providers/Dribbble/SimpleAuthDribbbleProvider.m @@ -0,0 +1,206 @@ +// +// SimpleAuthDribbbleProvider.m +// SimpleAuth +// +// Created by Martin Pilch on 21/4/15. +// Copyright (c) 2015 Martin Pilch, All rights reserved. +// + +#import "SimpleAuthDribbbleProvider.h" +#import "SimpleAuthDribbbleLoginViewController.h" + +#import "UIViewController+SimpleAuthAdditions.h" +#import + +@implementation SimpleAuthDribbbleProvider + +#pragma mark - SimpleAuthProvider + ++ (NSString *)type { + return @"dribbble"; +} + ++ (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 *options = [NSMutableDictionary dictionaryWithDictionary:[super defaultOptions]]; + options[SimpleAuthPresentInterfaceBlockKey] = presentBlock; + options[SimpleAuthDismissInterfaceBlockKey] = dismissBlock; + options[SimpleAuthRedirectURIKey] = @"http://"; + + return options; +} + + +- (void)authorizeWithCompletion:(SimpleAuthRequestHandler)completion { + [[[self accessToken] + flattenMap:^(id responseObject) { + NSArray *signals = @[ + [self accountWithAccessToken:responseObject], + [RACSignal return:responseObject] + ]; + return [self rac_liftSelector:@selector(dictionaryWithAccount:accessToken:) withSignalsFromArray:signals]; + }] + subscribeNext:^(id responseObject) { + completion(responseObject, nil); + } + error:^(NSError *error) { + completion(nil, error); + }]; +} + +#pragma mark - Private + +- (RACSignal *)authorizationCode { + return [RACSignal createSignal:^RACDisposable *(id subscriber) { + dispatch_async(dispatch_get_main_queue(), ^{ + SimpleAuthDribbbleLoginViewController *login = [[SimpleAuthDribbbleLoginViewController alloc] initWithOptions:self.options]; + login.completion = ^(UIViewController *login, NSURL *URL, NSError *error) { + SimpleAuthInterfaceHandler dismissBlock = self.options[SimpleAuthDismissInterfaceBlockKey]; + dismissBlock(login); + + // Parse URL + NSString *fragment = [URL query]; + NSDictionary *dictionary = [CMDQueryStringSerialization dictionaryWithQueryString:fragment]; + NSString *code = dictionary[@"code"]; + + // Check for error + if (![code length]) { + [subscriber sendError:error]; + return; + } + + // Send completion + [subscriber sendNext:code]; + [subscriber sendCompleted]; + }; + + SimpleAuthInterfaceHandler block = self.options[SimpleAuthPresentInterfaceBlockKey]; + block(login); + }); + return nil; + }]; +} + + +- (RACSignal *)accessTokenWithAuthorizationCode:(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[SimpleAuthRedirectURIKey], + }; + NSString *query = [CMDQueryStringSerialization queryStringWithDictionary:parameters]; + NSURL *URL = [NSURL URLWithString:@"https://dribbble.com/oauth/token"]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; + request.HTTPMethod = @"POST"; + request.HTTPBody = [query dataUsingEncoding:NSUTF8StringEncoding]; + + // 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 nil; + }]; +} + + +- (RACSignal *)accessToken { + return [[self authorizationCode] flattenMap:^(id responseObject) { + return [self accessTokenWithAuthorizationCode:responseObject]; + }]; +} + +- (RACSignal *)accountWithAccessToken:(NSDictionary *)accessToken { + return [RACSignal createSignal:^RACDisposable *(id subscriber) { + NSDictionary *parameters = @{ + @"access_token" : accessToken[@"access_token"], + }; + NSString *URLString = [NSString stringWithFormat:@"https://api.dribbble.com/v1/user?%@", + [CMDQueryStringSerialization queryStringWithDictionary:parameters]]; + NSURL *URL = [NSURL URLWithString:URLString]; + NSURLRequest *request = [NSURLRequest requestWithURL:URL]; + [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 nil; + }]; +} + + +- (NSDictionary *)dictionaryWithAccount:(NSDictionary *)account accessToken:(NSDictionary *)accessToken { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + + // Provider + dictionary[@"provider"] = [[self class] type]; + + // Credentials + dictionary[@"credentials"] = @{@"token" : accessToken[@"access_token"]}; + + // User ID + dictionary[@"uid"] = account[@"id"]; + + // Raw response + dictionary[@"extra"] = @{ + @"raw_info" : account + }; + + // User info + NSMutableDictionary *user = [NSMutableDictionary new]; + user[@"name"] = account[@"name"]; + user[@"username"] = account[@"username"]; + user[@"image"] = account[@"avatar_url"]; + dictionary[@"info"] = user; + + return dictionary; +} + +@end diff --git a/SimpleAuth.podspec b/SimpleAuth.podspec index 4a9df8f..e085143 100755 --- a/SimpleAuth.podspec +++ b/SimpleAuth.podspec @@ -128,4 +128,10 @@ Pod::Spec.new do |s| ss.private_header_files = 'Pod/Providers/OneDriveWeb/*.h' end + s.subspec 'Dribbble' do |ss| + ss.dependency 'SimpleAuth/Core' + ss.source_files = 'Pod/Providers/Dribbble' + ss.private_header_files = 'Pod/Providers/Dribbble/*.h' + end + end