Impossible issue connecting to Windows Live Messenger network - iphone

This issue is giving me serious headaches, I don't have a clue what's going on here. If you don't have any experience with the Windows Live network, I ask you to read this anyway, maybe it has nothing to do with it and am I overlooking something totally unrelated.
In short: I wrote an Objective-C class that allows me to connect to the Windows Live Messenger network, called WLNotificationSession. I works really straightforward, I set the username and password variables and do [notificationSession start];. It then logs in successfully.
Let's say I have two Windows Live accounts. The first one, A, is now logged in.
The problem arises when I try to fire up a second WLNotificationSession, with the other Windows Live account, B. It always fails. The usernames and passwords are 100% correct. When I try to log in B first, it succeeds. When I try A while B is logged in, it fails. The second login session always fails.
It can't be something like "too much log in attempts in a short period of time". When I log in A, quit the app, restart the app and log in A again, both attempts succeed. I can do this within 20 seconds. But, when I fire up the app, log A in, disconnect A, wait 2 hours, log in B (all without closing the app), it fails. (??)
For those of you with experience with the WL network: the failure occurs during the Tweener authentication. The part where you get the "Authentication-Info" or "WWW-Authenticate" HTTP header from the login server. When it fails, I get this value:
"Www-Authenticate" = "Passport1.4 da-status=failed-noretry,srealm=Passport.NET,ts=-2,prompt,cburl=http://messenger.msn.com/images/logo102x88.gif,cbtxt=.NET%20Messenger%20Service";
I really hope someone can help with this. Thank you.
UPDATE
This is some example code. I create a new project, add this code in the applicationDidFinishLaunching method and click Build & Run:
WLNotificationSession *notificationSession1 = [[WLNotificationSession alloc] init];
notificationSession1.username = #"testaccount1#hotmail.com";
notificationSession1.password = #"testpwd";
[notificationSession1 start];
WLNotificationSession *notificationSession2 = [[WLNotificationSession alloc] init];
notificationSession2.username = #"testaccount2#hotmail.com";
notificationSession2.password = #"testpwd";
[notificationSession2 start];
notificationSession1 always succeeds, notificationSession2 always fails. No global variables, or shared variables whatsoever.
UPDATE 2
Following David's suggestion the problem could be cookie-related, I added this code to my project:
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyNever];
I also use his method in the comments below to delete any cookies before and after each URL request. This is probably unnecessary, but I do it anyway.
I think at this point it is safe to assume it's not the cookies, or there has to be some other place where cookies are stored.

