Permalink
Browse files

Better handle ios6 system auth in case user un-tosses app from accoun…

…t settings by calling "ACAccountStore renew" when using ios6 system auth and response contains an invalid session.

Summary:
      Also added overload to FBSession closeAndClearToken: to take an NSError*.
      Also tweaked FBSession close to no-op if already closed.
      Also tweaked Scrumptious example to report login errors from FBUserSettingsView.

     This does NOT address the "code 2" errors from when ios6 users toggle the
     slider in their device Facebook settings though those cases do transition
     to "ClosedLoginFailed" so it can be argued clients can code against it.

     This also does NOT seamlessly fix cases where the ios6 auth user has
     removed the post permission from their account settings; though it does
     allow them to logout and log back in to resolve. Further, this scenario
     does surface the "(#200) Requires extended permission: publish_actions" in the
     startForPostWithGraphPath handler so clients can surface it.

Test Plan:
     Tested many cases with scrumptious, but primary issue was:
     1. using io6 system auth, login and post from scrumptious.
     2. remove scrumptious from account settings.
     3. re-open app (if already on announce page, will transition to login)
     4. click login
     5. with fix, user is prompted via ios6 dialog again.
        (without fix), login would flash to announce view then present login.

     Tested the above in both scenarios where step 1 ends in either announce page
      or if user explicitly logged out (i.e., to verify user does not need to
      click login twice). Updated in revision 2 so that user does click login twice, if they had explicitly logged out of the app (the first login will report the error, the second login will re-surface the ios dialog).

     Tested same scenario with wilde-login on device, and also on ios 4.3 simulator
      to verify no changes in those uses cases.

Revert Plan:

Reviewers: jacl, gregschechte

Reviewed By: jacl

CC: clang, ekoneil

Differential Revision: https://phabricator.fb.com/D587553

Task ID: 1764202
  • Loading branch information...
1 parent 8868dcb commit 9aebe4cdfcee6ffeffacb195926d08bce6b26b18 @chrisp-fb chrisp-fb committed with onebit Sep 28, 2012
Showing with 70 additions and 17 deletions.
  1. +14 −0 samples/Scrumptious/scrumptious/SCViewController.m
  2. +4 −1 src/FBRequestConnection.m
  3. +2 −0 src/FBSession+Internal.h
  4. +50 −16 src/FBSession.m
