Facebook iOS SDK Login Fails On First Attempt but Successful on Second - iphone

Ok, so this has been a tough one for me to debug because I do not experience this problem that many users of my app appear to be experiencing. If you would like to try it yourself, you may download the app on iTunes by searching for "syncquik".
PROBLEM: When users attempt to login to my app via the Facebook login button, the first attempt to open an active FB session often fails. At first, I thought this was because they had to turn on the app's Facebook privacy settings in the iPhone's general settings. However, because so many are having the same issue, I am beginning to wonder if it's because of an error in my code (see below)? I followed the demo example and Facebook instructions, but perhaps I missed something? Thanks in advance for any help you can offer!
CODE:
(In ViewController holding FB button):
- (IBAction)fbSignIn:(id)sender
{
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate openSession];
}
(In AppDelegate.m):
- (void)sessionStateChanged:(FBSession *)session
state:(FBSessionState) state
error:(NSError *)error
{
NSLog(#"FB Session State Changed");
switch (state) {
case FBSessionStateOpen: {
NSLog(#"FBSessionStateOpen");
if (fbButtonPushed && FBSession.activeSession.isOpen) {
[self sendUserFbInfo];
fbButtonPushed = NO;
}
}
break;
case FBSessionStateClosed:
NSLog(#"FBSessionStateClosed");
break;
case FBSessionStateClosedLoginFailed:
NSLog(#"FBSessionStateClosedLoginFailed");
[FBSession.activeSession closeAndClearTokenInformation];
[self showLoginView];
break;
default:
break;
}
if (error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Please Try Again" message:#"If second attempt is unsuccessful, then set SyncQuik to ON in Settings > Facebook." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
[[NSNotificationCenter defaultCenter] postNotificationName:#"com.syncquik.connectionrequest.complete" object:nil];
}
}
- (void)openSession
{
NSLog(#"Opening a User's FB Session");
fbButtonPushed = YES;
NSArray *permissons = [[NSArray alloc] initWithObjects:#"email", nil];
[FBSession openActiveSessionWithReadPermissions:permissons
allowLoginUI:YES
completionHandler:
^(FBSession *session,
FBSessionState state, NSError *error) {
[self sessionStateChanged:session state:state error:error];
}];
}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
return [FBSession.activeSession handleOpenURL:url];
}
- (void)showLoginView
{
NSLog(#"Show Login View");
//Anything else need to be placed here?
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[FBSession.activeSession handleDidBecomeActive];
if (fbButtonPushed && FBSession.activeSession.isOpen) {
[self sendUserFbInfo];
fbButtonPushed = NO;
}
}

if (self.session.state != FBSessionStateCreated) {
// Create a new, logged out session.
[self openSession];
}
else{
[FBSession setActiveSession:self.session];
[self sessionStateChanged:session state:state error:error];
}
try this code in openActiveSessionWithReadPermissions block

Related

Facebook login in iOS app for multiple users on one device

My app will be used by multiple users.
How can I integrate facebook in a way so that the safari doesn't save any user credentials & throw a login page each time some one tries to login.
I already used FBSessionLoginBehaviorForcingWebView to force a web view login within the app but the problem is, when the first user tries to login to fb via the view presented within the app, the control then goes to safari for authentication & it just saves the creds.
So when other user tries to log in, & enters his credentials in the app local UIWebView, the control again goes to safari, which already has previos creds stored & the new user just cannot authenticate.
So basically the first user is permanently logged in. Clearing the cookies for web view doesn't work. I cant clear them for safari.
Help
Thanks in advance.
This is what I have been doing
if([FBSession activeSession].isOpen)
{
[[FBSession activeSession] closeAndClearTokenInformation];//Also, clearing all the local cookies
}
else
{
AppDelegate *delegate = [UIApplication sharedApplication].delegate;
delegate.session = [[FBSession alloc] init];
[FBSession setActiveSession:delegate.session];
[delegate.session openWithBehavior:FBSessionLoginBehaviorForcingWebView completionHandler:^(FBSession *session1, FBSessionState status, NSError *error) {
if (!error) {
[self openSession]; // your method
}
}];
}
- (void)sessionStateChanged:(FBSession *)session state:(FBSessionState) state error:(NSError *)error{
switch (state) {
case FBSessionStateOpen:
{
[FBRequestConnection startForPostStatusUpdate:#"Some message"completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
//logout again
NSLog(#"startForPostStatusUpdate SUCESS");
}
else {
//logout again
NSLog(#"startForPostStatusUpdate FAIL");
}
}];
}
break;
case FBSessionStateClosed:
{
NSLog(#"FBSessionStateClosed");
}
break;
case FBSessionStateClosedLoginFailed:
[FBSession.activeSession closeAndClearTokenInformation];
break;
default:
break;
}
if (error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error" message:error.localizedDescription delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
}
Also, modified the app delegate with your methods.
Found a solution. Basically, calling the following:
[FBSession.activeSession closeAndClearTokenInformation];
[FBSession.activeSession close];
[FBSession setActiveSession:nil];
Only clears the local FB session information but not the Safari cookies. So, after I log in the user, I clear the Safari cookies:
NSHTTPCookie *cookie;
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (cookie in [storage cookies]) {
[storage deleteCookie:cookie];
}
[[NSUserDefaults standardUserDefaults] synchronize];
It isn't very elegant, but it works. Now, when the next user tries to log in, it forces them to enter their credentials.
You just need to use this method after you want to close the FB Session.
[FBSession.activeSession closeAndClearTokenInformation];
This will clear the Facebook Token, and whenever button will be pressed for Facebook share, it will ask for new token credential and it will show Facebook login view again.
Use following code in AppDelegate
// This method will handle facebook session.
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
// Facebook SDK * login flow *
// Attempt to handle URLs to complete any auth (e.g., SSO) flow.
return [FBAppCall handleOpenURL:url sourceApplication:sourceApplication fallbackHandler:^(FBAppCall *call) {
// Facebook SDK * App Linking *
// For simplicity, this sample will ignore the link if the session is already
// open but a more advanced app could support features like user switching.
if (call.accessTokenData) {
if ([FBSession activeSession].isOpen) {
NSLog(#"INFO: Ignoring app link because current session is open.");
}
else {
[self handleAppLink:call.accessTokenData];
}
}
}];
}
// Helper method to wrap logic for handling app links.
- (void)handleAppLink:(FBAccessTokenData *)appLinkToken {
// Initialize a new blank session instance...
FBSession *appLinkSession = [[FBSession alloc] initWithAppID:nil
permissions:nil
defaultAudience:FBSessionDefaultAudienceNone
urlSchemeSuffix:nil
tokenCacheStrategy:[FBSessionTokenCachingStrategy nullCacheInstance] ];
[FBSession setActiveSession:appLinkSession];
// ... and open it from the App Link's Token.
[appLinkSession openFromAccessTokenData:appLinkToken
completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
// Forward any errors to the FBLoginView delegate.
if (error) {
//TODO: Show error here
}
}];
}
Add following code where you are opening session for Facebook
/***********************************************************************
This method opens browser or App Url to FB authentication and change FB
session state
************************************************************************/
- (void)openSession
{
NSArray *permisssions = [[NSArray alloc] initWithObjects:#"email",#"user_photos",nil];
[FBSession openActiveSessionWithReadPermissions:permisssions
allowLoginUI:YES
completionHandler:
^(FBSession *session,
FBSessionState state, NSError *error) {
[self sessionStateChanged:session state:state error:error];
}];
}
/***********************************************************************
This method will be called whenever FB Session state changed
It also shows an error message if any error occurs
************************************************************************/
- (void)sessionStateChanged:(FBSession *)session
state:(FBSessionState) state
error:(NSError *)error
{
switch (state) {
case FBSessionStateOpen: {
NSLog(#"FBSession Open State");
//Enter your code what you want to perform when session is open
}
break;
case FBSessionStateClosed:
case FBSessionStateClosedLoginFailed:
// Once the user has logged in, we want them to
// be looking at the root view.
NSLog(#"FBSession State Closed");
[FBSession.activeSession closeAndClearTokenInformation];
[FBSession.activeSession close];
[FBSession setActiveSession:nil];
break;
default:
break;
}
if (error) {
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:#"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
}
Now when ever user tap on Facebook Share button check that session exists or not. If not then call openSession method. It will open facebook login view.
There is a sample in the Facebook Github repository, showing how to switch between multiple users.
It looks like it requires use of the custom login flow.

how to check if already logged in Facebook in ios?

I am using "Graph Api" For Facebook login with in my app. I successfully logIn and logged out with this code
facebook = [[Facebook alloc] initWithAppId:kAppId andDelegate:self.viewController];
and i using below code for already facebook logged in
if (![facebook isSessionValid]) {
[facebook authorize:permissions ];
}
My problem is i install Facebook App, and i logged in via Facebook App. then i run my app, it ask again login Facebook. how i solve this issue. it should take as a default FB app login credentials. i saw many app using this feature. anyone know the solution
1. In your AppDelegate.m file
define NSString *const FBSessionStateChangedNotification =
#"com.example:FBSessionStateChangedNotification";
replace com.example with your bundle identifier name
2.- In method
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[FBSession setActiveSession:[FBSession new]];
if ([FBSession activeSession].state == FBSessionStateCreatedTokenLoaded)
{
// means u are already logged in
//do ur code
[self openSessionWithAllowLoginUI:NO];
}
else
{ //do ur code
[self openSessionWithAllowLoginUI:YES];
}
}
3. whenenever and wherever this method is called after end of switch case write
- (void)sessionStateChanged:(FBSession *)session
state:(FBSessionState) state
error:(NSError *)error
{
[[NSNotificationCenter defaultCenter]
postNotificationName:FBSessionStateChangedNotification
object:session];
}
- (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI
{
return [FBSession openActiveSessionWithReadPermissions:#"email"
allowLoginUI:allowLoginUI
completionHandler:^(FBSession *session, FBSessionState state, NSError *error)
{
[self sessionStateChanged:session state:state error:error];
}];
}
Sorry in your appDelegate.h file it should be
extern NSString *const FBSessionStateChangedNotification;

iOS native Facebook Login fails for Mobile Safari authentication

I have a native iOS app with Facebook Login integrated. The login flow works when the native Facebook app is installed.
However, when I delete the app, the FB login fallback on FB Mobile Safari webapp. The app switching works fine, but when controls come back to my app, the state of the session is declared FBSessionStateClosedLoginFailed.
I am using Facebook SDK v3.5.2 as of June 6th, 2013
Using iPad1 iOS5.1.1
/*
* Opens a Facebook session and optionally shows the login UX.
*/
- (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI {
NSArray *permissions = [[NSArray alloc] initWithObjects:
#"email",
#"publish_actions",
nil];
BOOL result = NO;
FBSession *session =
[[FBSession alloc] initWithAppID:nil
permissions:permissions
urlSchemeSuffix:#"<MySchemeSuffix>"
tokenCacheStrategy:nil];
if (allowLoginUI ||
(session.state == FBSessionStateCreatedTokenLoaded)) {
[FBSession setActiveSession:session];
[session openWithBehavior:FBSessionLoginBehaviorWithFallbackToWebView
completionHandler:
^(FBSession *session, FBSessionState state, NSError *error)
{
[self sessionStateChanged:session state:state error:error];
}];
result = session.isOpen;
}
return result;
}
/*
* If we have a valid session at the time of openURL call, we handle
* Facebook transitions by passing the url argument to handleOpenURL
*/
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
// attempt to extract a token from the url
//return [FBSession.activeSession handleOpenURL:url];
BOOL urlWasHandled = [FBAppCall handleOpenURL:url
sourceApplication:sourceApplication
fallbackHandler:^(FBAppCall *call) {
// handle deep links and responses for Login or Share Dialog here
}];
return urlWasHandled;
}
/*
* Callback for session changes.
*/
- (void)sessionStateChanged:(FBSession *)session
state:(FBSessionState) state
error:(NSError *)error
{
switch (state) {
case FBSessionStateOpen:
if (!error) {
if (FBSession.activeSession.isOpen) {
[[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
//Application specific code goes here.
}
}];
}
// We have a valid session
NSLog(#"User session found");
}
break;
case FBSessionStateClosed:
case FBSessionStateClosedLoginFailed:
[FBSession.activeSession closeAndClearTokenInformation];
break;
default:
break;
}
}
I happened to fix the issue.
The bug was, I was requesting read and write permissions in the same call.
Taking out the "publish_actions" from permissions solved the issue.
I tested Facebook Login in iOS6+ through native FB login, native FB app, and webapp.
Also tested on iOS5.1.1 through native FB app, and webapp.

Facebook authentication doesnot auto closes

I am trying to integrating Facebook-Connect SDK to a cocos2d-x game on iOS platform.
I am using the api,
[FBSession openActiveSessionWithReadPermissions:nil
allowLoginUI:TRUE
completionHandler:^(FBSession *session, FBSessionState state, NSError *error)
{
if(state == FBSessionStateOpen)
NSLog(#"Success");
}
Dialog box doesn't close on pressing okay, however the completionHandler executes successfully with FBSessionStateOpenstate.
I see you tagged this question iOS 6 so I'm assuming you are developing this game for iOS 6 devices which you'll be able to use iOS 6 integration with Facebook.
I explained how i did it once (https://stackoverflow.com/a/14151853/1449151) but I put it here anyway. If you like to use iOS 6 Facebook integration you could follow these steps to make sure you did all of them right. (for instance I see your permission is nil, see how i did it below). Hope it helps:
OK here is how I do this integration with iOS 6 and get what I want from Facebook:
In AppDelegate I do this:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [FBSession.activeSession handleOpenURL:url];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[FBSession.activeSession handleDidBecomeActive];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
[FBSession.activeSession close];
}
and in my ViewController where I want to retrieve information about myself or my friends I do this (NOTE: this is a test, so it is a lot of permissions!):
NSArray *permissions =
[NSArray arrayWithObjects:#"email", #"user_location",
#"user_birthday",
#"user_likes", #"user_interests",#"friends_interests",#"friends_birthday",#"friends_location",#"friends_hometown",#"friends_photos",#"friends_status",
#"friends_about_me", #"friends_birthday", #"friends_hometown", #"friends_interests", #"friends_likes", #"friends_location", nil];
[FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
/* handle success + failure in block */
if (status) {
NSLog(#"Facebook Read Permission is successful!");
[self presentPostOptions];
// [self presentPostOptions];
}
}];
Then in "presentPostOptions" I do this (in this example I try to retrieve something from my friend):
- (void)presentPostOptions
{
[[FBRequest requestForMyFriends] startWithCompletionHandler:^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error)
{
if (!error) {
NSArray *data = [user objectForKey:#"data"];
NSLog(#"%d", [data count]);
for (FBGraphObject<FBGraphUser> *friend in data) {
NSLog(#"%#", [friend first_name]);
NSLog(#"%#", [friend last_name]);
NSLog(#"%#", [friend id]);
//make sure you have FBProfilePictureView outlet in your view
//otherwise skip the profile picture!
self.fbProfilePic.profileID = #"you'r friend's profile.id";
}
}
else
{
NSLog(#"error");
// [self didFailWithError:error];
}
}];
I don't know what else you want to do because in your question you just tried to make a connection but this way you can do what ever you want while you are integrated in iOS 6.
One more thing, make sure about your App Settings over Facebook and the configs over there like enable your app for iOS and the ID for iPhone/iPad. Also the FacebookAppID in your plist.
Let me know if it works out for you,
EDIT: My Facebook SDK is 3.1.1 btw.

FBSession often fails to be at FBSessionStateCreatedTokenLoaded when returning to foreground

I am using Facebook iOS SDK3.0 (client is not ready to move to 3.1 yet), and have been having trouble to get FBSession to be at the FBSessionStateCreatedTokenLoaded when the app returns to the foreground after a longer period of time (close to a day); I don't have issue if I just send the app to background and come back to the foreground after a minute or two
my code is as follow, please let me know if I should provide further information; I have this issues across devices (4, 4S, 5) and iOS version (4.3, 5.0, 5.1, 6.0)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
if (![self openSessionWithAllowLoginUI:NO]) {
// still allow loading the public feed even though user is not logged in (the view controller will have a login button overlay)
[[self pictureFeedVC] loadFeed];
}
...
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
DEBUGLog(#"didBecomeActive");
DEBUGLog(#"FBSession is Open? %#",FBSession.activeSession.isOpen ? #"YES" : #"NO");
if (FBSession.activeSession.state == FBSessionStateCreatedOpening) {
//Dismiss login view controller
if ([self.tabBarController modalViewController]) {
[self.tabBarController dismissModalViewControllerAnimated:NO];
}
[FBSession.activeSession close];
}
}
- (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI {
BOOL result = NO;
[FBSettings setLoggingBehavior:[NSSet setWithObjects:FBLoggingBehaviorSessionStateTransitions,nil]];
NSArray *permissions = [NSArray arrayWithObjects:#"publish_actions", #"email", nil];
FBSession *session = [[FBSession alloc] initWithAppID:nil permissions:permissions urlSchemeSuffix:FB_URL_SCHEME_SUFFIX tokenCacheStrategy:nil];
if (allowLoginUI || (session.state == FBSessionStateCreatedTokenLoaded)) {
[FBSession setActiveSession:session];
[session openWithCompletionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
[self sessionStateChanged:session state:state error:error];
}];
result = session.isOpen;
}
return result;
}
- (void)sessionStateChanged:(FBSession *)session state:(FBSessionState)state error:(NSError *)error
{
DEBUGLog(#"changed FB state");
switch (state) {
case FBSessionStateOpen:
if (!error) {
DEBUGLog(#"FBSessionStateOpen");
[[self pictureFeedVC] loadFeed];
}
break;
case FBSessionStateClosed:
DEBUGLog(#"FBSessionStateClosed");
case FBSessionStateClosedLoginFailed:
DEBUGLog(#"FBSessionStateClosedLoginFailed");
[session closeAndClearTokenInformation];
[self.tabBarController showFBLoginView];
break;
default:
break;
}
}
I am having problem where after extended period in the background, the DEBUGLog(#"FBSession is Open? %#",FBSession.activeSession.isOpen ? #"YES" : #"NO"); in applicationDidBecomeActive: will print a NO
more info: this app relies on Facebook login entirely, it uses facebook user ID to identify users, therefore for lots of actions I need to pass the Facebook access token so that the server can get the facebook user ID even though a lot of times those actions does not require api calls to facebook (besides getting the user ID from the access token of course). My other question is whether I need to actively do something to refresh the token or do something keep the FBSession open (if so, how?), would that be the reason why the FBSession.activeSession might not be in an open state when I return to foreground?
Thanks much in advance!