How does my fb connect webserver authenticate a user that logged in via fb ios sdk? I have a website which uses facebook connect. In it, i do use the app secret to authenticate the user via a cookie created by the facebook javascript sdk via the facebook python library:
def get_user_from_cookie(cookies, app_id, app_secret):
"""Parses the cookie set by the official Facebook JavaScript SDK.
cookies should be a dictionary-like object mapping cookie names to
cookie values.
If the user is logged in via Facebook, we return a dictionary with the
keys "uid" and "access_token". The former is the user's Facebook ID,
and the latter can be used to make authenticated requests to the Graph API.
If the user is not logged in, we return None.
Download the official Facebook JavaScript SDK at
http://github.com/facebook/connect-js/. Read more about Facebook
authentication at http://developers.facebook.com/docs/authentication/.
"""
cookie = cookies.get("fbs_" + app_id, "")
if not cookie: return None
args = dict((k, v[-1]) for k, v in cgi.parse_qs(cookie.strip('"')).items())
payload = "".join(k + "=" + args[k] for k in sorted(args.keys())
if k != "sig")
sig = hashlib.md5(payload + app_secret).hexdigest()
expires = int(args["expires"])
if sig == args.get("sig") and (expires == 0 or time.time() < expires):
return args
else:
return None
Now, I'm wondering how to connect a user who logs in via the iphone to my website. Do I just send the access token over to my webserver and based on the the access token make a call to the graph(just bypass the above function)? If that is it, then what about all the validation the above function offers?
The cookie fetch and validation is useful for Google AppEngine or JavaScript SDK access.
For accessing user session initiated in iOS app, in Python SDK, just use:
graph = facebook.GraphAPI(access_token)
user = graph.get_object("me")
Hope that helps.
If you want use Facebook iOS API you need:
Alloc and Init a Facebook object
Facebook *facebookObject = [[Facebook alloc] init];
Check permissions
NSArray *permissions = [NSArray arrayWithObjects:#"publish_stream", #"offline_access",nil];
Authorize facebook
[facebookObject authorize:APP_ID permissions:permissions delegate:self];
After that you can used NSUserDefaults to keep session like that :
To store session I use NSUserDefaults.
[[NSUserDefaults standardUserDefaults] setObject:facebookObject.accessToken forKey:#"AccessToken"];
[[NSUserDefaults standardUserDefaults] setObject:facebookObject.expirationDate forKey:#"ExpirationDate"];
[[NSUserDefaults standardUserDefaults] synchronize];
After that I can catch Access with :
facebookObject.accessToken = [[NSUserDefaults standardUserDefaults] stringForKey:#"AccessToken"];
facebookObject.expirationDate = (NSDate *) [[NSUserDefaults standardUserDefaults] objectForKey:#"ExpirationDate"];
Like that you can keep session alive and the user enter login and password only once. I think that is why cookies.
Related
I am creating an app for Facebook and twitter integration using sharekit. That works fine. But now I want to retrieve the username and password for facebook login. I followed this link.
But I am not able to retrieve these..
I can't understand how can use that method and where can i use to retrieve username and password
Can you please guide me if you know.
First of all, you can't retrieve a user's Facebook password. Hopefully the reasons for this are obvious.
You can, however, retrieve the access token that your app is granted once you connect your app to a user's Facebook account using ShareKit.
As of this writing, I don't believe ShareKit makes the access token accessible directly, but there's an easy hack to retrieve it.
Step 1: Ensure that you're app is authorized to connect to Facebook
BOOL isConnected = [SHKFacebook isServiceAuthorized];
If you get isConnected == NO here, your UI should indicate that the user needs to connect to Facebook to use your sharing features.
Step 2: Get the access token in order to access the user's Facebook data
Assuming you got isConnected == YES in step 1
// Hack into ShareKit's user defaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *accessToken = [defaults valueForKey:#"kSHKFacebookAccessToken"];
Step 3: Bypass ShareKit and make a custom query to the Facebook SDK
Assuming a property in your class such as this one...
// Change "strong" to "retain" if not using ARC
#property (nonatomic, strong) SHKFacebook *shkFb;
...you can start a Facebook query with something like this...
if ( !fb ) {
// This is how SHKFacebook instantiates a Facebook object. YMMV.
self.fb = [[Facebook alloc] initWithAppId:SHKCONFIG(facebookAppId)];
}
NSMutableDictionary *fbParams = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"name", #"fields",
accessToken, #"access_token",
nil];
[fb requestWithGraphPath:#"me" andParams:fbParams andDelegate:self];
Step 4: Implement the Facebook delegate methods
Once the Facebook query's done, it'll notify your object, at which point you can do fancy things such as display the user's name to make it clear whose wall will get posts sent from your app.
You'll need to declare the FBRequestDelegate protocol in your .h, of course:
#import "Facebook.h"
#interface YourClass : NSObject <FBRequestDelegate>
And you'll need to implement (minimally) the success and failure methods from FBRequestDelegate:
#pragma mark - FBRequestDelegate
- (void)request:(FBRequest *)request didLoad:(id)result {
// Additional keys available in "result" can be found here:
// https://developers.facebook.com/docs/reference/api/user/
NSString *username = [result objectForKey:#"name"];
// Localize if you're at all interested in the global app market!
NSString *localizedString = NSLocalizedString(#"connected as %#",
#"Connection status label");
// The label will read "connected as <username>"
self.statusLabel.text = [NSString stringWithFormat:localizedString, username];
}
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
// Handle failure
// (In our app, we call [SHKFacebook logout]
// and display an error message to the user with
// an option to retry connecting to Facebook.)
}
I believe clozach's answer is for an older version of ShareKit. In the most recent (as of Jan 30 2012) the method of acquiring the Facebook Access Token provided fails with accessToken always being nil.
// Hack into ShareKit's user defaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *accessToken = [defaults valueForKey:#"kSHKFacebookAccessToken"];
Instead, using
NSString *accessToken = [[FBSession activeSession] accessToken];
works for me.
ShareKit uses FBConnect internally for authentication. So the values can't be retrieved using the same method as you would for Twitter. FBConnect uses a UIWebView view to connect to the server while authenticating. After authentication, the app will store a token which can be reused to publish the text until the user discards the token on Facebook.
So, the answer is no. You can't get that data.
Yet another fb login question:
By the time my user has something to share, they've already logged into my app server.
I give them the option to use their FB creds for my app.
So if they share, I want to pre-populate the FB login page with the creds they've already supplied to me.
Does anyone know if this is possible, and if so, how to do it?
Here's what I'm talking about:
I use Facebook API too. You never keep login and pass in your app (check Facebook API doc). However you can keep session. So the user enter login and pass once. In my code I've a "FacebookLogger" who is a singleton with a Facebook object.
To store session I use NSUserDefaults (find doc here).
[[NSUserDefaults standardUserDefaults] setObject:m_Facebook.accessToken forKey:#"AccessToken"];
[[NSUserDefaults standardUserDefaults] setObject:m_Facebook.expirationDate forKey:#"ExpirationDate"];
[[NSUserDefaults standardUserDefaults] synchronize];
Where m_Facebook is my Facebook object in my singleton. After that I can catch Access with :
m_Facebook.accessToken = [[NSUserDefaults standardUserDefaults] stringForKey:#"AccessToken"];
m_Facebook.expirationDate = (NSDate *) [[NSUserDefaults standardUserDefaults] objectForKey:#"ExpirationDate"];
Is it possible to use the Facebook iOS SDK to authenticate within an app (not go to Safari), and also keep those authentication credentials for the next launch of the app?
When I try to use the demo app in the simulator, it always goes to safari to authenticate, which seems a bit crappy. And then, when I authenticate... if I completely kill the app it will ask to authenticate again (and then tell me I am already logged in)
Is there a way, to just present the user with just an email field and a password field and then keep that info.. within the app?
Take a look at this question and answer: Iphone facebook connect example calls safari. I don't want to use safari. Also, you'll want to store the authentication stuff in NSUserDefaults and check for them to make to prevent re-logins.
EDIT Some sample code:
To save login stuff:
[[NSUserDefaults standardUserDefaults] setObject:_facebook.accessToken forKey:#"AccessToken"];
[[NSUserDefaults standardUserDefaults] setObject:_facebook.expirationDate forKey:#"ExpirationDate"];
[[NSUserDefaults standardUserDefaults] synchronize];
To check for login stuff:
_facebook = [[[Facebook alloc] initWithAppId:#"[app_id]"] retain];
_facebook.accessToken = [[NSUserDefaults standardUserDefaults] stringForKey:#"AccessToken"];
_facebook.expirationDate = (NSDate *) [[NSUserDefaults standardUserDefaults] objectForKey:#"ExpirationDate"];
if (![_facebook isSessionValid]) {
[_facebook authorize:_permissions delegate:self];
}
else {
[_facebook requestWithGraphPath:#"me" andDelegate:self];
}
You can hack round it to stop it if this is what you really want, bearing in mind most other apps that migrate from the older Facebook connect api to graph will behave in the new way
In facebook.m find the following method
- (void)authorizeWithFBAppAuth:(BOOL)tryFBAppAuth
safariAuth:(BOOL)trySafariAuth
find the bottom of the didOpenOtherApp logic and comment out all above it so that it always opens inline and tuns this section of code thats contained in the !didOpenOtherApp braces
// If single sign-on failed, open an inline login dialog. This will require the user to
// enter his or her credentials.
if (!didOpenOtherApp) {
[_loginDialog release];
_loginDialog = [[FBLoginDialog alloc] initWithURL:loginDialogURL
loginParams:params
delegate:self];
[_loginDialog show];
}
However by doing this you are making it more likely that the user will have to input their credentials, which is surely worse than putting up with the fast app switching approach?
When you first auth, make sure you're asking for "offline_access" permission. That will make the token that OAuth returns to you NOT be invalidated at the end of the session, but instead stay valid literally until they come along and use the API to log your app OUT of Facebook.
Then, obviously, you need to save the token (I feel like NSUserDefaults is a fine place for it) and reuse it on later FB interactions.
I'm integrating facebook with my Application, and I want to give users the option to post a story to their FB wall from within my app. It's my understanding this requires users to 1. login/authorize and select 'publish' from a separate screen to post the story.
My goal is to have both these events occur when a user presses a button in my app. The only tiny glitch I'm noticing is that if the user has already logged in and authorized the app, the login screen flashes briefly before the 'publish story' screen. Is there any sort of property I can check to see if a user has already authorized the application to prevent the login screen from flashing? This is the code I'm working with right now:
if (facebook == nil)
{
facebook = [[Facebook alloc] init];
}
if (!facebook.accessToken)
{
[facebook authorize:#"###############" permissions:[NSArray arrayWithObject:#"publish_stream"] delegate:self];
}else
{
[self fbDidLogin];
}
Casey, this might help you:
The main issue I have found with that flashing login-screen had to do with the cookies Facebook stores in the iPhone.
What I did is:
1. Store the facebook access_token and expiration_date elsewhere (NSUserDefaults might be a good place).
This way, you can check if the user has already authenticated. If the user hasn't authenticated, then you can show the login screen.
2. Another thing that is important, is to know that Facebook will save some cookies as part of the authentication process. So, what happens is: the user authenticates and then turns off the app, then the next he opens it and taps login; Facebook will use its cookies, causing that annoying screen to appear and disappear.
You can try deleting the facebook cookies each time you want the user to login. That did the job for me.
Here you have a code snippet that removes the cookies.
-(void) deleteFacebookCookies{
NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray* facebookCookies = [cookies cookiesForURL:
[NSURL URLWithString:#"http://login.facebook.com"]];
for (NSHTTPCookie* cookie in facebookCookies) {
[cookies deleteCookie:cookie];
}
}
HTH, cheers!
If you use FBConnect you can just use [[FBSession session] isConnected], and [[FBSession session] logout, this is for old SDK, there are some changes in auth in current version.
I'm using the Facebook SDK for iOS in my app: http://github.com/facebook/facebook-ios-sdk
The oAuth token expires after about 2 hours. How can I "refresh" the oAuth token without having to call the [Facebook authorize...] method again - which shows an empty Facebook dialog briefly if the user had previously logged in? What I want to avoid is requiring the user to re-login to FB each time they use the app - say, day to day.
I am already saving / restoring oAuth tokens when the app exits / starts. And I can check to see if the token is valid using [Facebook isSessionValid], or by checking the expire time on the token. But what to do if the token has expired? I've read that it is possible to "refresh" the token, but I don't understand how this is done.
I don't want to request "offline_access" permission, which would give me a "forever" token.
Help!?
Facebook's implementation of OAuth doesn't support token refresh.
You have 2 types of access_tokens in Facebook. Short term token, which is given by default and a long term token which is given if you request offline_access. If refresh token were supported, it was the same as giving a offline_access token for all apps.
As long as the user has an active facebook session on your web control, you can request a new access_token just by accessing https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=http://www.facebook.com/connect/login_success.html&response_type=token or probably some iOS SDK command that does the same (never worked with it so I can't tell). This kind of request will not ask the user to login again, but will use the previous session that was created during the first login.
Since none of these answers actually addressed the question I am going to detail how I have implemented OAuth token refresh using The Facebook SDK.
The SDK will automatically refresh your tokens when you make requests however, in my scenario we send the tokens to our servers and we need to use the latest token. So when our server indicates that we need new tokens this is what we do:
Note You can either pass the AppID into the FBSession or you can add the FacebookAppID key to your App's plist (this is what we do).
- (void)renewFacebookCredentials {
if (FBSession.activeSession.state == FBSessionStateOpen ||
FBSession.activeSession.state == FBSessionStateOpenTokenExtended) {
[self sessionStateChanged:[FBSession activeSession] state:[FBSession activeSession].state error:nil];
} else {
// Open a session showing the user the login UI
// You must ALWAYS ask for public_profile permissions when opening a session
[FBSession openActiveSessionWithReadPermissions:#[#"public_profile",#"email"]
allowLoginUI:NO
completionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
//this block will run throughout the lifetime of the app.
[self sessionStateChanged:session state:state error:error];
}];
}
}
The you can use the sessionStateChanged: method that Facebook include in their documentation but a simplified handler looks like this:
- (void)sessionStateChanged:(FBSession *)session state:(FBSessionState) state error:(NSError *)error {
// If the session was opened successfully
NSString *accessToken;
if (!error && state == FBSessionStateOpen && [[session accessTokenData] accessToken]){
// Show the user the logged-in UI
//#see http://stackoverflow.com/questions/20623728/getting-username-and-profile-picture-from-facebook-ios-7
accessToken = [[session accessTokenData] accessToken];
//Now we have an access token, can send this to the server...
} else {
//No access token, show a dialog or something
}
//either call a delegate or a completion handler here with the accessToken
}
Be aware that some of the FBSession API calls check for thread affinity so I found that I had to wrap all my FBSession calls inside a dispatch_async(dispatch_get_main_queue(), ^{...
Just do [ [FBSession session] resume] if it return false do login again
Facebook iOS SDK doesn’t handle the session storage.
FBSessionDelegate is a callback interface that your application should implement. it's methods will be invoked on application's successful login or logout.
See the example application facebook-ios-sdk/sample/DemoApp/Classes/DemoAppViewController.m for fbDidLogin, fbDidNotLogin and fbDidLogout methods
As per the Facebook SDK documentation:
The Facebook SDK automatically refreshes the user's session if
necessary. When it does this, the state also transitions to the
FBSessionStateOpenTokenExtended state.
Also to further clarify (since SDK version 3.1):
The SDK refreshes session automatically on API calls.
It also refreshes the token data as needed when follow-on authentication or Facebook API calls made using the SDK.
As of today Facebook is supposed to refresh tokens automatically, requests to GraphAPI can be done without providing token string either (Facebook handles it under the hood).
Moreover, if it happens that user wasn't using app for a long time and his token managed to expire, on your next request to Graph API an alert will be shown by Facebook's SDK asking user to relogin (all that is handled by Facebook and when done - will return into FBSDKGraphRequest's closure).
However, if someone really has a reason to manually refresh access token, here's an example (Swift 4):
private var selfDestructableNotificationToken: NotificationTokenThatAutomaticallyRemovesObserver?
final class NotificationTokenThatAutomaticallyRemovesObserver: NSObject { // more info here: https://oleb.net/blog/2018/01/notificationcenter-removeobserver/
let token: Any
init(_ token: Any) { self.token = token }
deinit { NotificationCenter.default.removeObserver(token) }
}
...
if let currentFBToken = FBSDKAccessToken.current() { // if this is a Facebook user, not an email-based user
if FBSDKAccessToken.currentAccessTokenIsActive() { // and his token has not expired yet
let token = NotificationCenter.default.addObserver(forName: NSNotification.Name.FBSDKAccessTokenDidChange, object: nil, queue: OperationQueue.main) { notification in
if let userInfo = notification.userInfo, let refreshedToken = userInfo["FBSDKAccessToken"] as? FBSDKAccessToken {
self.fbAccessToken = refreshedToken.tokenString
} else {
self.fbAccessToken = currentFBToken.tokenString // falling back to using an old token (better than none)
}
}
self.selfDestructableNotificationToken = NotificationTokenThatAutomaticallyRemovesObserver(token)
FBSDKAccessToken.refreshCurrentAccessToken { _, _, error in
if let error = error {
print("failed to refresh Facebook token with error \(error.localizedDescription)")
self.fbAccessToken = currentFBToken.tokenString // falling back to an old token (better than none)
}
}
} else if FBSDKAccessToken.current().isExpired { // unlucky user. Probably returned to the app after > 2 months break
self.fbAccessToken = currentFBToken.tokenString // assigning expired token. Facebook will ask user to relogin as soon as we call Graph API with that expired token
}
}