How to change Twitter accounts with Twitter Kit - ios

I implemented Twitter share button, but there are no column to change Twitter accounts like this picture. And if I logged out from the account, I can't authorize (login) another account. The view to authorize will never appear.
My app doesn't have the 2 columns ("Account" and "Location"). So I can't change Account except I authorized at first.
How can I solve this problem?
(if possible, it's better to be able to authorize accounts when I logout from all accounts after authorized and tweeted once. But I don't know how to do it....)
let shareImage = UIImage(named:"Twitter_shareImage")
let tweetDescription:String = "text"
TWTRTwitter.sharedInstance().sessionStore.saveSessionWithAuthToken(authToken, authTokenSecret: authTokenSecret, completion: { (session, error) -> Void in
if (TWTRTwitter.sharedInstance().sessionStore.hasLoggedInUsers()) {
if let session = TWTRTwitter.sharedInstance().sessionStore.session() {
let composer = TWTRComposer()
composer.setText("text")
composer.setImage(shareImage)
composer.show(from: self.parentViewController()!, completion: nil)
print(session.userID)
} else {
TWTRTwitter.sharedInstance().logIn {
(session, error) -> Void in
if (session != nil) {
// println("signed in as \(session.userName)");
} else {
// println("error: \(error.localizedDescription)");
}
}
print("no account")
}
} else {
// Log in, and then check again
TWTRTwitter.sharedInstance().logIn { session, error in
if session != nil { // Log in succeeded
let composer = TWTRComposer()
composer.setText("text")
composer.setImage(shareImage)
composer.show(from: self.parentViewController()!, completion: nil)
} else {
let alert = UIAlertController(title: "No Twitter Accounts Available", message: "You must log in before presenting a composer.", preferredStyle: .alert)
self.parentViewController()?.present(alert, animated: false, completion: nil)
}
}
}

Related

Firebase Social Login logs into last used Mail account

I have a really weird bug right now. Inside my app the user is able to login with Email/Facebook/Google. This is how the bug occurs:
When I log in via Email and log out again and then use one of the Social-Logins, it logs me into the email account that I used before??? How and why is that happening? It makes absolut no sense for me.
This is for example my facebookLogin- method (google-method works pretty much the same way):
//MARK: Facebook Login
#objc func facebookButtonTapped(){
// disable button tap
self.facebookButton.isEnabled = false
let accessToken = AccessToken.current
LoginManager().logIn(permissions: ["email", "public_profile"], from: self) { (result, error) in
if error != nil {
// some FB error
Utilities.showErrorPopUp(labelContent: "Fehler beim Facebook-Login", description: error!.localizedDescription)
return
}else if result?.isCancelled == true {
// enable button tap
self.facebookButton.isEnabled = true
}else {
// successfull FB-Login
GraphRequest(graphPath: "/me", parameters: ["fields": "id, email, name"]).start { (connection, result, error) in
if error != nil {
// some FB error
Utilities.showErrorPopUp(labelContent: "Fehler beim Facebook-Login", description: error!.localizedDescription)
}else {
print(result!)
// check if user has account
guard let Info = result as? [String: Any] else { return }
let email = Info["email"] as? String
print(email!)
Auth.auth().fetchSignInMethods(forEmail: email!) { (methods, error) in
if error != nil {
// show error popUp
Utilities.showErrorPopUp(labelContent: "Fehler", description: error!.localizedDescription)
} else {
// no error -> check email adress
// enable button tap
self.facebookButton.isEnabled = true
// Email ist noch nicht registriert -> sign up
if methods == nil {
let usernameVC = self.storyboard?.instantiateViewController(withIdentifier: "UsernameVC") as! UserNameVC
usernameVC.accessToken = accessToken
usernameVC.signInOption = "facebook"
self.navigationController?.pushViewController(usernameVC, animated: true)
}
// Email is registered -> login
else {
// set user status to logged-in
UserDefaults.standard.setIsLoggedIn(value: true)
UserDefaults.standard.synchronize()
// enable button tap
self.facebookButton.isEnabled = true
// transition to Home-ViewController
self.transitionToHome()
}
}
}
}
}
}
}
}
I also tested and printed the Auth.auth().currentUser!.uid inside my HomeViewController and it is actually logging into the previous used Email-Account! Super weird...
If anyone can bring some light into this and help me out I am more than grateful!
If there is anything unclear and you need more info just let me know.
Solved the issue.. The problem was that I never actually logged the in. I just checked wether or not the user has an account. I missed to call Auth.auth().signIn

How to change firebase email without reauthentication

I implemented a button in my app that allows the user to change their email using Firebase.
#IBAction func resetEmail(_ sender: Any) {
let alertController = UIAlertController(title: "Change Email", message: "", preferredStyle: .alert)
alertController.addTextField { (textField : UITextField!) -> Void in
textField.placeholder = "Enter New Email Address"
let saveAction = UIAlertAction(title: "Save", style: .default, handler: { (action : UIAlertAction!) -> Void in
//Reset Email
let currentUser = Auth.auth().currentUser
if Auth.auth().currentUser != nil{
currentUser?.updateEmail(to: textField.text!) { error in
if let error = error {
print(error)
} else {
print("CHANGED")
let user = Auth.auth().currentUser
let name = user?.displayName!
let ref = Database.database().reference().child("main").child("users_sen").child(name!).child("email")
ref.setValue(textField.text!)
}
}
}
})
alertController.addAction(saveAction)
}
self.present(alertController, animated: true, completion: {
alertController.view.superview?.isUserInteractionEnabled = true
alertController.view.superview?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.alertClose(gesture:))))
})
}
However, when I run it and I try to change the email it gives me this error:
UserInfo={NSLocalizedDescription=This operation is sensitive and requires
recent authentication. Log in again before retrying this request.
and tells me to re-sign in order to change the email. How do I avoid this? How do I change the email without re-signing in?
This is how I change the password:
// Password updated.
let currentUser = Auth.auth().currentUser
currentUser?.updatePassword(to: textField.text!) { error in
if let error = error {
} else {
// Password updated.
print("success")
}
}
let userEmail = Auth.auth().currentUser?.email
self.currentPassword = textField.text!
let credential = EmailAuthProvider.credential(withEmail: userEmail!, password: textField.text!)
currentUser?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
Base on Firebase's documentation, you need to re-authenticate the user when performing this type of action.
Re-authenticate a user Some security-sensitive actions—such as
deleting an account, setting a primary email address, and changing a
password—require that the user has recently signed in. If you perform
one of these actions, and the user signed in too long ago, the action
fails with an error. When this happens, re-authenticate the user by
getting new sign-in credentials from the user and passing the
credentials to reauthenticateWithCredential.
let user = Auth.auth().currentUser
let credential = EmailAuthProvider.credential(withEmail: "email", password: "password")
user?.reauthenticate(with: credential)
{ error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
user?.updateEmail(to: "newemail")
{ error in
}
}
}
To change user email without re-authentication you can also leverage Cloud Functions. An example course of action could be:
Create a function that accepts user access token and new email address as parameters
In the function, verify access token and get the user ID from it
In the function, call
admin.auth().updateUser(userId, { email: newEmail })
Call the new function from the client
Note: This solution is less secure because the user intent is not verified by additional authentication. Therefore anyone getting hold of the user's device could change their email address.
If you use email and password to authenticate a user you should to do something like this.
You have to re-authenticate user using credential
Re-authenticate user
Update email
Before don't forget to get current user in your class and import Firebase like this :
...
import Firebase
class Blabla {
...
var currentUser: User? {
return Auth.auth().currentUser
}
Then :
func updateUserEmail(newEmail: String, password: String) {
// 1. Get the credential
guard let currentEmail = currentUser?.email else {return}
var credential = EmailAuthProvider.credential(withEmail: currentEmail, password: password)
You can't get directly password, so you must ask to the user his password by a textfield or other.
// 2. Re-authenticate the user
//(To change mail or password, the user must to be authentificate a short time ago !!!)
self.currentUser?.reauthenticate(with: credential, completion: { (result, error) in
if error != nil {
print("ERROR: ", error?.localizedDescription)
return
}
//3. Update email
self.currentUser?.updateEmail(to: newEmail, completion: { (error) in
if error != nil {
print("ERROR: ", error?.localizedDescription)
}else {
//Do something, for example present an alert of confirmation..
}
})
})
All of the code in the same function from the step 1.

iOS SWIFT: Unable to delete user from Firebase Database

I want to delete my current user from Firebase. The authenticated user gets deleted however, I am unable to delete the data for that user in the database. What am i doing wrong?
This is my delete user method....
FIRAuth.auth()?.signIn(withEmail: (emailTextField?.text)! , password: (passwordTextField?.text)!, completion: { (user, error) in
if error == nil {
print("User Authenticate!!!")
let user = FIRAuth.auth()?.currentUser
user?.delete(completion: { (error) in
if error != nil {
print("Error unable to delete user")
} else {
DataService.ds.deleteCurrentFirebaseDBUser()
KeychainWrapper.standard.removeObject(forKey: KEY_UID)
self.performSegue(withIdentifier: "goToLogin", sender: nil)
}
})
} else {
//Password was wrong, unable to authenicate user. Data is not updated
print("!!!ALERT!!! Unable to authenticate user")
let alert = UIAlertController(title: "Incorrect Password", message: "Please re-enter your password", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
})
Firebase Rules:
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
Database:
App
-> users
->
4erkjkl543jfe46
->name
->email
ERRORS:
2017-01-21 21:33:10.321704 APP[11582:4102711] [FirebaseDatabase] setValue: or removeValue: at /users/4erkjkl543jfe46 failed: permission_denied
Optional(Error Domain=com.firebase Code=1 "Permission denied" UserInfo={NSLocalizedDescription=Permission denied})
I'm having the same issue. You are not able to make use of your function deleteCurrentFirebaseDBUser() because the Firebase delete function (if successful) removes the user auth object.
As a result user is not authenticated anymore at the time you want to delete user's data in database with deleteCurrentFirebaseDBUser().
Currently I delete user's data in database before Firebase delete function which is not the ideal solution.
We can delete user from both side authentication and database.But before that we need to reauthenticate user first then we get latest token to delete the user.
Here is the pretty code:
let user = Auth.auth().currentUser
user?.reauthenticate(with:credential) { error in
if let error = error {
// An error happened.
showAlertWithErrorMessage(message: error.localizedDescription)
} else {
// User re-authenticated.
user?.delete { error in
if let error = error {
// An error happened.
showAlertWithErrorMessage(message: error.localizedDescription)
} else {
// Account deleted.
let userID = HelperFunction.helper.FetchFromUserDefault(name: kUID)
Database.database().reference(fromURL: kFirebaseLink).child(kUser).child(userID).removeValue()
try! Auth.auth().signOut()
showAlertWithErrorMessage(message: "Your account deleted successfully...")
return
}
}
}
}
100% working in my project and well tested
for just to delete a child from Firebase use "removeValue()"
var db: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
db = Database.database().reference()
deleteByID()
}
func deleteByID(){
db.child("YOURID").removeValue()
}
Swift 5 | Firebase 8.11.0
As #SvshX said, deleting the user data before deleting the actual user is the only available solution.
The problem with this method is that deleting the user might give an error like AuthErrorCode.requiresRecentLogin, then the data will be deleted but the user will not.
This error is given when the last authentication of the user was more than 5 minuets ago (from Firebase Docs)
So, fixing both of the issues can be achieved by using DispatchGroup and checking the lastSignInDate.
This is my final solution (just call deleteUserProcess()):
let deleteDataGroup = DispatchGroup()
func deleteUserProcess() {
guard let currentUser = Auth.auth().currentUser else { return }
deleteUserData(user: currentUser)
// Call deleteUser only when all data has been deleted
deleteDataGroup.notify(queue: .main) {
self.deleteUser(user: currentUser)
}
}
/// Remove data from Database & Storage
func deleteUserData(user currentUser: User) {
// Check if `currentUser.delete()` won't require re-authentication
if let lastSignInDate = currentUser.metadata.lastSignInDate,
lastSignInDate.minutes(from: Date()) >= -5 {
deleteDataGroup.enter()
Database.database().reference().child(userId).removeValue { error, _ in
if let error = error { print(error) }
self.deleteDataGroup.leave()
}
// Delete folders from Storage isn't possible,
// so list and run over all files to delete each one independently
deleteDataGroup.enter()
Storage.storage().reference().child(userId).listAll { list, error in
if let error = error { print(error) }
list.items.forEach({ file in
self.deleteDataGroup.enter()
file.delete { error in
if let error = error { print(error) }
self.deleteDataGroup.leave()
}
})
deleteDataGroup.leave()
}
}
}
/// Delete user
func deleteUser(user currentUser: User) {
currentUser.delete { error in
if let error = error {
if AuthErrorCode(rawValue: error._code) == .requiresRecentLogin {
reauthenticate()
} else {
// Another error occurred
}
return
}
// Logout properly
try? Auth.auth().signOut()
GIDSignIn.sharedInstance.signOut()
LoginManager().logOut()
// The user has been deleted successfully
// TODO: Redirect to the login UI
}
}
func reauthenticate() {
// TODO: Display some UI to get credential from the user
let credential = ... // Complete from https://stackoverflow.com/a/38253448/8157190
Auth.auth().currentUser?.reauthenticate(with: credential) { _, error in
if let error = error {
print(error)
return
}
// Reload user (to update metadata.lastSignInDate)
Auth.auth().currentUser?.reload { error in
if let error = error {
print(error)
return
}
// TODO: Dismiss UI
// Call `deleteUserProcess()` again, this time it will delete the user
deleteUserProcess()
}
}
}
The minuets function can be added in an extension to Date (thanks to Leo Dabus):
extension Date {
/// Returns the amount of minutes from another date
func minutes(from date: Date) -> Int {
return Calendar.current.dateComponents([.minute], from: date, to: self).minute ?? 0
}
}

Can't get login call back with Facebook SDK swift

I'm trying to login to facebook with facebook sdk but when user authorized the app, it shows blank screen like this and can't get callback.
It used to work fine, but suddenly, it doesn't work.
let manager = FBSDKLoginManager()
manager.logInWithReadPermissions(["public_profile", "email", "user_friends"], fromViewController: self) { (result, error) in
if error != nil {
print(error.localizedDescription)
}
else if result.isCancelled {
print("Facebook login cancelled")
}
else {
let token = FBSDKAccessToken.currentAccessToken().tokenString
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(token)
FIRAuth.auth()?.signInWithCredential(credential, completion: { (user, error) in
if error != nil {
print(error?.localizedDescription)
}
else {
self.performSegueWithIdentifier("MainScreenSignUp", sender: self)
}
})
}
}
Is there anybody who has experience in this area?
I was stuck by the same, it is due to the property "loginBehavior" of the "FBSDKLoginManager", i get it working using: manager.loginBehavior = .Web
By default it uses the ".Native" that gets the result you posted.
Hope it helps!
Excuse my english!

PFFacebookUtils. How to make difference between login and signup?

I'm trying to provide some very simple (as I thought) functionality into my application which uses Parse.com service. What I need is just allow users to create an account via Facebook and login them again via Facebook.
The problem is that PFFacebookUtils login methods not only login users through Facebook but also create a new PFUser. Why is it a problem for me? Well, of course. I can distinguish between signing up and in by isNew field but it doesn't really help.
Consider the following - user tries to login via Facebook (he doesn't any have PFUser yet), he loggs in, a new user is created. I see that the user is new (i.e. the user wasn't registered before) and I have to reject this login. Ok, I reject him, I say "You haven't been registered yet, go and sign up". User signs up (via the same login method) and this time the same PFUser is returned which was created when the user tried to log in. I see that the user is not new, it has already been registered and therefore I have to reject the user again, because the account already exists and it is impossible to create the same account again.
Do you understand the problem? Am I being idiotic not realizing how to deal with PFFacebookUtils account creation and logging in or it is PFFacebookUtils who provides an idiotic API? How do you people do that? How do you solve the problem that I've described. Really, it must be so simple but I can't find a good example anywhere
I have login and signup code in swift that checks to see if a user is new in login and signup. Here is my code:
LOGIN
let spinningActivity = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
spinningActivity.label.text = "Just a Moment"
spinningActivity.detailsLabel.text = "Logging in"
if reachabilityStatus == kNOTREACHABLE {
spinningActivity.hideAnimated(true)
self.displayError("No Internet Connection", message: "Please connect to the internet before continuing")
} else {
let permissions = ["public_profile"]
PFFacebookUtils.logInInBackgroundWithReadPermissions(permissions) { (user:PFUser?, error:NSError?) -> Void in
if error != nil {
spinningActivity.hideAnimated(true)
self.displayError("Error", message: error!.localizedDescription)
} else if let user = user {
if user.isNew {
spinningActivity.hideAnimated(true)
PFUser.currentUser()?.deleteInBackground()
self.displayNoticeWithTwoActions("Account Not Found", message: "This Facebook account is not in our system. You have to sign up first.", firstButtonTitle: "Sign Up",closeButtonTitle: "Ok", segue: "dontHaveAccountSegue")
} else {
spinningActivity.hideAnimated(true)
self.performSegueWithIdentifier("successfulLoginSegue", sender: self)
}
} else {
PFUser.currentUser()?.deleteInBackground()
spinningActivity.hideAnimated(true)
self.displayError("Error", message: "Unless you tapped on 'Cancel' or 'Done', something went wrong. Please try again.")
}
}
}
SIGNUP
I have a signup button and then a function that is implemented into the login button called "loadFacebookUserDetails"
let spinningActivity = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
spinningActivity.label.text = "Just a Moment"
spinningActivity.detailsLabel.text = "Loading Details"
if reachabilityStatus == kNOTREACHABLE {
spinningActivity.hideAnimated(true)
self.displayError("No Internet Connection", message: "Please connect to the internet before continuing")
} else {
let permissions = ["public_profile", "email"]
PFFacebookUtils.logInInBackgroundWithReadPermissions(permissions) { (user:PFUser?, error:NSError?) -> Void in
if let user = user {
if !user.isNew {
spinningActivity.hideAnimated(true)
PFUser.logOut()
self.displayNoticeWithTwoActions("Account Found", message: "This Facebook account already in our system. You have to log in first.", firstButtonTitle: "Log In", closeButtonTitle: "Cancel", segue: "haveAccountSegue")
} else if error != nil {
spinningActivity.hideAnimated(true)
self.displayError("Error", message: error!.localizedDescription)
} else if error == nil {
spinningActivity.hideAnimated(true)
self.loadFacebookUserDetails()
}
}
else {
spinningActivity.hideAnimated(true)
self.displayError("Something Went Wrong", message: "Unless you tapped on 'Cancel' or 'Done', something went wrong. Please try again")
}
}
}
func loadFacebookUserDetails() {
let spinningActivity = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
spinningActivity.mode = MBProgressHUDMode.AnnularDeterminate
spinningActivity.label.text = "Just a Moment"
spinningActivity.detailsLabel.text = "Loading Details"
let requestPerameters = ["fields": "id, email, first_name, last_name, name"]
let userDetails = FBSDKGraphRequest(graphPath: "me", parameters: requestPerameters)
userDetails.startWithCompletionHandler { (connection, result, error:NSError!) -> Void in
if error != nil {
spinningActivity.hideAnimated(true)
self.displayError("Error", message: error!.localizedDescription)
PFUser.logOut()
} else {
let userID:String = result["id"] as! String
let userEmail:String = result["email"] as! String
let userFirstName:String = result["first_name"] as! String
let userLastName:String = result["last_name"] as! String
// Get Facebook Profile Picture
let userProfile = "https://graph.facebook.com/" + userID + "/picture?type=large"
let usernameLink = "https://graph.facebook.com/" + userID
let username = usernameLink.stringByReplacingOccurrencesOfString("https://graph.facebook.com/", withString: "")
let profilePictureUrl = NSURL(string: userProfile)
let profilePictureData = NSData(contentsOfURL: profilePictureUrl!)
if profilePictureData != nil {
let profilePictureObject = PFFile(data: profilePictureData!)
PFUser.currentUser()?.setObject(profilePictureObject!, forKey: "profile_picture")
}
PFUser.currentUser()?.setObject(userFirstName, forKey: "first_name")
PFUser.currentUser()?.setObject(userLastName, forKey: "last_name")
PFUser.currentUser()?.setObject(username, forKey: "facebook_link")
if userEmail == userEmail {
PFUser.currentUser()?.email = userEmail
}
PFUser.currentUser()?.saveInBackgroundWithBlock({ (success:Bool, error:NSError?) -> Void in
if error != nil {
spinningActivity.hideAnimated(true)
self.displayError("Error", message: error!.localizedDescription)
PFUser.logOut()
} else if success == true {
if !userID.isEmpty {
spinningActivity.hideAnimated(true)
NSUserDefaults.standardUserDefaults().setObject("authData", forKey: "facebookAuth")
NSUserDefaults.standardUserDefaults().synchronize()
self.performSegueWithIdentifier("facebookUserDetailsSegue", sender: self)
}
} else {
spinningActivity.hideAnimated(true)
self.displayError("Something Went Wrong", message: "Please try again")
PFUser.logOut()
}
})
}
}
}
If you have trouble with the conversion to objective c, I bet you can find YouTube videos on how to do this.

Resources