Changing User Email and Password with Swift and Firebase - ios

Here's my edit button for an iOS application with swift and firebase.
var ref = Firebase(url:"https://·············.firebaseio.com")
#IBAction func Done(sender: AnyObject) {
ref.changeEmailForUser("users/\(self.ref.authData.uid)/email",
password: "users/\(self.ref.authData.uid)/provider", toNewEmail: EmailTextField.text)
{ (ErrorType) -> Void in
if ErrorType != nil {
print("There was an error processing the request")
} else {
print("Email changed successfully")
}
}
ref.changePasswordForUser("users/\(self.ref.authData.uid)/email",
fromOld: "users/\(self.ref.authData.uid)/provider", toNew: PasswordTextField.text)
{ (ErrorType) -> Void in
if ErrorType != nil {
print("There was an error processing the request")
} else {
print("Password changed successfully")
}
}
ref.childByAppendingPath("users").childByAppendingPath(self.ref.authData.uid).updateChildValues(["name":self.NameTextField.text!,"about":self.TextView.text!,"Picker":self.PickerVar])
}
When the user clicks the done button I want to update all of his information in the firebase.
Email, Password, Name, etc.
All of the information updated when I click the done button, except for email and password!
It's says there was an error processing the request:
as the picture here
I don't know where 's the error! Did I use the changeEmailForUser and changePasswordForUser functions in a wrong way?
Here's the JSON tree:
{
"users" : {
"7b595e99-b20d-4961-bcf0-6c46956a0cbe" : {
"Picker" : "Student",
"about" : "Hey, I'm Here",
"email" : "mariah#gmail.com",
"name" : "Mariah Khayat",
"provider" : "password"
},
"7eb23db6-6b56-4225-9306-22ed0b935b52" : {
"Picker" : "Teacher",
"about" : "Hi",
"email" : "mmm#gmail.com",
"name" : "Memo",
"provider" : "password"
}
}
}

Accarding to Firebase API making some confusion. after i seen the API https://www.firebase.com/docs/ios/api/#firebase_changeEmailForUserpasswordtoNewEmailwithCompletionBlock There is clearly said:
So for the update Email For user this is not a part a update profile. So as per following you can change your email address:
ref.changeEmailForUser("OldEmailthatYouuserForLogin", password: "correctpassword",
toNewEmail: "newEmail", withCompletionBlock: { error in
if error != nil {
print("There was an error processing the request")
// There was an error processing the request
} else {
print("Email changed successfully")
// Email changed successfully
}
})
// Dont use edited email for the old email and edited password for the password make sure you are using your actually email and password

Mariah,
The problem is your entered email and password details. You are showing the path for your email and password but instead you should enter what calls them such as.
instead of ""users/(self.ref.authData.uid)/email" you should provide it as ref.authData.providerData["email"]as? NSString as? String
for password you need to edit your Firebase rules so user can have access to his own password.

Related

SwiftUI Firebase: Sending a message to the mail

In the function I want to add sending a message to the confirmation email, if you confirm, then you get to Homescreen(), else SignUp().
What do I need to add to my code?
func register(){
if self.email != ""{
if self.pass == self.repass{
Auth.auth().createUser(withEmail: self.email, password: self.pass) { (res, err) in
if err != nil{
self.error = err!.localizedDescription
self.alert.toggle()
return
}
print("success")
UserDefaults.standard.set(true, forKey: "status")
NotificationCenter.default.post(name: NSNotification.Name("status"), object: nil)
}
}
else{
self.error = "Password mismatch"
self.alert.toggle()
}
}
else{
self.error = "Please fill all the contents properly"
self.alert.toggle()
}
}
SignUp() - View where registration takes place
Homescreen() - View where the message about successful registration appears
You can first try to sign the user in with email using following code:
Auth.auth().signIn(withEmail: email, link: self.link) { authResult, error in
// ...
}
If there is an error, present the error alert to the user.
If sign in was successful, you can use the following code to get the current user:
if let user = Auth.auth().currentUser {
}
And inside of the curly brackets just check if current user has already verified the E-Mail with user.isEmailVerified
If that is the case, then simply present your Homescreen
If current user has not verified his E-Mail yet, you can present an alert, where the user can choose if he wants to have the verification E-Mail sent out again. If that is the case, you can simply resend the E-Mail with
user.sendEmailVerification {error in
}
and the user will get his verification E-Mail or there can occur an error while sending the Email so you should handle that error aswell!
Good luck!
You can also check the Firebase docs for Email Verification

Firebase Email Verification Redirect Url

