FBSession.openActiveSessionWithReadPermissions not calling completionHandler - ios

I'm implementing OAuth login using Facebook SDK, but the FBSession.openActiveSessionWithReadPermissions:allowLoginUI:completionHandler method is not calling the completionHandler nor is it returning "true", which would indicate a cached token. Everything else seems to work fine, except this. What's wrong with this code?
var completionHandler: FBSessionStateHandler = {
session, status, error in
NSLog("token not cached");
if error {
// Login failed for some reason, so we notify the delegate.
self.delegate.didFailOpenningSession?(self, withError: ErrorUtility.error(
code: .FailedOpenningSession,
withFacebookError: error
));
// Don't proceed on errors
return;
}
// Session is now open, we should notify the delegate
self.delegate.didOpenSession?(self);
};
NSLog("here");
// Open the session, prompting the user if necessary. This might send the application to the background
if FBSession.openActiveSessionWithReadPermissions(["public_profile"], allowLoginUI: true, completionHandler: completionHandler) {
NSLog("Token cached");
// If we end up here, the session token must exist, so we now have an open session
self.delegate.didOpenSession?(self);
}
Edit: I forgot to mention that the NSLog logs "here", but not "Token cached" nor "token not cached"
Edit: Now things got a bit stranger. When my app launches I show a screen with a Facebook login button. When the user presses the button the code above gets triggered. After I authorise the app, the app gets back to the same screen (it shouldn't, but I can't do it any other way until the completion handler gets called). Then I tried pressing the button again and now I do get the logs.
This is what I get:
here
token not cached
here
The first line is from the first button press, the other two appear when I press it the second time. Now what's so odd about it? Shouldn't "token not cached" and "here" be reversed??? Is it calling the competionHandler from the last call or something?
Nevermind, that much is fixed.
Edit: one other thing. If I press the button multiple times, I always get "token not cached". I think it should be caching

It's solved. I was missing the method AppDelegate.application:handleOpenURL -> Bool

First ensure that you are using correct closure syntax. You are not providing a matching closure callback for the block callback.
notice the block callback for the FB session is:
^(FBSession *session,
FBSessionState state, NSError *error)
You should have
/*function parameters*/){ session, state, error in
//closure declaration goes at the top after the {
//Insert logic here
NSLog("Token cached");
}
This will equate to
FBSession.openActiveSessionWithReadPermissions(permissions, allowLoginUI:true){ session, state, error in
//callback code body here
}

This usually because of missing handle seesion in app’s application:openURL function.
It is in AppDelegate.m. Description from facebook samples says below:
// During the Facebook login flow, your app passes control to the Facebook iOS app or Facebook in a mobile browser.
// After authentication, your app will be called back with the session information.
// Override application:openURL:sourceApplication:annotation to call the FBsession object that handles the incoming URL
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
return [FBSession.activeSession handleOpenURL:url];
}

Related

How to detect that the current request is an authentication callback?

