Requesting user's email from Twitter - swift

I am trying to request a user's email address from Twitter and put it into Firebase. I got my app whitelisted by Twitter and activated it in the apps.twitter.com portal. As per the Fabric documentation here, I wrote this code out:
let twitterEmailClient = TWTRAPIClient.clientWithCurrentUser()
let twitterEmailRequest = twitterEmailClient.URLRequestWithMethod("GET", URL: "https://api.twitter.com/1.1/account/verify_credentials.json", parameters: ["include_email": "true", "skip_status": "true"], error: nil)
twitterEmailClient.sendTwitterRequest(twitterEmailRequest, completion: { (TWTREmailClientResponse: NSURLResponse?, TWTREmailClientEmail: NSData?, TWTREmailClientError: NSError?) in
if TWTREmailClientError != nil {
print("Twitter Email Client Error - \(TWTREmailClientError!.code): \(TWTREmailClientError!.localizedDescription)")
} else if TWTREmailClientResponse == nil {
print("Twitter Email Client Error - valid connection not available")
} else if TWTREmailClientEmail != nil {
print("Twitter Client Email - \(String(data: TWTREmailClientEmail!, encoding: NSUTF8StringEncoding))")
FIRAuth.auth()?.currentUser?.updateEmail("\(TWTREmailClientEmail)", completion: { (updateEmailError: NSError?) in
if updateEmailError != nil {
print("Set Email from Twitter Error - \(updateEmailError)")
}
})
}
})
Again, as per Fabric's docs, I should get a JSON result including the 'email'. Here is the result:
Twitter Client Email -
Optional("{\"id\":560366005,\"id_str\":\"560366005\",\"name\":\"Dan
Levy\",\"screen_name\":\"DanLevy114\",\"location\":\"Buffalo,
NY\",\"description\":\"Florida Tech \'20, Amherst \'16, iOS
Developer\",\"url\":\"https:\/\/t.co\/KOtATAEV3X\",\"entities\":{\"url\":{\"urls\":[{\"url\":\"https:\/\/t.co\/KOtATAEV3X\",\"expanded_url\":\"http:\/\/Instagr.am\/danlevy114\",\"display_url\":\"Instagr.am\/danlevy114\",\"indices\":[0,23]}]},\"description\":{\"urls\":[]}},\"protected\":false,\"followers_count\":292,\"friends_count\":196,\"listed_count\":4,\"created_at\":\"Sun
Apr 22 15:20:46 +0000
2012\",\"favourites_count\":1151,\"utc_offset\":-10800,\"time_zone\":\"Atlantic
Time
(Canada)\",\"geo_enabled\":true,\"verified\":false,\"statuses_count\":1305,\"lang\":\"en\",\"contributors_enabled\":false,\"is_translator\":false,\"is_translation_enabled\":false,\"profile_background_color\":\"C0DEED\",\"profile_background_image_url\":\"http:\/\/pbs.twimg.com\/profile_background_images\/743634202\/69dd45bc569542274b017cc25c1e464d.png\",\"profile_background_image_url_https\":\"https:\/\/pbs.twimg.com\/profile_background_images\/743634202\/69dd45bc569542274b017cc25c1e464d.png\",\"profile_background_tile\":false,\"profile_image_url\":\"http:\/\/pbs.twimg.com\/profile_images\/745047852796289024\/BWFfrEoI_normal.jpg\",\"profile_image_url_https\":\"https:\/\/pbs.twimg.com\/profile_images\/745047852796289024\/BWFfrEoI_normal.jpg\",\"profile_banner_url\":\"https:\/\/pbs.twimg.com\/profile_banners\/560366005\/1466468226\",\"profile_link_color\":\"0084B4\",\"profile_sidebar_border_color\":\"FFFFFF\",\"profile_sidebar_fill_color\":\"33FF33\",\"profile_text_color\":\"E05151\",\"profile_use_background_image\":false,\"has_extended_profile\":true,\"default_profile\":false,\"default_profile_image\":false,\"following\":false,\"follow_request_sent\":false,\"notifications\":false}")
I logged in with my Twitter account and an email did not appear. I know I have an email associated with my Twitter account. Any ideas?

I had the same problem with Twitter. First of all change Permissions in dev.twitter to "Read only".
Next, use this code to login with Twitter:
Twitter.sharedInstance().logInWithCompletion() { session, error in
if let session = session {
let credential = FIRTwitterAuthProvider.credentialWithToken(session.authToken, secret: session.authTokenSecret)
FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in
if let error = error {
Alert.sharedInstance.showAlert("Error", message: error.localizedDescription)
return
}
//Logged!
}
} else {
Alert.sharedInstance.showAlert("Error", message: error!.localizedDescription)
}
}
This work without request to Twitter.
API Key and API Secret in Firebase Console get from dev.twitter.

Related

Trying to access https://api.github.com/user but it is returning status code of 401