I incorporated Firebase's email verification for my iOS mobile app and am trying to resolve the following issues:
The length of the redirect url appears extremely long. It looks like it repeats itself.
https://app.page.link?link=https://app.firebaseapp.com//auth/action?apiKey%3XXX%26mode%3DverifyEmail%26oobCode%3XXX%26continueUrl%3Dhttps://www.app.com/?verifyemail%253Demail#gmail.com%26lang%3Den&ibi=com.app.app&ifl=https://app.firebaseapp.com//auth/action?apiKey%3XXX%26mode%3DverifyEmail%26oobCode%3XXX%26continueUrl%3Dhttps://www.app.com/?verifyemail%253Demail#gmail.com%26lang%3Den
When I set handleCodeInApp equal to true, and am redirected back to the app when I click on the redirect url, the user's email is not verified. Whereas when I set it to false and go through Firebase's provided web widget, it does get verified. Wasn't able to find documentation that outlined handling the former in swift...
Any thoughts are appreciated.
func sendActivationEmail(_ user: User) {
let actionCodeSettings = ActionCodeSettings.init()
let redirectUrl = String(format: "https://www.app.com/?verifyemail=%#", user.email!)
actionCodeSettings.handleCodeInApp = true
actionCodeSettings.url = URL(string: redirectUrl)
actionCodeSettings.setIOSBundleID("com.app.app")
Auth.auth().currentUser?.sendEmailVerification(with: actionCodeSettings) { error in
guard error == nil else {
AlertController.showAlert(self, title: "Send Error", message: error!.localizedDescription)
return
}
}
}
Make sure you're verifying the oobCode that is part of the callback URL.
Auth.auth().applyActionCode(oobCode!, completion: { (err) in
if err == nil {
// reload the current user
}
})
Once you have done that, try reloading the the user's profile from the server after verifying the email.
Auth.auth().currentUser?.reload(completion: {
(error) in
if(Auth.auth().currentUser?.isEmailVerified)! {
print("email verified")
} else {
print("email NOT verified")
}
})

Firebase unlink email/password auth from user on iOS