I have a single-page JavaScript application and I'm using the Auth0 service for signup/login.
I have integrated the Lock widget and I'm saving a string to localStorage after a user is authenticated, like so:
lock.on("authenticated", function(authResult)
{
localStorage.setItem('login', authResult.idToken);
}
The problem is that when Auth0 redirects them back to my application after logging in, the authenticated event is fired only after page loaded, but by that time, I've already done the check to see if the localStorage string is set (which it is not); therefore, the user just keeps getting asked to login again:
if(localStorage.getItem('login') == undefined)
{
lock.show(function(err, profile, token)
{
// ...
}
}
I tried to see if there was anything special passed in to the page after a callback - but the referrer isn't always there.
If I don't automatically prompt the user to login, but instead show a login button - the authenticated event never fires for some reason.
How do I get around this?
Based on the information provided you seem to be using Lock in redirect mode and if that's the case you can use the hash_parsed event as a way to know if Lock found a response that it will process.
Every time a new Auth0Lock object is initialized in redirect mode (the default), it will attempt to parse the hash part of the URL, looking for the result of a login attempt. After that, this event will be emitted with null if it couldn't find anything in the hash. It will be emitted with the same argument as the authenticated event after a successful login or with the same argument as authorization_error if something went wrong.
Leveraging this event you could do the following:
Subscribe to the hash_parsed event:
If hash_parsed is emitted with null and localStorage has no indication the user already logged in then redirect to login.
If hash_parsed is emitted with a non-null value that either the authenticated or authorization_error will be emitted and you can react accordingly.
Some sample code:
lock.on("hash_parsed", function (response) {
if (!response && !localStorage.getItem('login')) {
// Redirect to the login screen
} else {
// Either the user is already logged in or an authentication
// response will be processed by Lock so don't trigger
// an automatic redirect to login screen
}
});

How does google plus call back function work after authentication in IOS

I've done a google plus integration and i've done a simple login and displaying google friends on table. But the problem is before I load the friends I want to check if the user is logged in, so in my friendsdisplayViewController i've done something like this
if ([GPPSignIn sharedInstance].authentication)
{
NSLog(#"Status is authenticated, fetching friends!!");
[self fetchGooglePlusFriends:kGTLPlusCollectionVisible];
}
else
{
[[GPPSignIn sharedInstance]authenticate];
}
I've defined the callback function one on initial signupviewcontroller and one under this one
-(void)finishedWithAuth:(GTMOAuth2Authentication *)auth error:(NSError *)error
{
//code
}
But whenever the else case executes it always calls the call back function defined on signupviewcontroller not the one I defined on friendsdisplayViewController hence I'm unable to display the list and but able to validate the authentication.
I'm just curious how the call to this google call back function (finishedWithAuth) is made. Why did it call the one in signup and not in friendsdisplay eventhough the authentication was triggered from friendsdisplay.
I figured out the issue was because of the delegate which needs to be put on every view controller which you are calling, the GPPSignInDelegate. And the finishedWithAuth will then get triggered from the ViewController which you are trying to call it else it will trigger the finishedWithAuth in the ViewController which is standing in top of stack.

reauthorizeWithPublishPermissions handler is called with ErrorReauthorizedFailedReasonUserCancelled

I'm using iOS SDK 3.1.1 and trying to get both read and publish permission at once.
As tutorial says, I'm calling FBSession openActiveSessionWithReadPermissions and in its handler - handler A - call [[FBSession activeSession] reauthorizeWithPublishPermissions only if handler A is called with session state of FBSessionStateOpen.
When I have facebook account is set in iOS 6's setting, reauthorizeWithPublishPermissions's handler - handler B - is called normally, with error argument of nil.
However, if I don't have facebook account set in iOS 6's setting, handler B is called with reauth error named "ErrorReauthorizeFailedReasonUserCancelled" when app is switched to Safari to gain publish permission.
More weird thing is this. In both cases before handler B is called, handler A is called with session state of FBSessionStateOpenTokenExtended.
Are these normal or expected behavior of new SDK? If so, should I not check if error is nil in handler B?
happened to me and after searching a while I found a solution for that. You have to call reauthorizeWithPublishPermissions in dispatch_async in the handler A of openActiveSessionWithReadPermissions:
dispatch_async(dispatch_get_current_queue(), ^{
[[FBSession activeSession] reauthorizeWithPublishPermissions:permissions
defaultAudience:FBSessionDefaultAudienceEveryone
completionHandler:^(FBSession *session, NSError *error) {
// handle the flow here
}];
});

Facebook iOS SDK: authorize with permissions requires 2 attempts

I have an app that still uses the deprecated Facebook class to connect with Facebook. If I authorize with no extended permissions, everything works fine. But if I do include permissions, the first round trip to authorize always fails (even though it gets a valid token!). Am I missing a step?
Here's the code to initiate Facebook authorization for the app
- (IBAction) doConnect:(id)sender
{
NSArray* permissions = [NSArray arrayWithObjects:
#"email",#"publish_actions",nil];
[self.facebook authorize:permissions];
}
Here's the code that gets invoked after the user has granted permissions and control returns to my app. The url always includes a nice looking token, even the first time through.
// handle the incoming url from app switching
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [self.facebook handleOpenURL:url];
}
And here's the FBSessionDelegate method that gets invoked after a successful connect. Even though the url above contained a token, it's gone the first time we get here. But if I invoke the doConnect method above, the token will be present when we get here.
// FBSessionDelegate
- (void)fbDidLogin
{
if( [self.facebook accessToken] == nil )
{
NSLog(#"Had an access token above, but not now!");
// If I reinvade the doConnect: method again, it will work!!!
}
// ...
}
Looking deep in the sdk code in FBSession.m, it seems that the requested permissions haven't been associated with the new token first time through, causing the session to ignore the new token. First time through, cachedPermissions is always an empty list
// get the cached permissions, and do a subset check
NSArray *cachedPermissions = [tokenInfo objectForKey:FBTokenInformationPermissionsKey];
BOOL isSubset = [FBSession areRequiredPermissions:permissions
aSubsetOfPermissions:cachedPermissions];
You are asking for two types of permissions, a read type (email) and a write type (publish_actions).
You should be using the latest Facebook SDK, v3.1.1 and you'll have to split up read and writes separately - especially if you wish to support iOS6. You can only ask for reads initially. See the note in https://developers.facebook.com/docs/tutorial/iossdk/upgrading-from-3.0-to-3.1/ and the section on asking for read and writes separately.
For Facebook SDK 3.1, use [FBSession activeSession]'s
reauthorizeWithPublishPermissions: defaultAudience:completionHandler:
For Facebook SDK 3.2, use [FBSession activeSession]'s
requestNewPublishPermissions: defaultAudience:completionHandler:
we can authorise user in single request. It does not need two times attempt. To achieve this what we need do is "We need to ask for publish permission first". Call the below method
let fbLoginMngr = FBSDKLoginManager();
fbLoginMngr.logOut()
fbLoginMngr.logInWithPublishPermissions
it will ask first profile details then asks for requested publish permission. Once we get a call back we query to Graph api to extract the profile data like below.
let fbRequest = FBSDKGraphRequest(graphPath:"me", parameters:self.FB_REQ_PARAMS);
fbRequest.startWithCompletionHandler { (connection : FBSDKGraphRequestConnection!, result : AnyObject!, error : NSError!) -> Void in
if error == nil {
debugPrint(result)
} else {
handleError(error)
}
}

How handle cancel button of facebook dialog apprequest?

As you know, you can display some facebook views to make some operations, like app requests, post etc. There is a delegate to manage the callbacks like : dialogDidComplete:, dialogDidNotComplete:. The view is like that :
But there is not differentiation between the cancel and share button. You will have the same callback in dialogDidComplete:. The only way to manage a cancelation is the little cross in the corner.
In my case I would like to do some operations if the user pressed share and not when he pressed Cancel.
The private social network Path manage this case and I'm wondering how ?
Do you guys have some ideas ?
If I am not wrong:
if you press Share you get a requestID back as part of the returned URL
if you press Cancel you are returned into dialogDidComplete but it does not return a requestID back as part of the returned URL.
Code:
- (void)dialogCompleteWithUrl:(NSURL *)url {
if (![url query]) {
NSLog(#"User canceled dialog or there was an error");
return;
}
}

Resources