diff --git a/Dist/OAuthiOS.framework/Versions/A/Headers/OAuthIOData.h b/Dist/OAuthiOS.framework/Versions/A/Headers/OAuthIOData.h index 4af82c7..f243121 100644 --- a/Dist/OAuthiOS.framework/Versions/A/Headers/OAuthIOData.h +++ b/Dist/OAuthiOS.framework/Versions/A/Headers/OAuthIOData.h @@ -29,6 +29,7 @@ @property (nonatomic, strong) NSDictionary *request_conf; @property (nonatomic, strong) NSString *oauth_token; +@property (nonatomic, strong) NSString *code; @property (nonatomic, strong) NSString *oauth_token_secret; @property (nonatomic, strong) NSString *access_token; @property (nonatomic, strong) NSString *request_url; diff --git a/Dist/OAuthiOS.framework/Versions/A/Headers/OAuthIOModal.h b/Dist/OAuthiOS.framework/Versions/A/Headers/OAuthIOModal.h index cec0bd7..dd2215e 100644 --- a/Dist/OAuthiOS.framework/Versions/A/Headers/OAuthIOModal.h +++ b/Dist/OAuthiOS.framework/Versions/A/Headers/OAuthIOModal.h @@ -25,6 +25,9 @@ @protocol OAuthIODelegate - (void)didReceiveOAuthIOResponse:(OAuthIORequest *)request; - (void)didFailWithOAuthIOError:(NSError *)error; +- (void)didReceiveOAuthIOCode:(NSString *)code; +- (void)didAuthenticateServerSide:(NSString *)body andResponse:(NSURLResponse *) response; +- (void)didFailAuthenticationServerSide:(NSString *)body andResponse:(NSURLResponse *)response andError:(NSError *)error; @end @interface OAuthIOModal : UIViewController @@ -43,15 +46,19 @@ UIWebView *_browser; UINavigationBar *_navigationBar; NSUInteger _navigationBarHeight; + } - +@property NSMutableArray *saved_cookies; @property (weak) id delegate; +@property NSURLSession *session; +@property NSString *authUrl; - (id)initWithKey:(NSString *)key delegate:(id)delegate; - (id)initWithKey:(NSString *)key delegate:(id)delegate andOptions:(NSDictionary *) options; - (void)showWithProvider:(NSString *)provider; - (void)showWithProvider:(NSString *)provider options:(NSDictionary*)options; +- (void)showWithProvider:(NSString *)provider options:(NSDictionary*)options stateTokenUrl:(NSString*) stateUrl authUrl:(NSString*) authUrl; - (BOOL) clearCache; - (BOOL) clearCacheForProvider:(NSString *)provider; - (BOOL) cacheAvailableForProvider:(NSString *)provider; diff --git a/Dist/OAuthiOS.framework/Versions/A/OAuthiOS b/Dist/OAuthiOS.framework/Versions/A/OAuthiOS index b575c42..9cb9925 100644 Binary files a/Dist/OAuthiOS.framework/Versions/A/OAuthiOS and b/Dist/OAuthiOS.framework/Versions/A/OAuthiOS differ diff --git a/OAuth.io.podspec b/OAuth.io.podspec index b6c08e3..4babfb0 100644 --- a/OAuth.io.podspec +++ b/OAuth.io.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "OAuth.io" - s.version = "1.1.3" + s.version = "1.2.0" s.summary = "OAuth that just works!" s.description = <<-DESC @@ -71,7 +71,7 @@ Pod::Spec.new do |s| # Supports git, hg, bzr, svn and HTTP. # - s.source = { :git => "https://github.com/oauth-io/oauth-ios.git", :tag => "1.1.3" } + s.source = { :git => "https://github.com/oauth-io/oauth-ios.git", :tag => "1.2.0" } # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # diff --git a/OAuthiOS/Src/OAuthIOData.h b/OAuthiOS/Src/OAuthIOData.h index 4af82c7..f243121 100644 --- a/OAuthiOS/Src/OAuthIOData.h +++ b/OAuthiOS/Src/OAuthIOData.h @@ -29,6 +29,7 @@ @property (nonatomic, strong) NSDictionary *request_conf; @property (nonatomic, strong) NSString *oauth_token; +@property (nonatomic, strong) NSString *code; @property (nonatomic, strong) NSString *oauth_token_secret; @property (nonatomic, strong) NSString *access_token; @property (nonatomic, strong) NSString *request_url; diff --git a/OAuthiOS/Src/OAuthIOData.m b/OAuthiOS/Src/OAuthIOData.m index 88d7057..e73dcf4 100644 --- a/OAuthiOS/Src/OAuthIOData.m +++ b/OAuthiOS/Src/OAuthIOData.m @@ -40,7 +40,10 @@ - (id)initWithDictionary:(NSDictionary *)dict [_request setValue:_oauth_token forKey:@"oauth_token"]; [_request setValue:_oauth_token_secret forKey:@"oauth_token_secret"]; - } else { + } else if ([[dict objectForKey:@"data"] objectForKey:@"code"] != nil) { + _code = [[dict objectForKey:@"data"] objectForKey:@"code"]; + _request = nil; + }else { [NSException raise:@"Wrong credentials" format:@"A problem occured with the credentials data initialization"]; } diff --git a/OAuthiOS/Src/OAuthIOModal.h b/OAuthiOS/Src/OAuthIOModal.h index cec0bd7..dd2215e 100644 --- a/OAuthiOS/Src/OAuthIOModal.h +++ b/OAuthiOS/Src/OAuthIOModal.h @@ -25,6 +25,9 @@ @protocol OAuthIODelegate - (void)didReceiveOAuthIOResponse:(OAuthIORequest *)request; - (void)didFailWithOAuthIOError:(NSError *)error; +- (void)didReceiveOAuthIOCode:(NSString *)code; +- (void)didAuthenticateServerSide:(NSString *)body andResponse:(NSURLResponse *) response; +- (void)didFailAuthenticationServerSide:(NSString *)body andResponse:(NSURLResponse *)response andError:(NSError *)error; @end @interface OAuthIOModal : UIViewController @@ -43,15 +46,19 @@ UIWebView *_browser; UINavigationBar *_navigationBar; NSUInteger _navigationBarHeight; + } - +@property NSMutableArray *saved_cookies; @property (weak) id delegate; +@property NSURLSession *session; +@property NSString *authUrl; - (id)initWithKey:(NSString *)key delegate:(id)delegate; - (id)initWithKey:(NSString *)key delegate:(id)delegate andOptions:(NSDictionary *) options; - (void)showWithProvider:(NSString *)provider; - (void)showWithProvider:(NSString *)provider options:(NSDictionary*)options; +- (void)showWithProvider:(NSString *)provider options:(NSDictionary*)options stateTokenUrl:(NSString*) stateUrl authUrl:(NSString*) authUrl; - (BOOL) clearCache; - (BOOL) clearCacheForProvider:(NSString *)provider; - (BOOL) cacheAvailableForProvider:(NSString *)provider; diff --git a/OAuthiOS/Src/OAuthIOModal.m b/OAuthiOS/Src/OAuthIOModal.m index a765d73..becc4cb 100644 --- a/OAuthiOS/Src/OAuthIOModal.m +++ b/OAuthiOS/Src/OAuthIOModal.m @@ -18,7 +18,6 @@ #import "OAuthIOModal.h" @implementation OAuthIOModal - NSString *_host; - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil @@ -146,10 +145,63 @@ - (void)getTokens:(NSString *)url [data_to_file writeToFile:file_url atomically:YES]; } + if (_saved_cookies != nil) { + NSHTTPCookie *cookie; + NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + for (cookie in _saved_cookies) { + [storage setCookie:cookie]; + } + [[NSUserDefaults standardUserDefaults] synchronize]; + } + + @try { OAuthIORequest *request = [self buildRequestObject:json]; - if ([self.delegate respondsToSelector:@selector(didReceiveOAuthIOResponse:)]) - [self.delegate didReceiveOAuthIOResponse:request]; + if ([[request getCredentials] objectForKey:@"code"] != nil && _authUrl != nil) { + //Signing in to the selected URL, giving back the response + + NSString *code = [[request getCredentials] objectForKey:@"code"]; + NSString *post = [NSString stringWithFormat:@"code=%@", code]; + NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; + NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]]; + NSMutableURLRequest *state_request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:_authUrl] + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:60.0]; + [state_request setHTTPMethod:@"POST"]; + [state_request setValue:postLength forHTTPHeaderField:@"Content-Length"]; + [state_request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; + [state_request setHTTPBody:postData]; + + [[_session dataTaskWithRequest:state_request + completionHandler:^(NSData *data, + NSURLResponse *response, + NSError *error) { + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; + NSString *body = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; + if ([httpResponse statusCode] == 200) { + if ([self.delegate respondsToSelector:@selector(didAuthenticateServerSide:andResponse:)]) + [self.delegate didAuthenticateServerSide:body andResponse:response]; + } else { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil), + NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The authentication endpoint did not return 200 OK.", nil), + NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Are you using the right authentication endpoint?", nil) + }; + NSError *error = [NSError errorWithDomain:@"com.oauthio.error" + code:-1 + userInfo:userInfo]; + if ([self.delegate respondsToSelector:@selector(didFailAuthenticationServerSide:andResponse:andError:)]) + [self.delegate didFailAuthenticationServerSide:body andResponse:response andError:error]; + } + }] resume]; + + } else if ([[request getCredentials] objectForKey:@"code"] != nil) { + if ([self.delegate respondsToSelector:@selector(didReceiveOAuthIOCode:)]) + [self.delegate didReceiveOAuthIOCode:[[request getCredentials] objectForKey:@"code"]]; + }else { + if ([self.delegate respondsToSelector:@selector(didReceiveOAuthIOResponse:)]) + [self.delegate didReceiveOAuthIOResponse:request]; + } } @catch (NSException *e) { NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; @@ -251,6 +303,38 @@ - (void)showWithProvider:(NSString *)provider { [self showWithProvider:provider options:nil]; } +- (void)showWithProvider:(NSString *)provider options:(NSDictionary*)options stateTokenUrl:(NSString*) stateUrl authUrl:(NSString*) authUrl; +{ + _session = [NSURLSession sharedSession]; + _authUrl = authUrl; + [[_session dataTaskWithURL:[NSURL URLWithString:stateUrl] + completionHandler:^(NSData *data, + NSURLResponse *response, + NSError *error) { + NSString *body = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; + NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; + if (dictionary != nil && [dictionary objectForKey:@"token"] != nil) { + + NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; + NSMutableDictionary *options_mut = [options mutableCopy]; + [options_mut setObject:[dictionary objectForKey:@"token"] forKey:@"state"]; + [self showWithProvider:provider options:options_mut]; + } else { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil), + NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The state token could not be retrieved.", nil), + NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Are you using the right state token retrieval endpoint?", nil) + }; + NSError *error = [NSError errorWithDomain:@"com.oauthio.error" + code:-1 + userInfo:userInfo]; + if ([self.delegate respondsToSelector:@selector(didFailAuthenticationServerSide:andResponse:andError:)]) + [self.delegate didFailAuthenticationServerSide:body andResponse:response andError:error]; + } + + }] resume]; +} + - (void)showWithProvider:(NSString *)provider options:(NSDictionary*)options { _options = options; @@ -293,11 +377,12 @@ - (void)showWithProvider:(NSString *)provider options:(NSDictionary*)options NSURLRequest *url = [_oauth getOAuthRequest:provider andUrl:_callback_url andOptions:options]; if ([[_options objectForKey:@"clear-popup-cache"] isEqual: @"true"]) { -// [[NSURLCache sharedURLCache] removeAllCachedResponses]; NSHTTPCookie *cookie; NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + _saved_cookies = [[NSMutableArray alloc] init]; for (cookie in [storage cookies]) { [storage deleteCookie:cookie]; + [_saved_cookies addObject:cookie]; } [[NSUserDefaults standardUserDefaults] synchronize]; } diff --git a/README.md b/README.md index c3e5310..393280f 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,42 @@ You can filter the fields returned by this method by passing a `NSArray` contain }]; ``` +Notes on the server-side flow +============================= + +If you're planning to use OAuth.io from your back-end thanks to one of our server-side SDKs, the usage of this SDK is a bit different. + +First, a little reminder of the server-side flow, in which all API calls are performed server-side, as well as authentication (thanks to a code retrieved in the front-end). The server-side flow involves the following steps: + +- Retrieving of a state token from your back-end in your front-end +- Launching the authentication popup in the front-end, with the state token, gives back a code +- Sending the code back to the back-end +- Authenticating in the back-end thanks to the code +- Performing API calls in the back-end + +Thus, in your back-end, you need two endpoints: + +- one for the state token (GET) +- one for the authentication (POST with the code as parameter) + +In the iOS SDK, the calls to these endpoints are performed automatically. All you need to do is give their URLs to the `showWithProvider` method like this: + +```Objective-C +[_oauthioModal showWithProvider:@"facebook" options:options stateTokenUrl:@"http://example/state/url" authUrl:@"http://example/auth/url"]; +``` + +This will first call the state URL, then show the authentication popup to the user, get the code, and finally send the code to the authentication URL. + +To know when the process is done, you need to add the following methods to your OAuthIODelegate class: + +```Objective-C +- (void)didAuthenticateServerSide:(NSString *)body andResponse:(NSURLResponse *) response; +- (void)didFailAuthenticationServerSide:(NSString *)body andResponse:(NSURLResponse *)response andError:(NSError *)error; +``` + +The first one will catches a successfull authentication (which usually means the authentication URL returned "200 OK") and give you the body and response objects it got from that URL. The second one will catch errors (state token not found, unsucessfull authentication). + + Contributing ============