Facebook IOS SDK : how to do silent sign in? - ios

I have an app doing a facebook login, which works well, but everytime I reopen it I have to connect again to facebook to do the sign-in. I'm also using google sign-in sdk where I can call the function gSignIn.signInSilently(), is there something similar for facebook? I found this for the javascript sdk but I don't know if it's possible for the ios SDK and how to use it in swift...

The Facebook SDK automatically maintains the login state, which can be confirmed by checking for the access token.
You can check for the access using the following method:
FBSDKAccessToken.currentAccessToken()
You can check for the presence of the token which would mean that the user is logged in.
Check the docs for more details.

i've tried this
if(![FBSDKAccessToken currentAccessToken])
{
FBSDKLoginManager *manager = [[FBSDKLoginManager alloc]init];
[manager logInWithReadPermissions:#[#"public_profile", #"email",#"user_photos"] handler:^(FBSDKLoginManagerLoginResult *result,NSError *error)
{
if(error == nil)
{
NSLog(#"Facebook - successfully login %#",result.token);
//login successfully
//do your stuff here
}
}];
}
else
{
//already login
//Do Your stuff here
}

I am saving the access token string and manually setting it on consecutive launches to bypass the re-login flow.
func loginWithFacebook() {
//Check for previous Access Token
if let accessToken = AccessToken.current {
//AccessToken was obtained during same session
getAccountDetails(withAccessToken: accessToken)
}
else if let strAuthenticationToken = UserDefaults.standard.string(forKey: "AccessToken_Facebook") {
//A previous access token string was saved so create the required AccessToken object
let accessToken = AccessToken(authenticationToken: strAuthenticationToken)
//Skip Login and directly proceed to get facebook profile data with an AccessToken
getAccountDetails(withAccessToken: accessToken)
}
else {
//Access Token was not available so do the normal login flow to obtain the Access Token
LoginManager().logIn(readPermissions: [.publicProfile, .email], viewController: nil) { loginResult in
switch loginResult {
case .failed(let error):
print(error)
case .cancelled:
print("User cancelled login.")
case .success(let grantedPermissions,
let declinedPermissions,
let accessToken):
//Save Access Token string for silent login purpose later
let strAuthenticationToken = accessToken.authenticationToken
UserDefaults.standard.set(strAuthenticationToken,
forKey: "AccessToken_Facebook")
//Proceed to get facebook profile data
self.getAccountDetails(withAccessToken: accessToken)
}
}
}
}
func getAccountDetails(withAccessToken accessToken: AccessToken) {
let graphRequest: GraphRequest = GraphRequest(graphPath : "me",
parameters : ["fields" : "id, name, email"],
accessToken : accessToken,
httpMethod : GraphRequestHTTPMethod.GET,
apiVersion : GraphAPIVersion.defaultVersion)
graphRequest.start { (response, result) in
switch result {
case .success(let resultResponse):
print(resultResponse)
case .failed(let error):
print(error)
}
}
}
NOTE: For the ease of this example, the Facebook Access Token string is being saved to UserDefaults but ideally it should be saved to the Keychain.
(Swift 4 / Facebook SDK 4.30)

Related

Log Out Facebook User When Authenticated With Firebase Auth

I have the following code that authenticates a user with Facebook and then with Firebase:
func authenticateWithFacebook() {
FBSDKLoginManager().logIn(withReadPermissions: ["public_profile"], from: self) { (user, error) in // Signs up user with Facebook
if let error = error {
print(error.localizedDescription)
} else if (user!.isCancelled) { // User cancels sign up
print("User Cancelled")
} else {
self.authenticateFacebookUserWithFirebase(credential: FacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString))
}
}
}
func authenticateFacebookUserWithFirebase(credential: AuthCredential) {
Auth.auth().signInAndRetrieveData(with: credential) { (user, error) in
if let error = error {
print(error.localizedDescription)
} else {
print("Success")
}
}
}
This code works as expected. Once the user is authenticated with Firebase, what do I do with the Facebook user that has been "created" in the app? Do I need to keep track of the currentAccessToken and alert Firebase auth when the token expires? If not, do I just leave the code as is or should I log the Facebook user out of my app using the FBSDK? I just don't want a Facebook token floating around in my app.
The user is not logged in to Firebase with Facebook as such. Your app does not get the user's facebook email and password credentials in order to log them into your app's Firebase. Instead it gets the access token for that user and then that token is used to authenticate the user with Firebase. Therefore you cannot log out your user from Facebook but what you can do is invalidate the access token.

