Verification code always invalid using Swift Parse and Twilio - swift

Im using SMS verification to verify users. My problem is that when I enter a code to verify I get invalid code. I can't for the life of me figure out why.
Calling cloud code function:
#IBAction func verifyCodeButtonTapped(sender: AnyObject) {
var verificationCode: String = verificationCodeTextField.text!
let textFieldText = verificationCodeTextField.text ?? ""
if verificationCode.utf16.count != 4 {
displayAlert("Error", message: "You must entert the 4 digit verification code sent yo your phone")
} else {
let params = ["verifyPhoneNumber" : textFieldText]
PFCloud.callFunctionInBackground("verifyPhoneNumber", withParameters: params, block: { (object: AnyObject?, error) -> Void in
if error == nil {
self.performSegueWithIdentifier("showVerifyCodeView", sender: self)
} else {
self.displayAlert("Sorry", message: "We couldnt verify you. Please check that you enterd the correct 4 digit code sent to your phone")
}
})
}
}
Cloud code to verify code:
Parse.Cloud.define("verifyPhoneNumber", function(request, response) {
var user = Parse.User.current();
var verificationCode = user.get("phoneVerificationCode");
if (verificationCode == request.params.phoneVerificationCode) {
user.set("phoneNumber", request.params.phoneNumber);
user.save();
response.success("Success");
} else {
response.error("Invalid verification code.");
}
});

Twilio developer evangelist here.
In the Parse code, you are expecting request.params.phoneVerificationCode but when you call the cloud function from iOS you let params = ["verifyPhoneNumber" : textFieldText].
So, either change that line to
let params = ["phoneVerificationCode" : textFieldText]
so that it matches the cloud code. Or change your cloud code to
if (verificationCode == request.params.verifyPhoneNumber) {
so that it matches the iOS code.

Related

Firebase Re-authentication error: Contextual closure type '(AuthDataResult?, Error?) -> Void' expects 2 arguments, but 1 was used in closure body

So, I tried to add a delete account function using Firebase Manage User service here, it's showing me error. Below is the code.
#IBAction func deleteAccount() {
let user = Auth.auth().currentUser
var credential: AuthCredential
user?.reauthenticate(with: credential) { error in
if let error = error {
self.redAlert(message: "Unable to authenticate. Please try again.")
} else {
Auth.auth().currentUser?.delete { error in
if let error = error {
self.redAlert(message: "An error happened. Please contact support to delete your account")
} else {
self.greenAlert(message: "Your account has been deleted")
}
}
}
}
Attached the screenshot as well.
It keeps showing "Contextual closure type '(AuthDataResult?, Error?) -> Void' expects 2 arguments, but 1 was used in closure body" which I really don't know how to fix it. Would you mind helping to pinpoint this?
Screenshot
EDIT: I actually found the solution. The right code should be:
#IBAction func deleteAccount(_send: Any) {
let user = Auth.auth().currentUser
let emailText: String = email.text!
let passwordText: String = password.text!
var credential: AuthCredential = EmailAuthProvider.credential(withEmail: emailText, password: passwordText)
user?.reauthenticate(with: credential, completion: { (result, error) in
if let error = error {
self.redAlert(message: "Unable to authenticate. Please try again.")
} else {
Auth.auth().currentUser?.delete { error in
if let error = error {
self.redAlert(message: "An error happened. Please contact support to delete your account")
} else {
self.greenAlert(message: "Your account has been deleted")
self.stopLoading()
self.logout()
}
}
}
})
}
Hope it helps anyone that is struggling!

SwiftUI: Check if Firebase RealtimeDatabase has a specific Child the register the value or return error