I'm trying to unlink email/password authentication from a user in Swift on iOS. I've read the documentation and managed to link and unlink Facebook authentication without a problem. However, after linking email/password credentials successfully, the providerData object is nil. The providerID is "Firebase" but when I pass that to the unlink code the following error is thrown:
Error Domain=FIRAuthErrorDomain Code=17016 "User was not linked to an account with the given provider." UserInfo={NSLocalizedDescription=User was not linked to an account with the given provider., error_name=ERROR_NO_SUCH_PROVIDER}
The unlink code I'm using is:
let providerId = (FIRAuth.auth()?.currentUser?.providerID)!
print("Trying to unlink:",providerId) // providerId = "Firebase"
FIRAuth.auth()?.currentUser?.unlinkFromProvider(providerId) { user, error in
if let error = error {
print("Unlink error:", error)
} else {
// Provider unlinked from account successfully
print("Unlinked...user.uid:", user!.uid, "Anonymous?:", user!.anonymous)
}
}
Reading the docs and having got it working for Facebook, I expected the providerData array to be populated with something after email authentication. So is my linking code wrong (it doesn't throw an error and appears to work fine)?
My linking code:
let credential = FIREmailPasswordAuthProvider.credentialWithEmail(email, password: password)
FIRAuth.auth()?.currentUser!.linkWithCredential(credential) { (user, error) in
if user != nil && error == nil {
// Success
self.success?(user: user!)
dispatch_async(dispatch_get_main_queue(), {
self.dismissViewControllerAnimated(true, completion: nil)
if type == "new" {
print("New user logged in...")
}
if type == "existing" {
print("Existing user logged in...")
}
})
} else {
print("Login error:",error)
self.showOKAlertWithTitle("Login Error", message: error!.localizedDescription)
}
}
Any pointers of how I can modify my approach would be great.
To get the profile information retrieved from the sign-in providers linked to a user, use the providerData property.
if let user = FIRAuth.auth()?.currentUser {
for profile in user.providerData {
// Id of the provider (ex: facebook.com)
let providerID = profile.providerID
}
} else {
// No user is signed in.
}
Calling FIRAuth.auth()?.currentUser?.providerID will result to "firebase".

Permission issue while reading the provider(i.e.Google,Facebook,Firebase) originally used to authenticate user to Firebase 3.0

I am using Firebase 3.0 and Swift v2.2. I ran into an issue when using Firebase's sendPasswordResetWithEmail to allow users to reset their passwords in my iOS app. The issue is that the password reset email gets sent to all users even those users that did not originally sign in with Firebase but instead used the Google sign in button or Facebook sign in button also available in my app. Unfortunately, even if the user gets the email and follows the instructions to reset their password the link provided in the password reset email only resets the password for those accounts that signed in with Firebase; it does not reset their Google or Facebook password. Thus, they still won't be able to sign in.
My solution was to implement the function getAuthProvider() (shown in the code I provided below) to first get the provider which I am saving in my user node in the real time database (as shown below) and depending on the provider I then send the password reset request or show the user an error message stating that they must reset their password with the appropriate provider.
However, the queryEqualToValue call is returning the error Permission Denied. I updated the rules in my Firebase real time database (as shown below). Note, since user's resetting their passwords are non-authenticated users at the time that they request to have their password reset I would like to give them as little access as possible. I do not want to allow them to access user's first name and last name, I would prefer to only give them access to read the provider. What am I missing? Thanks for any input!
Firebase hierarchy:
user
uid
email
firstname
lastname
provider [values: "Firebase" or "Google.com" or "Facebook.com"]
Firebase rules:
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"user": {
"uid": {
"$provider": {
".read": true,
".write": "auth != null"
}
}
}
}
}
ViewController code:
import UIKit
import Firebase
class ResetPasswordTableViewController: UITableViewController, UITextFieldDelegate {
#IBOutlet weak var emailTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
//Hide navigation bar
self.navigationController?.navigationBarHidden = true
//Text fields delegates
self.emailTextField.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//--------------------------------------------------------
// MARK: Hide status bar
//--------------------------------------------------------
override func prefersStatusBarHidden() -> Bool {
return true
}
#IBAction func resetPasswordButtonTapped(sender: AnyObject?) {
//Set user fields
let email = emailTextField.text
// Check for empty fields
if (email!.isEmpty)
{
// Display error message
displayAlertMessage(REQUIRED_FIELDS_ERROR_TITTLE, message: REQUIRED_FIELDS_ERROR_MESSAGE)
return;
}
// Validate email address
if !(UserAccountValidator.validateEmailTextField(email!)) { //if invalid email format
// Display error message
self.displayAlertMessage(REENTER_EMAIL_ERROR_TITLE, message: REENTER_EMAIL_ERROR_TITLE)
return
}
// Get the current user's provider, only those users who were authenticated with Firebase as the provider should be sent a reset password email
let authProvider = getAuthProvider(email!)
if (!authProvider.isEmpty && authProvider == "Firebase") {
FIRAuth.auth()?.sendPasswordResetWithEmail(email!) { error in
// Back to main thread
NSOperationQueue.mainQueue().addOperationWithBlock {
if error != nil {
if let errorCode = FIRAuthErrorCode(rawValue: error!.code) {
switch (errorCode) {
case .ErrorCodeUserNotFound:
self.displayAlertMessage(EMAIL_NOTFOUND_3RDPARTY_ERROR_TITLE, message: EMAIL_NOTFOUND_3RDPARTY_ERROR_MESSAGE);
return
default:
self.displayAlertMessage(ACCOUNT_CREATION_DB_ERROR_TITLE, message: ACCOUNT_CREATION_DB_ERROR_MESSAGE);
return
}
}
} else {
// Present reset password success view
self.performSegueWithIdentifier("resetPasswordSuccessView", sender: self)
}
}
}
} else {
// Display error message
displayAlertMessage("Authentication Provider Mismatch", message: "It looks like you originally signed in with this email using Google or Facebook. Please reset your password with the appropriate provider and then come back and sign in with your new password.")
}
}
//--------------------------------------------------------
// MARK: Local Methods
//--------------------------------------------------------
func getAuthProvider(email: String) -> String {
//Retrieve Authentication Provider for a given UID
var authProvider: String = ""
FIRDatabase.database().reference().child("user").queryEqualToValue(email).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
// Get user value
authProvider = snapshot.value!["provider"] as! String
}) { (error) in
print(error.localizedDescription)
}
return authProvider
}
//--------------------------------------
// MARK: - Display Error Message Methods
//--------------------------------------
func displayAlertMessage(title:String,message:String)
{
let alertMessage = UIAlertController(title: title, message: message, preferredStyle:UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title:"OK", style: .Default, handler:nil);
alertMessage.addAction(okAction);
self.presentViewController(alertMessage, animated: true, completion: nil);
}
}
If I reduce your question, this code:
FIRDatabase.database().reference().child("user").queryEqualToValue(email).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
Fails to read for an unauthenticated user with these rules:
{
"rules": {
".read": "auth != null",
".write": "auth != null",
"user": {
"uid": {
"$provider": {
".read": true,
".write": "auth != null"
}
}
}
}
}
Two things:
the uid in your rules is a literal string. To make the rules under it apply to every child-node of user the name should start with a $, e.g. $uid or $email (the actual name doesn't matter: if it starts with a $ it is a wildcard/variable).
you have uid in your rules, but are passing in a variable named email. I'm not sure if those ever match, but it sounds odd.
you are trying to read a specific user (/user/<myuid>), but are only granting public read access to $provider (/user/<myuid>/<provider>). The read will fail, since you don't grant access. See rules are not filters in the Firebase documentation.

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.

Resources