How to post to user's facebook feed

I need to post to user's facebook feed.
Based on several other SO questions I came up with the following posting request:
let request = GraphRequest(graphPath: "me/feed", parameters: ["message" : "Hello world"], accessToken: accessToken, httpMethod: .POST, apiVersion: GraphAPIVersion.defaultVersion)
request.start({ (response, requestResult) in
switch requestResult {
case .failed(let error):
print("error in graph request:", error)
case .success(let graphResponse):
if let responseDictionary = graphResponse.dictionaryValue {
print(responseDictionary)
}
}
})
This fails due to
error = {
code = 200;
"fbtrace_id" = GMp2cebddNb;
message = "(#200) Requires either publish_actions permission, or manage_pages and publish_pages as an admin with sufficient administrative permission";
type = OAuthException;
};
Based on the message, the problem seemed to be an easy to solve - all I need is to get either publish_actions, or manage_pages and publish_pages permissions. Based on this SO question, this seemed easy and I ended up in wrapping the code for posting with this:
let loginManager = LoginManager()
loginManager.logIn([PublishPermission.custom("publish_actions")], viewController: self) { (result) in
print(">> \(AccessToken.current?.grantedPermissions)")
switch result {
case .cancelled:
print(">>>> Cancelled")
case .failed(let error):
print(">>>> Error: \(error)" )
case .success(grantedPermissions: _, declinedPermissions: _, token: let accessToken):
print(">>>> Logged in!")
let request = GraphRequest(graphPath: "me/feed", parameters: ["message" : post], accessToken: accessToken, httpMethod: .POST, apiVersion: GraphAPIVersion.defaultVersion)
request.start({ (response, requestResult) in
switch requestResult {
case .failed(let error):
print("error in graph request:", error)
break
case .success(let graphResponse):
if let responseDictionary = graphResponse.dictionaryValue {
print(responseDictionary)
}
}
})
}
}
Now the "funny" part is, that then the facebook SDK shows a page telling me that I previously logged in to my app using Facebook and asks me if I would like to continue. When I press Continue, the SafariViewController dismisses and the .cancelled branch gets executed. What is going on here? I haven't cancelled, nor have I been asked to grant permissions to publish anything on my feed.
P.S.: I tried logging out first (loginManager.logOut() and/or AccessToken.current = nil), in that case the .success branch executes but again with the same error "(#200) Requires either publish_actions permission, or manage_pages and publish_pages as an admin with sufficient administrative permission".
The AccessToken.current.grantedPermissions in that case contains:
Set([FacebookCore.Permission(name: "user_friends"),
FacebookCore.Permission(name: "publish_pages"),
FacebookCore.Permission(name: "user_location"),
FacebookCore.Permission(name: "email"),
FacebookCore.Permission(name: "user_likes"),
FacebookCore.Permission(name: "pages_show_list"),
FacebookCore.Permission(name: "manage_pages"),
FacebookCore.Permission(name: "user_photos"),
FacebookCore.Permission(name: "public_profile"),
FacebookCore.Permission(name: "user_posts"),
FacebookCore.Permission(name: "user_birthday")])
So no publish_actions permission! Why does the login go through successfully while not granting me the permission that I ask for? Moreover, I obviously have "manage_pages" and "publish_pages", so why is that not enough?
https://developers.facebook.com/docs/graph-api/changelog/breaking-changes#4-24-2018
The publish_actions permission has been removed.
Since they do not mention any alternative, there is no way to post to the user feed anymore.

Re-using access token in Firebase 3