I am currently building an app with an account system.
Firebase is very new to me, that's why I watched a lot of tutorials, and now its working fine.
I want to implement that the user can choose a unique username at the registration. My problem is, I really don't know how to check if this name is already taken.
I found some code for that, but that's not working, I will show you the code for the RegistrationService file.
I hope someone can explain to me how to implement this username verification. It should return an error if the username is already taken and do continue the registration if its a valid username.
Thank you!
import Combine
import Firebase
import FirebaseDatabase
import Foundation
enum RegistrationKeys: String {
case firstName
case lastname
case info
case username
}
protocol RegisterService {
func register(with details: RegistrationDetails) -> AnyPublisher<Void, Error>
}
final class RegisterServiceImpl: RegisterService {
func register(with details: RegistrationDetails) -> AnyPublisher<Void, Error> {
Deferred {
Future { promise in
Auth.auth()
.createUser(
withEmail: details.email,
password: details.password
) { res, error in
if let err = error {
promise(.failure(err))
} else {
// Success on User creation
if let uid = res?.user.uid {
let values =
[
RegistrationKeys.firstName.rawValue: details.firstName,
RegistrationKeys.lastname.rawValue: details.lastName,
RegistrationKeys.info.rawValue: details.info,
] as [String: Any]
let db = Database.database(url: "theurl")
Database.database(url: "the url")
.reference()
.child("usernames")
.child("\([RegistrationKeys.info.rawValue: details.username] as [String : Any])")
// here should be the check and then continue if its valid
db
.reference()
.child("users")
.child(uid)
.updateChildValues(values) { error, ref in
if let err = error {
promise(.failure(err))
} else {
promise(.success(()))
}
}
} else {
promise(.failure(NSError(domain: "Invalid user ID", code: 0, userInfo: nil)))
}
}
}
}
}
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
}
I can see two possibilities to solve your problem:
If the e-mail can serve as the username
Firebase authentication already sends back an error message in case the e-mail (the one used when creating the user) already exists. If the e-mail passed in the following function is not unique, an error will be thrown:
Auth.auth()
.createUser(
withEmail: details.email,
password: details.password
) { res, error in
if let err = error {
promise(.failure(err))
If an additional username besides the e-mail is required
If you need usernames in addition to the e-mails, you can store them under a node "usernames", like we see in your example. Personally, I would hash them instead of storing them plain.
The structure could simply be:
{
usernames: {
username_1: true,
username_2: true,
...
username_n: true
}
}
The example below checks to see if a new username exists and stores the result in the variable isUsernameTaken:
let db = Database.database(url: "the url").reference()
let newUsername = "seeIfItIsTaken"
db.child("usernames").child(newUsername).getData() { error, snapshot in
guard error == nil else {
print("Found error \(error)")
return
}
let isUsernameTaken = snapshot.exists()
}

I cannot get the AWS Cognito credentials of a user (swiftUI)

I have tried a couple of different things, and at this point I am stumped. I simply want to be able to access the user's email to present it in a view. However I have not been able to successfully present, much less retrieve, this information. Here are the two pieces of code I have tried with:
func getUsername() -> String? {
if(self.isAuth) {
return AWSMobileClient.default().username
} else {
return nil
}
}
and
func getUserEmail() -> String {
var returnValue = String()
AWSMobileClient.default().getUserAttributes { (attributes, error) in
if(error != nil){
print("ERROR: \(String(describing: error))")
}else{
if let attributesDict = attributes{
//print(attributesDict["email"])
self.name = attributesDict["name"]!
returnValue = attributesDict["name"]!
}
}
}
print("return value: \(returnValue)")
return returnValue
}
Does anyone know why this is not working?
After sign in try this:
AWSMobileClient.default().getTokens { (tokens, error) in
if let error = error {
print("error \(error)")
} else if let tokens = tokens {
let claims = tokens.idToken?.claims
print("claims \(claims)")
print("email? \(claims?["email"] as? String ?? "No email")")
}
}
I've tried getting the user attributes using AWSMobileClient getUserAttributes with no success. Also tried using AWSCognitoIdentityPool getDetails With no success. Might be an error from AWS Mobile Client, but we can still get attributes from the id token, as seen above.
If you are using Hosted UI, remember to give your hosted UI the correct scopes, for example:
let hostedUIOptions = HostedUIOptions(scopes: ["openid", "email", "profile"], identityProvider: "Google")
It is because it is an async function so will return but later than when the function actually ends with the value. Only way I found to do it is placing a while loop and then using an if condition.

how to handle an exception in change password?

Function which change password in my app:
#IBAction func changePassword(_ sender: Any) {
let isMatched = NSPredicate(format:"SELF MATCHES %#", regexAllValidation).evaluate(with: newPasswordField.text)
if (isMatched == true){
let auth = Auth()
auth.changePassword(oldPassword: oldPasswordField.text!,newPassword: newPasswordField.text!)
displayAlert(title: "Correct password", message: "Password changed!")
}
}
Class Auth:
func changePassword(oldPassword: String, newPassword: String){
let user =
AppDelegate.defaultUserPool().currentUser()?.changePassword(oldPassword, proposedPassword: newPassword)
AppDelegate.defaultUserPool().currentUser()?.clearSession()
}
When I call this function with bad old password, I receive in console:
Response body:
{"__type":"NotAuthorizedException","message":"Incorrect username or password."}
"x-amzn-errormessage" = "Incorrect username or password.";
"x-amzn-errortype" = "NotAuthorizedException:";
I would like to display an alert informing the user of a wrong password.
how do it?
Not sure which framework you are using, but if you are using swift and cognito, then I recommend the amplify framework, more specifically AWSMobileClient.
It is filled with common functionalities and heavily documented with common scenario like what you mentioned.
Link: https://aws-amplify.github.io/docs/sdk/ios/authentication
In short, you should have completion closure where you could handle the different scenarios and display alert if needed. For example, using AWSMobileClient you can
AWSMobileClient.default().confirmForgotPassword(username: "my_username", newPassword: "MyNewPassword123!!", confirmationCode: "ConfirmationCode") { (forgotPasswordResult, error) in
if let forgotPasswordResult = forgotPasswordResult {
switch(forgotPasswordResult.forgotPasswordState) {
case .done:
print("Password changed successfully")
default:
print("Error: Could not change password.")
}
} else if let error = error {
print("Error occurred: \(error.localizedDescription)")
}
}

Firebase Storage Upload Error: FIRStorageErrorDomain Code=-13000

I am trying to upload an image to Firebase and I actually succeeded several times yesterday, but today I'm getting this error:
Optional
- Some : Error Domain=FIRStorageErrorDomain Code=-13000 "An unknown error occurred, please check the server response." UserInfo={ResponseErrorDomain=FIRStorageErrorDomain, object=ProfileImages/ascascasc ascas.jpg, error_name=ERROR_USER_NOT_FOUND, bucket=ichallenge-c52ae.appspot.com, ResponseErrorCode=-13020, NSLocalizedDescription=An unknown error occurred, please check the server response.}
I want to reiterate: nothing was changed in the code between yesterday and today, it just stopped working. This is the code, I've highlighted the line where it happens with a comment:
#IBAction func signUpButtonPressed(sender: AnyObject)
{
// If textfields have more than 3 characters
if firstNameTextField.text!.characters.count > 3 && passwordTextField.text!.characters.count > 3 && emailTextField.text!.containsString("#")
{
createUser()
//Goes to Main Storyboard
parseOutdated.signUpInBackgroundWithBlock { (success: Bool, error: NSError?) in
NSNotificationCenter.defaultCenter().postNotificationName("Login", object: nil)
}
}
else
{
firstNameShake.shakeAnimation()
lastNameShake.shakeAnimation()
passwordShake.shakeAnimation()
emailShake.shakeAnimation()
}
}
func createUser()
{
//Unwrap optionals before pushing to Firebase Database
let name: String = "\(self.firstNameTextField.text!) \(self.lastNameTextField.text!)"
storeProfileImage(name)
}
func storeProfileImage(name: String)
{
let profileImageData = UIImageJPEGRepresentation(self.profileImageView.image!, 1.0)
// Create a reference to the file you want to upload
let profileImageRef = storageRef.child("ProfileImages/\(name).jpg")
// Upload the file to the path defined above
profileImageRef.putData(profileImageData!, metadata: nil) { metadata, error in
if (error != nil) //ERROR HAPPENS HERE
{
print("Image not stored: ", error?.localizedDescription)
}
else
{
//Stores the profile image URL and sends it to the next function
let downloadURL = metadata!.downloadURL()
self.storeUserData(name, profileImageURL: downloadURL!)
}
}
}
Here's a screenshot of the breakpoint in XCode:
Any help provided would be deeply appreciated.
If you have been testing with the same user witout signing out on your app, your authentication token might have expired. Try signing out firs