XCode - Swift - Facebook Authentication with Parse - ios

I have Facebook authentication integrated with parse using the following code:
PFFacebookUtils.logInInBackgroundWithReadPermissions(permissions) {
(user: PFUser?, error: NSError?) -> Void in
if let user = user {
if user.isNew {
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: nil)
graphRequest.startWithCompletionHandler { (connection, result, error) -> Void in
user["email"] = result.valueForKey("email") as! String
user["firstName"] = result.valueForKey("first_name") as! String
user["lastName"] = result.valueForKey("last_name") as! String
user.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
// goto app
}
}
} else {
// goto app
}
} else {
// Canceled
}
}
So I use Parse's PFFacebookUtils to login the user, then add the extra fields email, firstName and lastName based on the user's Facebook info, then I log the user in. Just wondering if this is the right way to go about adding those additionals fields, and I was also wondering if there was a way to stop an email confirmation from going out in this scenario? I have a register via email option so I need email confirmation, but just not for Facebook login.

Yes as far as I know that is the correct way to set new field. Maybe you want to check for nil in the optionals instead of explicitly unwrapping like that?
And no I think every time you set the email on a PFUser it will send the authentication email if you have that option on in Parse.
One option to save the email without notifying them is to create your own emailId field and set it to that so that they don't get an email asking for authentication and when signing up a new user with email you can also set that field to be the same emailId (it is kind of messy cause you have the same value repeated) but its a workaround worth trying.

Related

How to get AWS Cognito user attributes using AWSMobileClient in iOS?

Question is very simple: I've added user authentication to iOS app using AWS Cognito and AWS Amplify. I have successfully implemented sign in and sign up, but how to get user attributes such as email, full name or phone number?
UPDATE:
For AWSMobileClient ~> 2.12.0, you can fetch user attributes as follows.
AWSMobileClient.default().getUserAttributes { (attributes, error) in
if(error != nil){
print("ERROR: \(error)")
}else{
if let attributesDict = attributes{
print(attributesDict["email"])
print(attributesDict["given_name"])
}
}
}
Per the documentation there are several property helpers for common attributes like username:
AWSMobileClient.getInstance().getUsername()
AWSMobileClient.getInstance().isSignedIn()
AWSMobileClient.getInstance().getIdentityId()
You can also get the JWT token and then pull out any user attributes:
AWSMobileClient.getInstance().getTokens().getIdToken().getTokenString()
You can use the getUserAttributes with the following API in the latest SDK version 2.8.x:
public func getUserAttributes(completionHandler: #escaping (([String: String]?, Error?) -> Void))
You can find the source code here:
https://github.com/aws-amplify/aws-sdk-ios/blob/master/AWSAuthSDK/Sources/AWSMobileClient/AWSMobileClientExtensions.swift#L532
In case you're looking for the email address specifically, and need to do so potentially offline, this would work for you:
AWSMobileClient.sharedInstance().getTokens { (tokens, error) in
if let error = error { print(error.localizedDescription) }
if let tokens = tokens {
let email = tokens.idToken?.claims?["email"] as? String
//completionHandler(email)... etc.
}
While AWSMobileClient.sharedInstance().getUsername() would be convenient, it will return the id of a User Pool user even if the User Pool is set to use email as the username. I consider this a bug, but have yet to report it to AWS.
I also research it on android (Kotlin).
// retrieve username
val username = AWSMobileClient.sharedInstance().username
When you sign in with "email" and "password", "username" is "email".
On the other hand, when the case of iOS (Swift), "username" is really "username" of cognito User Pool, even if you sign in with "email" and "password".
It is so confusing...

Firebase Not Capturing Email/Phone from Facebook

When a user logs into the app using Facebook, I am able to capture and display their full name; however, neither email nor phone number is coming across. I have tried both the "One account per email address" as well as "Multiple accounts per email". I have tested it with an account whose email address is definitely not already registered in Firebase. What am I missing such that email/phone are not being captured? This all does work with Google accounts.
let name = Auth.auth().currentUser?.displayName // works!
let email = Auth.auth().currentUser?.email // nil- why?
let phone = Auth.auth().currentUser?.phoneNumber // nil -why?
The login process, which is standard Firebase w/ Facebook, looks like this:
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!)
{
if let error = error
{
print(error.localizedDescription)
}
else
{
if FBSDKAccessToken.current() != nil
{
let credential = FacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error
{
print (error.localizedDescription)
}
}
}
}
}
To update based on comments below. The following also produces a nil email and phone when inspecting the contents of userInfo. I understand the phone might just be that way, but it seems the email was supposed to work.
let userInfo = Auth.auth().currentUser?.providerData
The top level phoneNumber currentUser.phoneNumber is only for Firebase verified phone numbers. If you have that Facebook phone number, you can use the currentUser.updatePhoneNumber API to verify that number (you will need to go through the whole flow). Otherwise, you can wait for the upcoming Admin node.js API to set phone numbers with Admin privileges on existing users: https://github.com/firebase/firebase-admin-node/commit/68563c4b2c8128fbc45fc65bad3f6730d320b539
As for the email, in the case of "multiple accounts per email" you need to set it yourself via currentUser.updateEmail. You can get the Facebook email from currentUser.providerData which contains the Facebook provider data.