I have an iOS app that uses Firebase as a backend for authentication.
Once a user logs in and then closes the app, I don't want the user to have to re-enter their email and password. My approach is to save the access token after a successful login to the Keychain, and then when the user comes back to the app, use the token from the keychain to signin.
I've tried using the method FIRAuth.auth()?.signInWithCustomToken(customToken) { (user, error) in but that's not quite right as that's for when using custom tokens, which is not what I'm doing.
Is there a way for me to do this?
// login with email / password
FIRAuth.auth()?.signInWithEmail(email, password: password, completion: { (firebaseUser, error) in
if error == nil {
FIRAuth.auth()!.currentUser!.getTokenWithCompletion({ (token, error) in
if error == nil {
// save token to keychain
} else {
print(error)
}
})
} else {
print(error)
}
})
// user comes back to app
do {
// get saved token from keychain
if let myToken = try keychain.get("token") {
FIRAuth.auth()?.signInWithCustomToken(myToken, completion: { (user: FIRUser?, error: NSError?) in
if error == nil {
// show post login screen
} else {
}
})
}
} catch {
// error getting token from keychain
}
}
I was approaching this problem in the wrong way. Saving a token is appropriate when using a 3rd party authentication provider, like Facebook, Google, etc and getting an OAuth token in return from one of those services.
In my case when logging in using email and password, a token is not required and instead the password can be securely saved in the Keychain and used later for login.

Using Swift, Parse and the iOS FacebookSDK, how do I obtain user's name and email at login?

So I've been banging my head on this for a couple days now, and I'm hoping someone here could shed some light on this for me!
So I'm using Parse in my Swift project, and am looking to leverage the Facebook integration to make logging in and signing up a user pretty effortless.
I have managed to get as far as logging in a user, but the part where I'm stuck is that I don't know how to access the data that I requested with permissions.
In my AppDelegate Swift file, I have this block of code..
// INITIALISE FACEBOOK
let permissions = ["email", "public_profile"]
PFFacebookUtils.initializeFacebook()
PFFacebookUtils.logInWithPermissions(permissions, {
(user: PFUser!, error: NSError!) -> Void in
if user == nil {
if (error == nil) {
println("User cancelled Facebook login")
} else {
println("FB Login Error \n(error)")
}
} else if user.isNew {
println("User has signed in through Facebook!")
} else {
println("User logged in through Facebook!")
}
})
Everything is all well and dandy, and I get the "User has signed in through Facebook!" message.
According to the Parse documentation, this then creates a new User object in my database.
In my database, I see that there is indeed a new user, but the only fields that are populated are..
ObjectID - random string characters
username - random string characters
authData - a facebook type ID.
Not the username or email.
From what I have gathered, I need to now further leverage the FacebookSDK and GraphUser, but I really dont know exactly how.. or at least not in Swift.
The Parse documentation says
"The Facebook iOS SDK provides a number of helper classes for interacting with Facebook's API. Generally, you will use the FBRequest class to interact with Facebook on behalf of your logged-in user. You can read more about the Facebook SDK here.
Our library manages the user's FBSession object for you. You can simply call [PFFacebookUtils session] to access the session instance, which can then be passed to FBRequests."
But I really dont know what to type or where to type it. :(
It feels like I'm really close with this, but I'm just hitting blanks..
If someone could be kind enough to shed some light into this (how to access the details I requested permission for, i.e. full name and email) I would be incredibly grateful!
Thank you.. :)
You may try this (for Swift):
// Obtain User Data via Facebook Login
func returnUserData()
{
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"name, email"])
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
// Process error
}
else
{
let userName: String = result.valueForKey("name") as! String
let Email: String = result.valueForKey("email") as! String
println(userName)
println(Email)
}
})
}
According to this: https://parse.com/questions/how-can-i-get-the-facebook-id-of-a-user-logged-in-with-facebook
You get the Facebook ID with this Objective-C
// After logging in with Facebook
[FBRequestConnection
startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
NSString *facebookId = [result objectForKey:#"id"];
}
}];
Which is this in Swift (not tested)
FBRequestConnection.startForMeWithCompletionHandler { connection, result, error in
if (!error) {
let facebookId = result["id"]
// use facebookID
}
}

iOS - getting user's Facebook profile picture

