Facebook Sdk 3.1 - Error Validating Access Token

Facebook SDK 3.1 - Error validating access token

The Facebook account on the device has become out-of-sync with the server as well as with the App's/SDK's cache. This can be solved by calling the ACAccountStore method renewCredentialsForAccount, which will update the OS's understanding of the token state.

In the next update of the SDK, the SDK will automatically call this API when it receives a response from the server indicating that a token has become invalid. For the 3.1.0 revision of the SDK, applications will need to explicitly call this API. Here is a code sample:

ACAccountStore *accountStore;
ACAccountType *accountTypeFB;
if ((accountStore = [[ACAccountStore alloc] init]) &&
(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){

}
}];
}
}

There are several options for where/when to call the API. The simplest place would be to opportunistically make the call on application launch, or on view load. One problem with this approach is that it will cause a network round-trip that is often unnecessary. Another option is to call it when a session change notification occurs, indicating that a session has closed. Also many applications fetch some basic information such as graph.facebook.com/me, at application launch time, and if so -- a call to this method in case of an error response may be a reasonable place to ask iOS to update its token status.

Hopefully this helps!

Random Facebook iOS SDK auth token invalidated by API

I've been experiencing the same issue. It happens with any graph request after calling authorize immediately after a successful logout callback (with the idea being that a different user can log in from there).

The authorization screen will say that the user's already accepted the permissions (even though they're supposed to be logged out now...) and if they hit ok it will cause the issue. Almost as if the auth process reissues an invalidated token because it doesn't get the memo that they've logged out. However it won't happen if the user hits the "not you?" link and logs in as someone else as intended (or if they log in again as the same user), so this isn't a major issue in my case.

As for an answer / fix, I made mine fix itself by detecting the error response from the graph call and then making another call to authorize. Not ideal though, since it annoys the user with two consecutive app switches...

Expired access token after openActiveSession for Facebook iOS SDK


UPDATE:
This issue has been addressed in Facebook iOS SDK 3.1.1.


I synched the code off of github and found that they weren't calling accountStore renewCredentialsForAccount:completion: anywhere. I changed the following code in authorizeUsingSystemAccountStore and it seems to have resolved the issue.

// we will attempt an iOS integrated facebook login
[accountStore requestAccessToAccountsWithType:accountType
options:options
completion:^(BOOL granted, NSError *error) {

// this means the user has not signed-on to Facebook via the OS
BOOL isUntosedDevice = (!granted && error.code == ACErrorAccountNotFound);

dispatch_block_t postReauthorizeBlock = ^{
NSString *oauthToken = nil;
if (granted) {
NSArray *fbAccounts = [accountStore accountsWithAccountType:accountType];
id account = [fbAccounts objectAtIndex:0];
id credential = [account credential];
oauthToken = [credential oauthToken];
}

// initial auth case
if (!isReauthorize) {
if (oauthToken) {
_isFacebookLoginToken = YES;
_isOSIntegratedFacebookLoginToken = YES;

// we received a token just now
self.refreshDate = [NSDate date];

// set token and date, state transition, and call the handler if there is one
[self transitionAndCallHandlerWithState:FBSessionStateOpen
error:nil
token:oauthToken
// BUG: we need a means for fetching the expiration date of the token
expirationDate:[NSDate distantFuture]
shouldCache:YES
loginType:FBSessionLoginTypeSystemAccount];
} else if (isUntosedDevice) {
// even when OS integrated auth is possible we use native-app/safari
// login if the user has not signed on to Facebook via the OS
[self authorizeWithPermissions:permissions
defaultAudience:defaultAudience
integratedAuth:NO
FBAppAuth:YES
safariAuth:YES
fallback:YES
isReauthorize:NO];
} else {
// create an error object with additional info regarding failed login
NSError *err = [FBSession errorLoginFailedWithReason:nil
errorCode:nil
innerError:error];

// state transition, and call the handler if there is one
[self transitionAndCallHandlerWithState:FBSessionStateClosedLoginFailed
error:err
token:nil
expirationDate:nil
shouldCache:NO
loginType:FBSessionLoginTypeNone];
}
} else { // reauth case
if (oauthToken) {
// union the requested permissions with the already granted permissions
NSMutableSet *set = [NSMutableSet setWithArray:self.permissions];
[set addObjectsFromArray:permissions];

// complete the operation: success
[self completeReauthorizeWithAccessToken:oauthToken
expirationDate:[NSDate distantFuture]
permissions:[set allObjects]];
} else {
// no token in this case implies that the user cancelled the permissions upgrade
NSError *error = [FBSession errorLoginFailedWithReason:FBErrorReauthorizeFailedReasonUserCancelled
errorCode:nil
innerError:nil];
// complete the operation: failed
[self callReauthorizeHandlerAndClearState:error];

// if we made it this far into the reauth case with an untosed device, then
// it is time to invalidate the session
if (isUntosedDevice) {
[self closeAndClearTokenInformation];
}
}
}
};



if (granted) {
[accountStore renewCredentialsForAccount:[[accountStore accountsWithAccountType:accountType] lastObject] completion:^(ACAccountCredentialRenewResult renewResult, NSError *error) {
dispatch_async(dispatch_get_main_queue(), postReauthorizeBlock);
}];
} else {
// requestAccessToAccountsWithType:options:completion: completes on an
// arbitrary thread; let's process this back on our main thread
dispatch_async(dispatch_get_main_queue(), postReauthorizeBlock);
}

}];

}

