My app is iOS7 only, and in the interest of saving headaches I decided against using the Facebook SDK for authentication and instead rely on iOS's ACAccountStore. Unfortunately I'm still getting headaches.
I have a class called FacebookService with one method called login
. I'll post the code below, but in general I do the standard dance of configuring an instance of ACAccountStore
and then call requestAccessToAccountsWithType
. In the completion block, if granted
returns false I message the user and short-circuit.
If granted
is true, then my code used to make the assumption that I had everything needed to obtain the user's oauthToken using the following code:
ACAccount *fbAccount = [[accountStore accountsWithAccountType:fbAccountType] lastObject];
ACAccountCredential *fbCredential = [fbAccount credential];
NSString *accessToken = [fbCredential oauthToken];
I get the oauthToken
from the ACAccount#credential
, and the first thing that's weird is that according to the documentation:
For privacy reasons, this property (is inaccessible after the account is saved.
I can definitely tell you that the majority of the time, this property is absolutely accessible and I can use it to send to my server to find a previously authenticated user.
However, a handful of times, the value will in fact be null. And in every case where this happens, it can be resolved if the user goes to Settings > Facebook > Turn our app off > Turn our app on. They go back to our app, tap "Sign in with Facebook" and everything works perfectly.
So here are my questions:
- Is there any way to consistently get a user's oauthToken from their FB ACAccount? As you'll see in my code, I now check first whether or not that value is empty, and if it is, I throw them an alert.
- What does it say that the user can fix this by going into Settings, disabling then enabling my app? Is their account out of sync or do I need to renew credentials?
- Is there a way to recover within the my method if I'm told that the access has been granted but I fail to receive an oauthToken?
- Does anybody know of a way I can set my system up so I can reliably reproduce this?
Here's my code:
- (void)login
{
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *fbAccountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSString *appId = [[Environment sharedInstance] fbAppId];
NSDictionary *fbOptions = @{
ACFacebookAppIdKey : appId,
ACFacebookPermissionsKey : [@"email,user_birthday,user_hometown,user_location,user_photos" componentsSeparatedByString:@","],
ACFacebookAudienceKey: ACFacebookAudienceEveryone
};
[accountStore requestAccessToAccountsWithType:fbAccountType options:fbOptions completion:^(BOOL granted, NSError *error) {
NSString *message;
NSString *title;
if (!granted) {
if (!error || error.code == ACErrorPermissionDenied) {
title = @"AFAR Needs Your Permission";
message = @"Your Facebook account is configured in Settings. Go to Settings and enable AFAR.";
} else if (error.code == ACErrorAccountNotFound) {
title = @"No Facebook Account";
message = @"There are no Facebook accounts configured. You can add or create a Facebook account in Settings.";
}
if (message) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AfarFacebookLoginDidNotSucceed object:nil];
[UIAlertView simpleAlertWithTitle:title message:message];
});
}
return;
}
ACAccount *fbAccount = [[accountStore accountsWithAccountType:fbAccountType] lastObject];
ACAccountCredential *fbCredential = [fbAccount credential];
NSString *accessToken = [fbCredential oauthToken];
if ([accessToken isEmpty]) {
message = @"You need to allow AFAR access to your Facebook account. Please visit Settings > Facebook and look for the AFAR entry. Turn the AFAR setting off, then back on.";
title = @"Problem Connecting to Facebook";
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AfarFacebookLoginDidNotSucceed object:nil];
[UIAlertView simpleAlertWithTitle:title message:message];
});
return;
}
[SessionAPIService signInUsingFacebookToken:accessToken withBlock:^(NSError *error) {
if (!error) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFARCurrentUserDidSignInNotificatioName object:nil];
}
}];
}];
}