From 6bc1d05ca438b4774a5c1668656f6691189d65dc Mon Sep 17 00:00:00 2001 From: Justin Carstens Date: Mon, 3 Feb 2014 18:06:31 -0700 Subject: [PATCH] Added re-auth for tokens that expire Added re-auth for tokens that expire by adding a new method that allows a user to pass in an existing token returning a new token with a later expiration date. If provider does not have this method code reverts to the normal login method for provider. This fixes issue #30 --- .../Facebook/SimpleAuthFacebookProvider.m | 3 +- .../SimpleAuthFaceBookWebProvider.m | 68 ++++++++++++++++++- SimpleAuth/SimpleAuth.h | 24 +++++++ SimpleAuth/SimpleAuth.m | 29 ++++++++ SimpleAuth/SimpleAuthProvider.h | 1 + SimpleAuth/SimpleAuthProvider.m | 5 ++ SimpleAuthDemo/SADAppDelegate.m | 1 + .../SADProviderListViewController.m | 4 +- 8 files changed, 132 insertions(+), 3 deletions(-) diff --git a/Providers/Facebook/SimpleAuthFacebookProvider.m b/Providers/Facebook/SimpleAuthFacebookProvider.m index 820da52..ef80f76 100644 --- a/Providers/Facebook/SimpleAuthFacebookProvider.m +++ b/Providers/Facebook/SimpleAuthFacebookProvider.m @@ -130,7 +130,8 @@ - (NSDictionary *)dictionaryWithRemoteAccount:(NSDictionary *)remoteAccount syst if (location) { user[@"location"] = location; } - user[@"verified"] = remoteAccount[@"verified"]; + if (remoteAccount[@"verified"]) + user[@"verified"] = remoteAccount[@"verified"]; user[@"urls"] = @{ @"Facebook" : remoteAccount[@"link"], }; diff --git a/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m b/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m index 10d356d..9a67bec 100644 --- a/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m +++ b/Providers/FacebookWeb/SimpleAuthFaceBookWebProvider.m @@ -62,6 +62,28 @@ - (void)authorizeWithCompletion:(SimpleAuthRequestHandler)completion { }]; } +- (void)reAuthorizeWithToken:(NSString *)token completionHandler:(SimpleAuthRequestHandler)completion{ + if (token.length <= 0) { + // There is no token so we are going to use the standard login method + [self authorizeWithCompletion:completion]; + return; + } + [[[self reAuthAccessTokenWithToken:token] + flattenMap:^(NSDictionary *response) { + NSArray *signals = @[ + [self accountWithAccessToken:response], + [RACSignal return:response] + ]; + return [self rac_liftSelector:@selector(dictionaryWithAccount:accessToken:) withSignalsFromArray:signals]; + }] + subscribeNext:^(id x) { + completion(x, nil); + } + error:^(NSError *error) { + completion(nil, error); + }]; +} + #pragma mark - Private @@ -98,6 +120,49 @@ - (RACSignal *)accessToken { }]; } +- (RACSignal *)reAuthAccessTokenWithToken:(NSString *)preToken { + return [RACSignal createSignal:^RACDisposable *(id subscriber) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSDictionary *parameters = @{ + @"client_id" : self.options[@"app_id"], + @"grant_type" :@"fb_exchange_token", + @"response_type" : @"token", + @"grant_type" :@"fb_exchange_token", + @"client_secret" : self.options[@"app_secret"], + @"fb_exchange_token" : preToken + }; + + NSString *URLString = [NSString stringWithFormat: + @"https://graph.facebook.com/oauth/access_token?%@", + [CMDQueryStringSerialization queryStringWithDictionary:parameters]]; + NSError *responseError = nil; + NSURLResponse *response = nil; + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:URLString]]; + NSData *dataResponse = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&responseError]; + if (!responseError){ + NSString *responseString = [[NSString alloc] initWithData:dataResponse encoding:NSUTF8StringEncoding]; + NSDictionary *dictionary = [CMDQueryStringSerialization dictionaryWithQueryString:responseString]; + id token = dictionary[@"access_token"]; + id expiration = dictionary[@"expires"]; + + // Check for error + if (!token || !expiration) { + [subscriber sendError:[NSError errorWithDomain:@"Missing token or experation from facebook" code:101 userInfo:nil]]; + return; + } + // Send completion + [subscriber sendNext:@{@"access_token" : token, @"expires_in" : expiration}]; + [subscriber sendCompleted]; + } + else { + [subscriber sendError:responseError]; + return ; + } + }); + return nil; + }]; +} + - (RACSignal *)accountWithAccessToken:(NSDictionary *)accessToken { return [RACSignal createSignal:^RACDisposable *(id subscriber) { @@ -170,7 +235,8 @@ - (NSDictionary *)dictionaryWithAccount:(NSDictionary *)account accessToken:(NSD if (location) { user[@"location"] = location; } - user[@"verified"] = account[@"verified"]; + if (account[@"verified"]) + user[@"verified"] = account[@"verified"]; user[@"urls"] = @{ @"Facebook" : account[@"link"], }; diff --git a/SimpleAuth/SimpleAuth.h b/SimpleAuth/SimpleAuth.h index 14042d3..8d78b8d 100644 --- a/SimpleAuth/SimpleAuth.h +++ b/SimpleAuth/SimpleAuth.h @@ -92,4 +92,28 @@ extern NSString * const SimpleAuthEndActivityBlockKey; */ + (void)authorize:(NSString *)provider options:(NSDictionary *)options completion:(SimpleAuthRequestHandler)completion; +/** + Perform re-authorization with the given provider and all previously configured + and default provider options. This is for providers where the token expires and you + want to renew the non expired token. + + @param token the unexpired token recieved from the service + @param completion Called on the main queue when the operation is complete. + + @see +authorize:options:completion: + */ ++ (void)reAuthorize:(NSString *)provider token:(NSString *)token completion:(SimpleAuthRequestHandler)completion; + +/** + Perform re-authorization with the given provider and all previously configured + and default provider options. This is for providers where the token expires and you + want to renew the non expired token. + + @param token the unexpired token recieved from the service + @param completion Called on the main queue when the operation is complete. + + @see +authorize:options:completion: + */ ++ (void)reAuthorize:(NSString *)type options:(NSDictionary *)options token:(NSString *)token completion:(SimpleAuthRequestHandler)completion; + @end diff --git a/SimpleAuth/SimpleAuth.m b/SimpleAuth/SimpleAuth.m index f2922e4..3cf633b 100644 --- a/SimpleAuth/SimpleAuth.m +++ b/SimpleAuth/SimpleAuth.m @@ -68,6 +68,35 @@ + (void)authorize:(NSString *)type options:(NSDictionary *)options completion:(S } ++ (void)reAuthorize:(NSString *)type token:(NSString *)token completion:(SimpleAuthRequestHandler)completion { + [self reAuthorize:type options:nil token:token completion:completion]; +} + + ++ (void)reAuthorize:(NSString *)type options:(NSDictionary *)options token:(NSString *)token completion:(SimpleAuthRequestHandler)completion{ + // Load the provider class + Class klass = [self providers][type]; + NSAssert(klass, @"There is no class registered to handle %@ requests.", type); + + // Create options dictionary + NSDictionary *defaultOptions = [klass defaultOptions]; + NSDictionary *registeredOptions = [self configuration][type]; + NSMutableDictionary *resolvedOptions = [NSMutableDictionary new]; + [resolvedOptions addEntriesFromDictionary:defaultOptions]; + [resolvedOptions addEntriesFromDictionary:registeredOptions]; + [resolvedOptions addEntriesFromDictionary:options]; + + // Create the provider and run authorization + SimpleAuthProvider *provider = [(SimpleAuthProvider *)[klass alloc] initWithOptions:resolvedOptions]; + [provider reAuthorizeWithToken:token completionHandler:^(id responseObject, NSError *error) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(responseObject, error); + }); + [provider class]; // Kepp the provider around until the callback is complete + }]; +} + + #pragma mark - Internal + (void)registerProviderClass:(Class)klass { diff --git a/SimpleAuth/SimpleAuthProvider.h b/SimpleAuth/SimpleAuthProvider.h index a531d37..57534ce 100644 --- a/SimpleAuth/SimpleAuthProvider.h +++ b/SimpleAuth/SimpleAuthProvider.h @@ -20,5 +20,6 @@ - (instancetype)initWithOptions:(NSDictionary *)options; - (void)authorizeWithCompletion:(SimpleAuthRequestHandler)completion; +- (void)reAuthorizeWithToken:(NSString *)token completionHandler:(SimpleAuthRequestHandler)completion; @end diff --git a/SimpleAuth/SimpleAuthProvider.m b/SimpleAuth/SimpleAuthProvider.m index 369d868..e57be7c 100644 --- a/SimpleAuth/SimpleAuthProvider.m +++ b/SimpleAuth/SimpleAuthProvider.m @@ -43,6 +43,11 @@ - (void)authorizeWithCompletion:(SimpleAuthRequestHandler)completion { [self doesNotRecognizeSelector:_cmd]; } +- (void)reAuthorizeWithToken:(NSString *)token completionHandler:(SimpleAuthRequestHandler)completion { + // Provider doesn't have a re authorize token method setup revert back to standard authorization + [self authorizeWithCompletion:completion]; +} + #pragma mark - Accessors diff --git a/SimpleAuthDemo/SADAppDelegate.m b/SimpleAuthDemo/SADAppDelegate.m index b5d5b35..e567d64 100644 --- a/SimpleAuthDemo/SADAppDelegate.m +++ b/SimpleAuthDemo/SADAppDelegate.m @@ -51,6 +51,7 @@ - (void)configureAuthorizaionProviders { // app_id is required SimpleAuth.configuration[@"facebook"] = @{}; + // app_secret optional required for re-authorization of token SimpleAuth.configuration[@"facebook-web"] = @{}; // client_id and redirect_uri are required diff --git a/SimpleAuthDemo/SADProviderListViewController.m b/SimpleAuthDemo/SADProviderListViewController.m index 4f8c7d0..15c34d7 100644 --- a/SimpleAuthDemo/SADProviderListViewController.m +++ b/SimpleAuthDemo/SADProviderListViewController.m @@ -10,7 +10,9 @@ #import "SimpleAuth.h" -@interface SADProviderListViewController () +@interface SADProviderListViewController () { + NSMutableDictionary *tokens; +} @end