Handle invalid accessToken with FBSession openActiveSessionWithReadPermissions in Facebook iOS 3.1.1 SDK

The questions you linked are relevant, especially Facebook SDK 3.1 - Error validating access token which explains the problem where the Facebook account on the device is out of sync with the server (I.e., if you deleted the app from App Center). As mentioned there, in 3.1.1 the SDK will call to renew the device token only when it gets the invalid response from the server. This is a trade off in convenience for less round-trips to the server.

Assuming your code block is executed on applicationDidFinishLaunching or something similar, it will go to the else block because the app starts with a new session. When it calls openActiveSessionWithReadPermissions, the iOS 6 device thinks the token is valid and will let the state go to Open, so then your "do something" gets executed. Only then does the SDK get the invalid response from the server and invalidate the device token. As a result, the next time the procedure is called, it will prompt the user appropriately to authorize again.

This is intentional. For now, you can consider a automatic retry in your application if the error code describes an invalid token. For example, see the Scrumptious sample postOpenGraph retry code. In your case, it may look closer to something like (I used requestForMe as the "do something" for demonstration purposes):

else {
[FBSessionopenActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
if(FB_ISSESSIONOPENWITHSTATE(status)) {
//do something
[[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error){
NSLog(@"success on first try");
} else if ([[error userInfo][FBErrorParsedJSONResponseKey][@"body"][@"error"][@"code"] compare:@190] == NSOrderedSame) {
//requestForMe failed due to error validating access token (code 190), so retry login
[FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
if (!error){
//do something again, or consider recursive call with a max retry count.
NSLog(@"success on retry");
}
}];
}
}];
}
}];
}

iOS 6 Facebook Login not refreshing access token

This isn't the type of fix I was after, but things look to be working as expected, so at this point I just need to ship.

First things first, it turns out that old versions of our app were requesting offline_access. I did not know that. I went in to my FB app's Settings > Advanced, and enabled "Remove offline_access permission." I am now getting back a token that lasts two months (check out the facbeook token debugger).

Now that I'm getting back the correct kind of token, I still have a problem with getting back a valid token when it has been deactivated/expired. Since I'm now getting back a 2 month token, I can't wait for that to expire, so my plan was to trigger an OAuthException by going into my account's settings and deauthorizing it.

In my appDidBecomeActive, I put [FBRequestConnection startForMeWithCompletionHandler:nil] to simply trigger a silent request to FB, and when that fails because of an invalid session/token, the code flow runs through the correct path and [FBSession renewSystemAuthorization] ends up getting called, which is ultimately what I was needing. What this allows iOS to do is the next time I go to Log in to my app through FB, iOS prompts me again, understanding I don't have a valid token anymore. Calling [FBSession renewSystemAuthorization] effectively tells iOS that the next time my app requests access, hit facebook and give me back a new/refreshed token.

For the most part everything is working as expected now.

Facebook SDK 3.1.1 on iOS 6 logs in but token instantly expires

that state is specific to this condition:

 /*! One of two initial session states indicating that a cached token was loaded;
when a session is in this state, a call to open* will result in an open session,
without UX or app-switching*/
FBSessionStateCreatedTokenLoaded = 1,

Since the "success" NSLog is executed in the code you posted, your state is actually: FBSessionStateOpen. Which means you are logged in and have a valid access token (log out FBSession.activeSession.accessToken to validate). Anyway, I believe you should be good - your loggedIn/notLoggedIn condition was just invalid.



Related Topics



Leave a reply



Submit