@@ -314,6 +314,7 @@ -(void)viewWillAppear:(BOOL)animated{
-(void)settingsButtonWasPressed:(id)sender {
if (self.settingsViewController == nil) {
self.settingsViewController = [[FBUserSettingsViewController alloc] init];
+ self.settingsViewController.delegate = self;
}
[self.navigationController pushViewController:self.settingsViewController animated:YES];
}
@@ -348,6 +349,19 @@ - (void)setPlaceCacheDescriptorForCoordinates:(CLLocationCoordinate2D)coordinate
fieldsForRequest:nil];
}
+#pragma mark - FBUserSettingsDelegate methods
+
+- (void)loginViewController:(id)sender receivedError:(NSError *)error{
+ if (error) {
+ UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error"
+ message:error.localizedDescription
+ delegate:nil
+ cancelButtonTitle:@"OK"
+ otherButtonTitles:nil];
+ [alertView show];
+ }
+}
+
#pragma mark UITableViewDataSource methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
@@ -1151,7 +1151,10 @@ - (void)completeWithResults:(NSArray *)results
if ([self isInvalidSessionError:itemError
resultIndex:error == itemError ? i : 0]) {
- [metadata.request.session closeAndClearTokenInformation];
+ [metadata.request.session closeAndClearTokenInformation:itemError];
+ if (metadata.request.session.loginType == FBSessionLoginTypeSystemAccount){
+ [FBSession renewSystemAuthorization];
+ }
} else if ([metadata.request.session shouldExtendAccessToken]) {
// If we have not had the opportunity to piggyback a token-extension request,
// but we need to, do so now as a separate request.
View
@@ -20,10 +20,12 @@
- (void)refreshAccessToken:(NSString*)token expirationDate:(NSDate*)expireDate;
- (BOOL)shouldExtendAccessToken;
+- (void)closeAndClearTokenInformation:(NSError*) error;
+ (FBSession*)activeSessionIfOpen;
+ (void)deleteFacebookCookies;
+ (NSDate*)expirationDateFromExpirationTimeString:(NSString*)expirationTime;
++ (void)renewSystemAuthorization;
@end
View
@@ -403,7 +403,10 @@ - (void)reauthorizeWithPublishPermissions:(NSArray*)writePermissions
- (void)close {
NSAssert(self.affinitizedThread == [NSThread currentThread], @"FBSession: should only be used from a single thread");
- FBSessionState state;
+ FBSessionState state;
+ if (self.state = FBSessionStateClosed) {
+ return;
+ }
if (self.state == FBSessionStateCreatedOpening) {
state = FBSessionStateClosedLoginFailed;
} else {
@@ -419,20 +422,7 @@ - (void)close {
}
- (void)closeAndClearTokenInformation {
- NSAssert(self.affinitizedThread == [NSThread currentThread], @"FBSession: should only be used from a single thread");
-
- [[FBDataDiskCache sharedCache] removeDataForSession:self];
- [self.tokenCachingStrategy clearToken];
-
- // If we are not already in a terminal state, go to Closed.
- if (!FB_ISSESSIONSTATETERMINAL(self.state)) {
- [self transitionAndCallHandlerWithState:FBSessionStateClosed
- error:nil
- token:nil
- expirationDate:nil
- shouldCache:NO
- loginType:FBSessionLoginTypeNone];
- }
+ [self closeAndClearTokenInformation:nil];
}
- (BOOL)handleOpenURL:(NSURL *)url {
@@ -622,6 +612,33 @@ + (NSString*)defaultAppID {
return g_defaultAppID;
}
+//calls ios6 renewCredentialsForAccount in order to update ios6's worldview of authorization state.
+// if not using ios6 system auth, this is a no-op.
++ (void)renewSystemAuthorization {
+ id accountStore = nil;
+ id accountTypeFB = nil;
+
+ if ((accountStore = [[[ACAccountStore alloc] init] autorelease]) &&
+ (accountTypeFB = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook] ) ){
+
+ NSArray *fbAccounts = [accountStore accountsWithAccountType:accountTypeFB];
+ id account;
+ if (fbAccounts && [fbAccounts count] > 0 &&
+ (account = [fbAccounts objectAtIndex:0])){
+
+ [accountStore renewCredentialsForAccount:account completion:^(ACAccountCredentialRenewResult renewResult, NSError *error) {
+ //we don't actually need to inspect renewResult or error.
+ if (error){
+ [FBLogger singleShotLogEntry:FBLoggingBehaviorAccessTokens
+ logEntry:[NSString stringWithFormat:@"renewCredentialsForAccount result:%d, error: %@",
+ renewResult,
+ error]];
+ }
+ }];
+ }
+ }
+}
+
#pragma mark -
#pragma mark Private Members
@@ -681,7 +698,7 @@ - (BOOL)transitionToState:(FBSessionState)state
loginType != FBSessionLoginTypeNone) {
self.loginType = loginType;
}
-
+
// invalid transition short circuits
if (!isValidTransition) {
[FBLogger singleShotLogEntry:FBLoggingBehaviorSessionStateTransitions
@@ -1666,6 +1683,23 @@ + (NSDate*)expirationDateFromExpirationTimeString:(NSString*)expirationTime {
return expirationDate;
}
+- (void)closeAndClearTokenInformation:(NSError*) error {
+ NSAssert(self.affinitizedThread == [NSThread currentThread], @"FBSession: should only be used from a single thread");
+
+ [[FBDataDiskCache sharedCache] removeDataForSession:self];
+ [self.tokenCachingStrategy clearToken];
+
+ // If we are not already in a terminal state, go to Closed.
+ if (!FB_ISSESSIONSTATETERMINAL(self.state)) {
+ [self transitionAndCallHandlerWithState:FBSessionStateClosed
+ error:error
+ token:nil
+ expirationDate:nil
+ shouldCache:NO
+ loginType:FBSessionLoginTypeNone];
+ }
+}
+
#pragma mark -
#pragma mark Debugging helpers

0 comments on commit 9aebe4c

Please sign in to comment.