I am working on creating Github third-party OAuth application. I get an email like this, which shows that my app is authorized:
A third-party OAuth application (Dev Snapshot) with read:user,
repo:status, user:email, and user:follow scopes was recently
authorized to access your account.
I am also signed in under my Github account via Firebase. However, I cannot access this endpoint: https://api.github.com/user
I get 401 response, meaning I am not authorized.
Here is the code that signs me in my app:
provider.customParameters = [
"allow_signup": "true"
]
provider.scopes = ["read:user","user:email","user:follow","repo:status"]
provider.getCredentialWith(nil) { credential, error in
if error != nil {
print("error: \(error!.localizedDescription)")
}
if credential == nil {
print("no credential")
}
if credential != nil {
Auth.auth().signIn(with: credential!) { [self] authResult, error in
if error != nil {
// Handle error.
print("error sign in: \(error?.localizedDescription)")
}
// User is signed in.
// IdP data available in authResult.additionalUserInfo.profile.
guard let oauthCredential = authResult?.credential as? OAuthCredential else {
print("oauth error")
return
}
print("signed in")
// GitHub OAuth access token can also be retrieved by:
// oauthCredential.accessToken
// GitHub OAuth ID token can be retrieved by calling:
// oauthCredential.idToken
configure()
tableView.reloadData()
}
}
}

iOS Google Auth: "The password is invalid or the user does not have a password"

My app has three login options: email, google, and facebook, all via Firebase. Everything works perfectly except for logging in eith email.
Recreate the problem
Create a user with email (first name, last name, email, password), (lets say with the email, test1#gmail.com)
Sign out and return back to login page
Sign in with google using the same email (test1#gmail.com)... this will give an alert that says that a user already uses that email. It does not redirect user to the home screen.
Sign in with email again (same email and password)
User cannot sign in now. User is shown the warning that "The password is invalid or the user does not have a password"
In Firebase Authentication, the user who used to have an email sign next to it now has a Google sign next to it. However, in Firestore, the firstName, lastName, email, password, and uid is still stored.
My code
Sign in with Google
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let err = error {
print("Failed to log into Google: ", err)
return
}
print("Successfully logged into Google")
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken)
// sign user in with Firebase
Auth.auth().signIn(with: credential, completion: { (user, error) in
let firstName = user?.user.displayName
let email = user?.user.email
let lastName = ""
let uid = user?.user.uid
if let err = error {
print("Failed to create a Firebase User with Google account: ", err)
return
} else {
// Successfully logged in
print("Successfully logged into Firebase with Google email: ", email ?? "", "Now add user to Firestore if user is new.")
// check if user already exists
self.addUserToFirestore(firstName ?? "", lastName, email ?? "", uid ?? "", "Google")
}
})
}
func addUserToFirestore(_ firstName:String, _ lastName:String, _ email:String, _ uid:String, _ signInMethod:String) {
let db = Firestore.firestore()
let docRef = db.collection("users").document(uid)
// check if user exists in firestore
docRef.getDocument { (document, error) in
if let document = document {
if document.exists {
let message = "Good news! You already have a Coal account that uses " + email + ".\nPlease sign in to your existing account. Then you will be able to link your " + signInMethod + " profile from your Account Settings page."
// user exists. send to chats screen.
print("User already exists. Document data: \(String(describing: document.data()))")
self.showError("You're already a member!", message)
} else {
// user does not exist. create a new user
print("Document does not exist. Create new user.")
docRef.setData(["firstname":firstName, "lastname":lastName, "email":email]) { err in
if err != nil {
// Show error message
print("Error saving user data to Firestore")
} else {
print("New user created in Firestore")
self.transitionToConvo()
}
}
}
}
}
}
Sign in with email function
// Signing in the user
Auth.auth().signIn(withEmail: email, password: password) { (result, error) in
if error != nil {
// Couldn't sign in
self.loginErrorLabel.text = error!.localizedDescription
self.loginErrorLabel.alpha = 1
}
else {
// user signed in successfully, go to TabBarController
print("User signed in")
let tabBarC = self.storyboard?.instantiateViewController(withIdentifier: "mainTabBarController") as! TabBarController
tabBarC.modalPresentationStyle = .fullScreen
self.present(tabBarC, animated: true, completion: nil)
print("Switched to TabBarController")
}
}
Sign up with email
// Create the user
Auth.auth().createUser(withEmail: email, password: password) { (result, err) in
// there is an error creating user
if err != nil {
self.showErrorLeft("There was an issue creating your account.\n\nPossible reasons for this isssue:\n1) Your email is not formatted correctly.\n2) You already have a Coal account that uses " + email + ". Please sign in to your existing account.\n\nIf you think this is a mistake, please contact as at coal.britto#gmail.com.")
}
else {
let uid = result!.user.uid
// User was created successfully, now store the first name, last name, email
print("new email created in firestore", uid)
db.collection("users").document(uid).setData(["firstname":firstName, "lastname":lastName, "email":email, "password": password, "uid":uid]) { err in
if err != nil {
// Show error message
print("Error saving user data to Firestore")
} else {
print("New user created in Firestore")
self.transitionToConvo()
}
}
// Transition to the chats screen
self.transitionToConvo()
}
}
A User signing in with Google on an E-Mail address that already exists as an E-Mail/PW User will not throw an error. Instead the user is transitioned into a Google Signin User.
So the behaviour you experience is intended by Firebase.
The reason why the password etc. is still stored in your firestore database is because you dont overwrite it with the new data in your code.
Your code after a Google signin sees that there is a document for that user already (the one you created in your createUser function) so nothing happens in your db.
NOTE: Please do not store clear text passwords in your database.