No global variables, or shared variables whatsoever
Then, as the authentication is performed using http request, this could be cookie issue. There might be some session cookie reminding the server about the former session.
I know that FBConnect (Facebook API for iPhone) uses the following method when logging out to remove any cookie :
- (void)deleteFacebookCookies {
NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray* facebookCookies = [cookies cookiesForURL:[NSURL URLWithString:#"http://login.facebook.com"]];
for (NSHTTPCookie* cookie in facebookCookies) {
[cookies deleteCookie:cookie];
}
}
You could try this (replace facebook url with yours). You could even add some NSLogs to watch for these cookies.

Related

Facebook OAuth Login - access_token API returning "This authorization code has been used"

This question has been asked a few times on Stack, but there have been no real answers. Let me try to explain my situation anyways.
We use an application that uses Facebook OAuth2 login. This login used to work fine till last week, and all of a sudden it is troubling us now.
Application Flow:
Step 1: User presses login with Facebook button on our website
Step 2: Redirected to Facebook login/authorization page
Step 3: On authorizing the app, the callback comes to our application, with a short lived "code" param.
Step 4: This "code" param would be exchanged for a 60 day Access token using "https://graph.facebook.com/oauth/access_token" URL.
Error in Step 4:
When we try to exchange the short living "code" for the access token, we get this error from Facebook.
{"error":{"message":"This authorization code has been used.","type":"OAuthException","code":100}}
Observation:
For users who are newly coming to the application, the above-said error does not occur.
For a returning user this call fails with the above-said error.
Our application is live for more than 9 months now, and this error has come only in the past 7-10 days. We have had thousands of users using it successfully prior to that.
What I already got from Forums:
Here is my interpretation of what I read. May be inaccurate.
Facebook has some weird policy that necessitates the app developer to maintain the temporary 10 minute code until the 60 day code that was obtained during the first login expires. So we should create a cookie with the Access token on the user's browser. I was even able to see people modifying their code in order to create the cookies.
What's really bothering me?
The suggested solutions assumes that the cookie that they create would be present in the user's browser always. This is a bad assumption to make, as the cookie may be erased at any time.
I have another app Id/app secret that I use for my development (i.e localhost), and that works perfectly. The login happens fine out there, But its only the product machine that has the problem.
This problem didn't happen on the production machine for nearly 10 months since we launched the app, and it has come all of a sudden. Worst of all, I am unable to get any record of recent changes that breaks this flow.
Edit:
Platform: Python, Google Appengine. We do not use any Facebook SDKs, we make direct HTTP Calls to all the login URLs.
Call that fails : https://graph.facebook.com/oauth/access_token - we are passing the appId, secret and code (obtained from facebook) within 20 seconds of the first call happening.
Hope there is enough information here to show that our code is not totally incorrect. Any tips/pointers from people who have encountered and solved this problem is Welcome. If its a Facebook bug, and the Facebook dev comes to notice, I would be even happier.
I got round this issue by using a random GUID which is appended to each callback url i pass into facebook. It seems the code that facebook returns is made up of a few parts including the redirect_uri parameter you have to specify. By using this GUID trick, your app continues to work but facebook thinks it's a different URL hence generating a new code.
If you store that GUID in a temporary session, it's always the same. Here's a very cut down version of what I mean. I'm using C# but the solution will be the same:
Before i start the oauth process:
Session["facebook_buster"] = System.Guid.NewGuid().ToString();
Then to kick off the login:
var facebook = new FacebookClient();
var loginUrl = facebook.GetLoginUrl(new
{
client_id = ...,
redirect_uri = ..."/facebook/oauthcallback?buster=" + Session["facebook_buster"].ToString(),
display = "popup",
scope = "publish_stream,user_photos"
});
And then in my callback method, when I want to exchange that code for a new access_token:
var facebook = new FacebookClient();
dynamic result = facebook.Post("oauth/access_token", new
{
client_id = ...,
client_secret = ...,
redirect_uri = ..."/facebook/oauthcallback?buster=" + Session["facebook_buster"].ToString(),
code = Request["code"] // this is the returned code from the first method
});
Note in that second method i'm using the same session key so that the authorization code is successful.
Been testing this all morning by revoking permissions / manually changing my stored access_token (in my db) / removing my stored access_token completely and it works every time.
Hope this helps!
I struggled with this today for a while too. Not sure if you're using the Facebook PHP class (from what you wrote, it seems you don't), however, it could be a pointer anyways - the problem was that the Facebook PHP library seems to obtain the token from the code automatically and I was trying to do it again.

Extend auth token without refreshing the page

Users want to use my facebook app for many hours without refreshing the browser.
But token expires in 2 hours. Now I ask users to refresh the page but that's annoying.
I don't want to ask offline access permissions because it will scare some users.
The best solution will be somehow "relogin" and get new token without refreshing the page.
Is it possible?
I would subscribe to the expiry trigger (I think this is authResponseChange), then automate another login check. It won't be a perfect solution as it could trigger a pop up (if they have logged out for example) automatically, which a lot of browsers may block. You could instead, when the token expires, check if they will need to complete a pop up, and display a notification on your page somewhere saying 'Facebook needs your attention to continue', then only launch the pop up from their response, which would stop the pop up being blocked.
FB.Event.subscribe('auth.authResponseChange', function(response) {
// do something with response
FB.login(){
// refresh their session - or use JS to display a notification they can
// click to prevent pop up issues
}
});
An algorithm to workout on this
Ask for permission from the user
Save the token
Periodically check for an access token is near to expire or not
If its in verse of expiry, embed some dummy iframe, which redirects to the facebook homepage. - Extend auth token without refreshing the page
This should refresh the token. You might need to generate another token or continue with the same. Whatever be required, can be done without refreshing the page.
Have you thought of using ajax? After two hours you will check, if user is still active. If so, you send axax request to URL, where his session details will be updated. example:
$(document).ready(function(){
setInterval('update_session()', 5500000);
})
update_session(){
$.post({
URL: ..., // script to update session on server
data:{ /* username, password */ },
})
}
and the server-side just takes username and password from post or and runs relogin.
Try acquiring tokens with the offline_access permission.
I presume, guess this is not possible,FB architecture would not allow it. And why is offline_access such a problem!!!!!!...anyway offline_access is the best optimal solution I guess....
Unfortunately I believe this is impossible by design (if you mean for it to happen without user intervention). If the user is still logged in to Facebook you can redirect the top-level page to Facebook and it will bounce you right back with a new code (as it sounds like you are doing already), but that is only possible because of the Facebook cookie that it can check. If you try to do anything from your server, it will be rejected because that cookie will not accompany the request. Same goes for trying to make a call to facebook from javascript -- since your code is running in a different domain, the cookie will not accompany the call and Facebook will reject it. The only way that Facebook can even know who the user is, and that they are still logged in, is to see that cookie. And the only way that can happen is if the browser itself is redirected to the facebook.com domain.
It's worth mentioning also that Facebook has blocked the only logical workaround, i.e. loading the oauth url in an iframe. If you try it you will see that they detect the page is being loaded in an iframe and output a page with a link on it which does a top-level redirect to break out of the frame. So not only does this approach not work, it's clear that Facebook has specifically made it impossible as part of their architecture.
Edit: If what you mean to do is not avoid the refresh altogether but just have it happen automatically when a new token is needed, you can do something like this:
$status=0;
$data=#file_get_contents("https://graph.facebook.com/me?access_token=$token");
foreach ($http_response_header as $rh) if (substr($rh, 0, 4)=='HTTP') list(,$status,)=explode(' ', $rh, 3);
if ($status==200)
{
//token is good, proceed
}
else
{
//token is expired, get new one
$fburl="http://www.facebook.com/dialog/oauth?client_id=APP_ID&redirect_uri=".urlencode('http://apps.facebook.com/yourapp/thispage.php');
echo "<html>\n<body>\n<script>top.location='$fburl';</script>\n</body>\n</html>\n";
exit;
}
This is assuming you have something before this code that will process a signed_request parameter if it is present and assign a value to $token (either explicit code of your own or the appropriate SDK entries). The shown code can then be used anywhere you need to check if $token is still valid before proceeding.
If you get the access_token without specifying any expiry to them they will not expire ..
atleast not till the time user either changes his Fb credentials or de registers your application ..
I presume you are using the iframe signed_request parameter to get your access token. One method of achieving what you require is to use the oAuth 2.0 method of aquiring an access token. This is more prolonged in the first instance; your server and Facebook's have to exchange credentials which can be slow, but it means that you will be given a code that can be exchanged for an access token regularly, meaning your server can maintain the session periodically (probably from an ajax call from the client). You would then pass this new access_token to the client, and use it in your dialog call for your requests (gifts).
Hope that helps.
Spabby
Have a look at https://developers.facebook.com/docs/offline-access-deprecation/#extend_token
basically you extend the token with
https://graph.facebook.com/oauth/access_token?
client_id=APP_ID&
client_secret=APP_SECRET&
grant_type=fb_exchange_token&
fb_exchange_token=EXISTING_ACCESS_TOKEN
that will give you new token with new expiry time (it should be 60d but I'm noticing similar bug like described here https://developers.facebook.com/bugs/347831145255847/?browse=search_4f5b6e51b18170786854060 )

facebook-ios-sdk logout question

I have seen a lot of questions here regarding the Facebook Graph API but I still haven't find a solution for simple 'login'/'logout' operations using it. Looks like the Single Sign-On style is causing more confusion than benefits.
I'd like to know if it is possible have the following situation:
Enter in the app (no accessToken/expirationDate created).
Perform a login using SSO by calling authorize:delegate: method (application goes background and the login is made in the 'global' scope (Facebook App/Mobile Safari), asking for the user credentials.
Enter back in the app (now logged in, both accessToken and expirationDate are saved to NSUserDefaults).
Perform a logout by calling the logout: method (now logged out, both accessToken and expirationDate are removed from NSUserDefaults)
Attempt to perform a login again, with exactly the same steps done in 2.
I realize that when I call logout:, I do really log out from Facebook (accessToken is invalidated) from my App scope, not from the global scope (Facebook App/Mobile Safari). In 5.) when I try to log in again, the application goes to background and the login attempt is made again in Facebook App/Mobile Safari as usual, however I'm getting a screen saying that I'm already logged in:
You have already authorized .... Press "Okay" to continue.
Logged in as ... (Not You?).
It's a strange behavior for the user that has just logged out in my App.
My question is:
"Can I really log out from facebook (I mean 'global' scope) from inside my App? This would affect other apps using the facebook credentials too. However, if I can't to do this, how can I avoid the 'strange behavior' describe above?
Thanks
Eduardo,
I feel your pain! I spent the better part of a day working on this issue. I have discovered that when you use SSO and the call:
Called from your code:
[facebook logout:self];
Facebook API method:
- (void)logout:(id<FBSessionDelegate>)delegate {
self.sessionDelegate = delegate;
[_accessToken release];
_accessToken = nil;
[_expirationDate release];
_expirationDate = nil;
NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray* facebookCookies = [cookies cookiesForURL:[NSURL URLWithString:#"http://login.facebook.com"]];
for (NSHTTPCookie* cookie in facebookCookies) {
[cookies deleteCookie:cookie];
}
if ([self.sessionDelegate respondsToSelector:#selector(fbDidLogout)]) {
[_sessionDelegate fbDidLogout];
}
}
The facebook API does invalidate the access token and expirationdate variables and attempts to delete the mobile Safari cookies, but for some reason, probably Apple's fault the cookies are not really deleted. So when you attempt to login in the next time your mobile Safari will see the cookie and it says:
"You have already authorized .... Press "Okay" to continue. Logged in as ... (Not You?)."
Until either Facebook finds a fix or Apple fixes their broken API we must bypass SSO through Safari. Below are the changes I made to Facebook.m in order to force the old login dialog. If you used these changes they may not work forever but it is my guess that they will work for a very long time. Also to be sure this worked with the most recent facebook API I updated to the latest as of this post (Nov 2011 build).
Called from your code:
[facebook authorize:permissions];
Facebook API method:
- (void)authorize:(NSArray *)permissions {
self.permissions = permissions;
// [self authorizeWithFBAppAuth:YES safariAuth:YES];
[self authorizeWithFBAppAuth:NO safariAuth:NO];
}
If this helps you please up rate this thread and my post to help others find it.
gadildafissh
I'm afraid the answer is no, you can't do this.
Your application is in a sandbox, and can't write outside, where global cookies are (for mobile safari) and Facebook app settings (in Facebook app preferences/cookies I think)
You can only warn your user to logout outside of your app...
...Or you can just not use facebook api SSO, but in app login webform, like I do for other reasons.
If you choose that option this pull request might save you some time ;)
Hii ,
its not possible , the reason is for Single Sign On (SSO) is not to make user login everytime, he logouts , instead if the user logs in anyone of FB enabled apps - it will use that to login again - This is because the device is mostly used by single person in this case only one user can login in Facebook.
you can't control any app outside of your app - for Example - if u login with Gmail & when you open google.com you can see your username there is currently logged In which has SSO,
In new SDK of Facebook, you can set login button loginBehaviour property
Below code in swift ...
let fbButton = FBSDKLoginButton()
fbButton.loginBehavior = .Web
Answer already done, but I just want to clarify it. May be it saved somebody's time.
Go to Facebook.m and change line
[self authorizeWithFBAppAuth:YES safariAuth:YES];
to
[self authorizeWithFBAppAuth:YES safariAuth:NO];
It will cause login window appear inside the app. Logout will work perfect. In other words, it will work as it used to in older versions of OS.
in addition to kishnan94 answer. the objective c version is ;
if you want a modal to open up and ask for facebook credentials seperately from Safari or Facebook app, just use the latest facebook sdk and set the login behaviour
FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init];
[login setLoginBehavior:FBSDKLoginBehaviorWeb];
this will make the logout process more convenient and less confusing for users without using safari or facebook app accounts.
It seems that this is a bug of Facebook SDK. In a case of the Facebook app is installed on device, access_token is renewed. In other hand, access_token and expirationDate could not be changed. :((

Authlogic and iPhone app login

This has been a head-banger for me. I have looked at all of the similar questions on SO and none have proved satisfactory. Here's my go at it.
I have a perfectly fine rails 3 app setup with Authlogic.
I have been using Objective Resource (http://iphoneonrails.com/) to connect my iPhone app and my Rails app, and it has been working fine.
Now is the time to add authentication, and I cannot figure it out.
I have a UserSession class in my iPhone project with a username and password field. When the app launches, I present a login screen with username and password fields. Upon clicking a button, the iPhone app attempts to create the UserSession.
I took my code for doing that from http://www.corprew.org/blog/2010/01/27/authlogic-and-objectiveresource/
us.username = usernameField.text;
us.password = passwordField.text;
NSError * err = nil;
if(![us saveRemoteWithResponse:&err])
{
NSLog(#"%d", err);
NSLog(#"whoops, error");
}
When I run that code, the Log shows "0" and "whoops, error" meaning that the UserSession did not save. This occurs for both correct and incorrect logins.
Additionally, I have placed token comments in saveRemote ("I'm in saveRemote" etc.) and those never appear in my log.
Any ideas here?
If you want me to post anymore code, let me know. I will be up all night with this one.
Question resolved.
Don't use ObjectiveResource :-)
Switched to ASIHTTP and it's working fine for my needs right now.

Confusion about session objects in Facebook Connect for iPhone

I've done a lot of reading of the Facebook docs, but I'm pretty confused about the role of the session object. For instance, with the method:
session = [FBSession sessionForApplication:myApiKey secret:myAppSecret delegate:self];
What am I supposed to do with the session object that's returned to me, when presumably I need to wait for the delegate callback in order to do anything?
Secondly...upon a later execution of my app, when the user has authorized me accessing their account during a previous execution, how do I get a reference to a session object so I can connect to their Facebook account and post status, etc.? The docs mention [session resume], but don't say where that session reference is supposed to come from. (And calling [[FBSession session] resume] compiles, but doesn't work.)
Thanks.
What am I supposed to do with the session object that's returned to me, when presumably I need to wait for the delegate callback in order to do anything?
You have to create and show a FBLoginDialog to the user. The delegate method will not be called until the user has logged in. It has nothing to do with creating the session instance in the first place.
how do I get a reference to a session object so I can connect to their Facebook account
The Facebook Connect library will store the session info in your app's user defaults. You don't have to do anything to store it. As I understand it, on every launch of your app you should create the session object with +sessionForApplication:secret:delegate: and then call [session resume]. If the FBConnect library finds a valid session stored in the user defaults, it will return YES and you can proceed as the user is now logged in. If resume returns NO, you will have to show the login dialog.
From Facebook's docs:
The session information will be stored on the iPhone disk in your application's preferences, so you won't have to ask the user to log in every time they use your application. After you've created your session object, call [session resume] to resume a previous session. If the session has expired or you have yet to create a session, it will return NO and you will have to ask your user to log in. Sessions expire after two hours of inactivity.
this is really following on from Ole's answer and comments as it is all in there. This is what I did.
Firstly create the session object:
facebookSession = [[FBSession sessionForApplication:SESSION secret:SECRET delegate:self]retain];
[facebookSession resume];
The second line pulls the session data from the settings file to keep you logged on if the user specifies that they'd like to be kept logged on.
You have to implement the method:
- (void)session:(FBSession*)session didLogin:(FBUID)uid
although I don't use it for anything.
To check if the user is already logged on, I call:
[[FBSession session] isConnected]
and then either display the login dialog or straight to the publish dialog if they are already logged on.