Facebook login using Firebase - Swift iOS

I'm implementing login with Facebook using Firebase, I have this code which searches my database after a successful facebook authentication for the email if exists in database and logs in the app if found, I want to direct the user to registration view controller if not found but its not working since this method is asynchronous. I appreciate if anyone can help. Here is my code :
func getFacebookUserInfo() {
if(FBSDKAccessToken.current() != nil){
let graphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields" : "id,name,gender,email,education"])
let connection = FBSDKGraphRequestConnection()
connection.add(graphRequest, completionHandler: { (connection, result, error) -> Void in
let data = result as! [String : AnyObject]
let email = data["email"] as? String
let emailRef = FIRDatabase.database().reference().child("usernameEmailLink")
emailRef.queryOrderedByValue().observe(.childAdded, with: { snapshot in
if let snapshotValue = snapshot.value as? [String: AnyObject] {
for (key, value) in snapshotValue {
if(value as? String == email){
self.stringMode = snapshotValue["mode"]! as! String
self.username = key
self.parseUserInfoFromJSON()
return
}
}
}
})
})
connection.start()
}
}
Thank you.
The registration/existence of the user in Firebase should probably be determined before the graphRequest code in the question.
Most importantly, (and this is critical), email addresses are dynamic so they should not be used to verify if a user exists. i.e. user with email address of 'leroy#gmail.com' updates his email to 'leroy.j#gmail.com'. If emails are used to verify registration, it can totally break if that email changes.
Please use Firebase uid's for that purpose as they are static and unique.
Since we only have a small snippet of code, we don't know the exact sequence being used. This answer is pseudo-code to outline a possible sequence.
We assume that by 'registered' it means that the user has gone through some kind of app registration sequence and the user has been created (and now exists/is registered) in Firebase.
In general there would be a login button and a delegate method to handle the actual login action.
The user enters their login and taps the login button
func loginButton(loginButton: FBSDKLoginButton!,
didCompleteWithResult result: FBSDKLoginManagerLoginResult!,
error: NSError?) {
Firebase can then get the credentials for that user (see Firebase doc quote below)
let credential = FIRFacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
At that point, sign in the user and check to see if they are registered (exist) in the Firebase user node.
FIRAuth.auth()?.signIn(with: credential) { (user, error) in
if let error = error { //failed due to an error
return
}
let uid = user.uid //the firebase uid
let thisUserRef = userRef.child(uid) //a reference to the user node
//check to see if the user exists in firebase (i.e. is Registered)
thisUserRef.observeSingleEvent(of: .value, with: { (snapshot) in
//if snapshot exists
//then the user is already 'registered' in the user node
// so continue the app with a registered user
//if not, then need to have the user go through a registration sequence and
// then create the user (make them registered) in the user node
doRegisterUser(user)
})
func doRegisterUser(user: FIRUser) {
//get what you need from the user to register them
// and write it to the users node. This could be from additional
// questions or from their Facebook graph, as in the code in the
// question
//for this example, we'll just write their email address
let email = user.email
let dict = ["email": email]
//create a child node in the users node with a parent of uid
// and a child of email: their email
thisUserRef.setValue(node)
//next time the user logs in via FB authentication, their user node
// will be found as they are now a 'registered' user
}
From the Firebase docs
After a user signs in for the first time, a new user account is
created and linked to the credentials—that is, the user name and
password, or auth provider information—the user signed in with. This
new account is stored as part of your Firebase project, and can be
used to identify a user across every app in your project, regardless
of how the user signs in.
As I mentioned, this is very pseudo code but offers a possible sequence for a solution.

Parse + Facebook Questions

I am trying to implement Parse + Facebook
Here is what I would like to do:
User Log In with Facebook
Login Authorized
A new User created from Facebook properties (gender, age, name, etc)
Below is my code which logs in to Facebook using PFFacebookUtils. The code successfully created a User on my Parse, but I don't have those details of the User from Facebook which I want to use.
I would like to get the user's name, gender, hometown, etc on Facebook.
How do I achieve that??
var permissionArray = ["public_profile","user_friends","email","user_birthday", "user_work_history", "user_education_history", "user_hometown", "user_location", "user_likes"]; on Facebook
PFFacebookUtils.logInInBackgroundWithReadPermissions(permissionArray) { (user: PFUser?, error: NSError?) -> Void in
if user != nil {
if user!.isNew {
println("New User");
}
else {
println("Old User");
}
println(user?.username);
}
else {
println("Login Cancel")
}
}
You need to request the users "me" data, and then you can use that to populate your user object. So, in your if user!.isNew { you'll need to do something along the lines of...
let fbDetailsRequest = FBRequest.requestForMe()
fbDetailsRequest.startWithCompletionHandler { connection, result, error in
if error != nil {
// Handle the error
}
if let graph = result as? FBGraphObject {
// Now you can fill out the data on your user object
user!.setObject(graph.objectForKey("email")!, forKey: "email")
user!.setObject(graph.objectForKey("gender")!, forKey: "gender")
// Don't forget to save afterwards...
user!.saveInBackgroundWithBlock(nil)
}
}
To see the fields you can grab, check out https://developers.facebook.com/docs/graph-api/reference/user If you need anything other than "core" items, you might have to have extra permissions.

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
}
}

Resources