iOS Firebase Twitter Login asks for Permission Every Time

I am able to create an account and login via Twitter; however, with each login attempt the user is taken to a Twitter web page where they have to login with their credentials. Given this happens every time, it negates the value of this kind of social login. I have Facebook and Google logins working without this issue on Firebase. Below is the code used to login:
private func loginWithTwitter() {
TWTRTwitter.sharedInstance().logIn { (session, error) in
guard error == nil && session != nil else {
self.spinner.dismiss()
Utilities.displayAlert(title: "Login Error", msg: error?.localizedDescription ?? "Could not login with Twitter at this time.", controller: self)
return
}
let credential = TwitterAuthProvider.credential(withToken: session!.authToken, secret: session!.authTokenSecret)
Auth.auth().signInAndRetrieveData(with: credential, completion: { (result, error) in
if let error = error {
self.spinner.dismiss()
self.present(self.sharedManager.getAlertWith(title: "Login Error", andMsg: error.localizedDescription), animated: true)
return
}
self.loadUserInfo()
})
}
}
I should also add that I do have the use for logins box checked on the Twitter Developers page.
UPDATE:
Based on the post of Pratik below and here is a modified version that does seem to work. However, I am not sure if it's the right long-term answer. I wonder about token expiration for example. Thoughts on if there is a better way than this?
private func loginWithTwitter() {
let token = TWTRTwitter.sharedInstance().sessionStore.session()?.authToken
let secret = TWTRTwitter.sharedInstance().sessionStore.session()?.authTokenSecret
guard token != nil && secret != nil else {
self.present(self.sharedManager.getAlertWith(title: "Login Error", andMsg: "Could not login with Twitter. Please try again."), animated: true)
return
}
let credential = TwitterAuthProvider.credential(withToken: token!, secret: secret!)
Auth.auth().signInAndRetrieveData(with: credential, completion: { (result, error) in
if let error = error {
self.spinner.dismiss()
self.present(self.sharedManager.getAlertWith(title: "Login Error", andMsg: error.localizedDescription), animated: true)
return
}
self.loadUserInfo()
})
}
I'm not sure with will help or not, but im using twitter login in my app and once i authenticate with twitter i used below function to when user open app again for validation.
func silentLoginWithTwitter()
{
let client = TWTRAPIClient.withCurrentUser()
let request = client.urlRequest(withMethod: "GET", urlString: "https://api.twitter.com/1.1/account/verify_credentials.json", parameters: ["include_email": "true", "skip_status": "true"], error: nil)
client.sendTwitterRequest(request, completion: { (response:URLResponse?, data:Data?, error:Error?) in
if error == nil
{
// Open main page of application
}else
{
print("Error: Twitter : \(String(describing: error))")
// Open Login page again
}
})
}
OR you can check session auth
if let authSession = TWTRTwitter.sharedInstance().sessionStore.session()?.authToken {
TWTRTwitter.sharedInstance().sessionStore.isValidOauthToken(authSession)
}
Hope this will help

Managing user Presense 'onDisconnect' Firebase and Swift?

I have built authentication into my app using Firebase/Facebook Auth and now I would like to build into my app a system for managing a user's presence (eg; updating a 'status' field on Firebase for the user to say 'online or offline') as described in the Firebase Docs here
*My question is... where do I put all this managing presense code?, here is my 'signIntoFirebase' function. would I create a separate function called 'manageUserPresense' and call it straight after 'firebaseLoginComplete'?
func signIntoFirebase(firebaseLoginComplete: #escaping (_ status: Bool, _ error: Error?) -> ()){
//Getting FB authentication
guard let authenticationToken = AccessToken.current?.authenticationToken else { return }
//Getting credential using authentication token
let credential = FacebookAuthProvider.credential(withAccessToken: authenticationToken)
//Signing in to Firebase using FB authentication token
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error { //if there is an error
print (error)
firebaseLoginComplete(false, error)
return
}
firebaseLoginComplete(true, nil)
self.isLoggedIn = true
print ("Successfully authenticated into Firebase")
}
}//end func

Facebook Login Error - Xcode 8 GM

I am using the latest Facebook SDK and I get this error when I run the block of code below: Facebook signup error - The operation couldn’t be completed. (com.facebook.sdk.login error 308.)
Here is my code:
func signupWithFacebook() {
FBSDKLoginManager().logIn(withReadPermissions: ["public_profile"], from: self) { (result, error) in
if let error = error {
print("Facebook signup error - \(error.localizedDescription)")
} else if result != nil {
let credential = FIRFacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
self.facebookSignup = true
self.addUserToAuth(credential, twitterUserID: "")
}
}
}
I Figured it out! It has to do with the way Apple deals with Keychain. All you have to do is go into the "Compatibilities" tab under the target for your app and turn "Keychain Sharing" on. Here is a more fulfilling answer.