ACAccountCredential returns null for oauthToken - ios

I am getting access to user's facebook via:
[accStore requestAccessToAccountsWithType:fbAccountType
options:options
completion:^(BOOL granted, NSError *error) {
if (granted) {
// If access granted, then get the Facebook account info
NSArray *accounts = [accStore
accountsWithAccountType:fbAccountType];
ACAccount *acc = [accounts lastObject];
// Get the access token, could be used in other scenarios
ACAccountCredential *fbCredential = [acc credential];
NSString *accessToken = [fbCredential oauthToken];
NSLog(#"Facebook Access Token: %#", accessToken);
// Add code here to make an API request using the SLRequest class
} else {
NSLog(#"Access not granted");
}
}];
This prints out:
Facebook Access Token: (null)
I am granted access, but the token is null. What am I doing wrong?
Thanks

I've solved the problem by renewing the credentials. That is, after access to FB has been granted, but in the case where ACAccountCredential's oauthToken returns nil, we just call ACAccountStore's renewCredentialsForAccount. Credentials are renewed, and after that the token is valid!

Here's some info from Apple doc:
#property(nonatomic, retain) ACAccountCredential *credential
Discussion This property is required and must be set before the
account is saved. For privacy reasons, this property is inaccessible
after the account is saved.
As in your code, [acc credential] will return null
For reference, http://developer.apple.com/library/ios/#documentation/Accounts/Reference/ACAccountClassRef/Reference/Reference.html#//apple_ref/doc/uid/TP40011019
Found a similar post here: Get Facebook access token from Social.framework(iOS6)
Hope this can help.

Related

Invalid token from Facebook ACAccount after removing app permission from Facebook Control Panel

I can correctly log into Facebook using the ACAccountStore account.
But if I delete the app from the Facebook Control Panel (https://www.facebook.com/settings?tab=applications), I get the following error when trying to get the user's friends :
{"error":
{"message":"Error validating access token: The user has not authorized
application XXXXXXXX.",
"type":"OAuthException","code":190,"error_subcode":458}}
If I delete the account from Settings, and re-login, the error goes away.
I'm using the following code to get the token, and use it in the requests. (snippet)
[self.store requestAccessToAccountsWithType:accountType
options:options
completion:^(BOOL granted, NSError *error) {
self.accounts = [self.store accountsWithAccountType:accountType];
NSMutableArray *availableAccounts = [NSMutableArray array];
self.account = self.accounts[0];
self.username = self.account.username;
self.userFullName = self.account.userFullName;
self.authToken = self.account.credential.oauthToken;
}]

How do I get the oauth token after logging into Twitter?

After logging into Twitter, I am able to print out some useful data such as the username and user ID. However, the OAuth token is always null. How do I get it? I need to send OAuth token to my server so it can verify that the user is indeed who he says he is.
ACAccountStore* accountStore = [[ACAccountStore alloc] init];
ACAccountType* twitterType = [self.context.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore
requestAccessToAccountsWithType:twitterType
withCompletionHandler:^(BOOL isAllowed, NSError* error) {
dispatch_sync(dispatch_get_main_queue(), ^(void) {
if (isAllowed) {
ACAccount* account = [[self.context.accountStore accountsWithAccountType:[self.context.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter]] lastObject];
NSLog(#"username = %#", account.username);
NSLog(#"user_id = %#", [[account valueForKey:#"properties"] valueForKey:#"user_id"]);
// ouath token is always null
NSLog(#"oauth token = %#", account.credential.oauthToken);
}
});
}
];
I "think" I need Reverse Auth, but that tutorial mysteriously left out the code for "step 1".
You will indeed need to use Reverse Auth.
I recently used Sean Cook's TWReverseAuth and it worked very well. Just be careful to turn off ARC for the individual files in the Vendor directory.

Get Facebook access token from Social.framework(iOS6)

I need to retrieve Facebook access token of my system account, which I set in Settings application.
I know that Social.framework(iOS6) knows all my FB account information & I can perform API calls to Graph API using SLRequest class, like in this post http://blogs.captechconsulting.com/blog/eric-stroh/ios-6-tutorial-integrating-facebook-your-applications
But, my question is - how can I retrieve my Facebook system account's access token?
I found solution for Twitter https://dev.twitter.com/docs/ios/using-reverse-auth , but could you help me with Facebook
If you already know how to get the account store set up, here's code around it showing how to get the access token:
#property (strong, nonatomic) ACAccountStore *accountStore;
#property (strong, nonatomic) ACAccount *fbAccount;
...
#synthesize accountStore = _accountStore;
#synthesize fbAccount = _fbAccount;
...
// Initialize the account store
self.accountStore = [[ACAccountStore alloc] init];
// Get the Facebook account type for the access request
ACAccountType *fbAccountType = [self.accountStore
accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
// Request access to the Facebook account with the access info
[self.accountStore requestAccessToAccountsWithType:fbAccountType
options:fbInfo
completion:^(BOOL granted, NSError *error) {
if (granted) {
// If access granted, then get the Facebook account info
NSArray *accounts = [self.accountStore
accountsWithAccountType:fbAccountType];
self.fbAccount = [accounts lastObject];
// Get the access token, could be used in other scenarios
ACAccountCredential *fbCredential = [self.fbAccount credential];
NSString *accessToken = [fbCredential oauthToken];
NSLog(#"Facebook Access Token: %#", accessToken);
// Add code here to make an API request using the SLRequest class
} else {
NSLog(#"Access not granted");
}
}];

When do we use saveAccount:(ACAccount *)account of ACAccountStore class?

Please clarify me when do we use this method ?
- (void)saveAccount:(ACAccount *)account withCompletionHandler:(ACAccountStoreSaveCompletionHandler)completionHandler
As I know we get access to the account through OAuth and we do not get user's credentials. So how do we create an account? I've found that ACAccount has the only one creation method:
- (id)initWithAccountType:(ACAccountType *)type
What actually happens when we create account this way? And can we save it now ?
Ok, finally I've found the information about it.
Consider this scenario:
Our app have already been authorized by the user and we've got both access token and secret. Now we want to support new iOS 6 features and create twitter (for example) account in Settings. To do this we need to migrate these tokens to the central account store.
Here's how:
- (void)storeAccountWithAccessToken:(NSString *)token secret:(NSString *)secret {
//We start creating an account by creating the credentials object
ACAccountCredential *credential = [[ACAccountCredential alloc] initWithOAuthToken:token tokenSecret:secret];
ACAccountType *twitterAcctType =[self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
ACAccount *newAccount = [[ACAccount alloc] initWithAccountType:twitterAcctType];
//Here we assign credentials and now can save account
newAccount.credential = credential;
[self.accountStore saveAccount:newAccount withCompletionHandler:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"the account was saved!");
}
else {
//Handle error here
}
}];
}
For more information about it read here how to migrate tokens

Expired access token after openActiveSession for Facebook iOS SDK

I'm using the 3.1 Facebook SDK with iOS 6 Facebook set up in Settings and my app authorized.
This executes flawlessly:
[FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *fbSession, FBSessionState fbState, NSError *error) { ... }
However now when I try to get 'me' information I'm getting an error:
com.facebook.sdk:ParsedJSONResponseKey = {
body = {
error = {
code = 190;
"error_subcode" = 463;
message = "Error validating access token: Session has expired at unix time 1348704000. The current unix time is 1348706984.";
type = OAuthException;
};
};
code = 400;
}
If I look at [error code] it's equal to 5. Shouldn't I have a valid access token after just logging in? Do I need to call reauthorize?
UPDATE: Reauthorizing doesn't help. Oddly the accessToken for my activeSession is always coming back the same. This despite calling closeAndClearToken.
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);
}
}];
}
So it's addressed, but I've been calling /me from our backend to verify since you can't trust the device.
So I make a call to FBSession's + (void)renewSystemAuthorization when the backend comes back with an authorization error.

Resources