I want to get user's profile picture from Facebook in my app. I am aware of the http request that returns the profile picture:
http://graph.facebook.com/USER-ID/picture?type=small
But for that I need the user-id of my user.
Is there a way to fetch that from somewhere without using the Facebook SDK?
If not, can someone show me a simple way to get the user's id (or the profile picture)?
try this... it's working fine in my code .. and without facebook id you cant get ..
and one more thing you can also pass your facebook username there..
//facebookID = your facebook user id or facebook username both of work well
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookID]];
NSData *imageData = [NSData dataWithContentsOfURL:pictureURL];
UIImage *fbImage = [UIImage imageWithData:imageData];
Thanks ..
For getting FacebookId of any user, you will have to integrate Facebook Sdk from where you need to open session allow user to login in to Facebook (If user is already logged in to Facebook app, then it will just take permission from user to get access of the permission). Once you does that, you will get user details of logged in user from where you can get his FacebookId.
For more details please check developers.facebook.com.
In order to get the profile picture you need to know the Facebook user ID of the user. This can be obtained logging into Facebook with Social.framework (if you don't want to use Facebook SDK).
You can use ACAccountStore to request access to user's Facebook account like this:
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *facebookTypeAccount = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
[accountStore requestAccessToAccountsWithType:facebookTypeAccount
options:#{ACFacebookAppIdKey: YOUR_APP_ID_KEY, ACFacebookPermissionsKey: #[#"email"]}
completion:^(BOOL granted, NSError *error) {
...
Please refer to instructions already answered in this post.
For information regarding how to obtain a Facebook App ID key (YOUR_APP_ID_KEY), look at Step 3 in this article.
Though it's an older question, the same you can do with iOS SDK 4.x like:
Swift:
let pictureRequest = FBSDKGraphRequest(graphPath: "me/picture?type=large&redirect=false", parameters: nil)
pictureRequest.startWithCompletionHandler({
(connection, result, error: NSError!) -> Void in
if error == nil {
println("\(result)")
} else {
println("\(error)")
}
})
Objective-C:
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc]
initWithGraphPath:[NSString stringWithFormat:#"me/picture?type=large&redirect=false"]
parameters:nil
HTTPMethod:#"GET"];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection,
id result,
NSError *error) {
if (!error){
NSLog(#"result: %#",result);}
else {
NSLog(#"result: %#",[error description]);
}}];
The answer is:
https://graph.facebook.com/v2.4/me?fields=picture&access_token=[yourAccessToken]
if your token is abcdef
then url will be:
https://graph.facebook.com/v2.4/me?fields=picture&access_token=acbdef
According to API explorer
you can use this link
https://developers.facebook.com/tools/explorer?method=GET&path=me%3Ffields%3Dpicture&version=v2.4
then if you want to get code for any platform make this:
go to the end of page and press "Get Code" button
then in appeared dialog choose your platform
and you will see the code
The following solution gets both essential user information and full size profile picture in one go...
The code uses latest Swift SDK for Facebook(facebook-sdk-swift-0.2.0), integrated on Xcode 8.3.3
import FacebookCore
import FacebookLogin
#IBAction func loginByFacebook(_ sender: Any) {
let loginManager = LoginManager()
loginManager.logIn([.publicProfile] , viewController: self) { loginResult in
switch loginResult {
case .failed(let error):
print(error)
case .cancelled:
print("User cancelled login.")
case .success(let grantedPermissions, let declinedPermissions, let accessToken):
print("Logged in!")
let authenticationToken = accessToken.authenticationToken
UserDefaults.standard.set(authenticationToken, forKey: "accessToken")
let connection = GraphRequestConnection()
connection.add(GraphRequest(graphPath: "/me" , parameters : ["fields" : "id, name, picture.type(large)"])) { httpResponse, result in
switch result {
case .success(let response):
print("Graph Request Succeeded: \(response)")
/* Graph Request Succeeded: GraphResponse(rawResponse: Optional({
id = 10210043101335033;
name = "Sachindra Pandey";
picture = {
data = {
"is_silhouette" = 0;
url = "https://scontent.xx.fbcdn.net/v/t1.0-1/p200x200/13731659_10206882473961324_7366884808873372263_n.jpg?oh=f22b7c0d1c1c24654d8917a1b44c24ad&oe=5A32B6AA";
};
};
}))
*/
print("response : \(response.dictionaryValue)")
case .failed(let error):
print("Graph Request Failed: \(error)")
}
}
connection.start()
}
}
}

Resources