Verification ID used to create the phone auth credential is invalid iOS - ios

I can't login and always see this error message:
The verification ID used to create the phone auth credential is invalid
I tried to add my phone like tester, but I wasn't succeed on it.
Sign in method is turn on in Firebase console.
I found the same issue here, but without answer.
I used firebase's docs.
Help me please to figure it out.
Here my code:
func verifyPhone(_ phone: String) -> AsyncTask<String?> {
return AsyncTask<String?>({ observer, lifetime in
guard !lifetime.hasEnded else {
observer.sendInterrupted()
return
}
Auth.auth().languageCode = "ua"
PhoneAuthProvider.provider().verifyPhoneNumber(phone, uiDelegate: nil) { verificationID, error in
if let error = error {
observer.send(error: AppError(error))
return
}
observer.send(value: verificationID)
observer.sendCompleted()
}
})
}
func signInViaPhoneNumber(usingCode smsCode: String) -> AsyncTask<Void> {
return AsyncTask<Void>({ observer, lifetime in
guard !lifetime.hasEnded else {
observer.sendInterrupted()
return
}
guard let verificationCode = UserDefaultsStorage.verificationCode else {
observer.send(error: AppError.logic("Відсутній код верифікації"))
return
}
let credential = PhoneAuthProvider.provider().credential(withVerificationID: smsCode,
verificationCode: verificationCode)
Auth.auth().signIn(with: credential) { result, error in
if let error = error {
observer.send(error: AppError(error))
return
}
guard let firUser = result?.user else {
observer.send(error: AppError.logic("Відсутній юзер"))
return
}
let factory: ModelFactory = ModelFactoryImpl()
let appUser = factory.makeUser(id: firUser.uid, name: firUser.displayName ?? "", phone: firUser.phoneNumber ?? "")
AppService.shared.user = appUser
observer.send(value: ())
observer.sendCompleted()
}
})
}

It's hard for me to see the complete code flow because you are using AsyncTask and delegating all the results outside.
The issue that pops out in your code is the line
PhoneAuthProvider.provider().credential(withVerificationID: smsCode,
verificationCode: verificationCode)
In this method verificationCode should be the code you received via SMS and withVerificationID should be the verificationId that you got in the verifyPhoneNumber callback.

Related

Google Sign In & Firebase with SwiftUI

Hi all I was trying to use sign in with google and firebase in a SwiftUI project. Now checking the old implementation methods and also some suggestions got from the net I am having problem with this part of the code
private func authenticateUser(for user: GIDGoogleUser?, with error: Error?) {
// 1
if let error = error {
print(error.localizedDescription)
return
}
// 2
guard let authentication = user?.authentication, let idToken = authentication.idToken else { return }
let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: authentication.accessToken)
// 3
Auth.auth().signIn(with: credential) { [unowned self] (_, error) in
if let error = error {
print(error.localizedDescription)
} else {
self.state = .signedIn
}
}
}
I'm getting errors with this authentication constant
guard let authentication = user?.authentication, let idToken = authentication.idToken else { return }
The error is Value of type 'GIDGoogleUser' has no member 'autentication'
i know google dropped some properties replacing them...currently how can i update the google login implementation in SwiftUI?
Here is how you can authenticate a Firebase user when using Google Sign-In using the latest release (7.0.0) of the Google Sign-In SDK:
extension AuthenticationViewModel {
func signInWithGoogle() async -> Bool {
guard let clientID = FirebaseApp.app()?.options.clientID else {
fatalError("No client ID found in Firebase configuration")
}
let config = GIDConfiguration(clientID: clientID)
GIDSignIn.sharedInstance.configuration = config
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first,
let rootViewController = window.rootViewController else {
print("There is no root view controller!")
return false
}
do {
let userAuthentication = try await GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController)
let user = userAuthentication.user
guard let idToken = user.idToken else { throw AuthenticationError.tokenError(message: "ID token missing") }
let accessToken = user.accessToken
let credential = GoogleAuthProvider.credential(withIDToken: idToken.tokenString,
accessToken: accessToken.tokenString)
let result = try await Auth.auth().signIn(with: credential)
let firebaseUser = result.user
print("User \(firebaseUser.uid) signed in with email \(firebaseUser.email ?? "unknown")")
return true
}
catch {
print(error.localizedDescription)
self.errorMessage = error.localizedDescription
return false
}
}
}
This code is part of a video I am currently working on, and I will update this answer with more details, but for the time being this might help you out.

Swift Google Auth using Firebase and additional scopes

Application uses GIDSignIn and Firebase for google authentication in my iOS app.
I am trying to add additional scopes to the authentication flow, however, I do not know the proper way to add the needed scopes.
Google Sign in Documentation
func signInWithGoogle() {
guard let clientID = FirebaseApp.app()?.options.clientID else { return }
// Create Google Sign In configuration object.
let config = GIDConfiguration(clientID: clientID)
// Start the sign in flow!
GIDSignIn.sharedInstance.signIn(with: config, presenting: self) { [unowned self] user, error in
if let error = error {
// ...
return
}
guard
let authentication = user?.authentication,
let idToken = authentication.idToken
else {
return
}
let credential = GoogleAuthProvider.credential(withIDToken: idToken,
accessToken: authentication.accessToken)
// ...
Auth.auth().signIn(with: credential, completion: { (user, error) in
if let err = error {
print("Failed to create a Firebase User with Google account: ", err)
return
}
// Successfully logged in
guard let uid = user?.user.uid else { return }
print("Successfully logged into Firebase with Google", uid)
let mainTabBarController = TabBarViewController()
(UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.changeRootViewController(mainTabBarController)
})
}
}
I need to add the code below to add scopes to the authentication process. I just dont know how to properly ask for the scopes without interrupting the firebase login process.
let additionalScopes = ["https://www.googleapis.com/auth/youtube.readonly", "https://www.googleapis.com/auth/yt-analytics.readonly"]
GIDSignIn.sharedInstance.addScopes(additionalScopes, presenting: self) { user, error in
guard error == nil else { return }
guard let user = user else { return }
// Check if the user granted access to the scopes you requested.
}
A similar post with a similar problem can be found here

Firebase Google SignIn Ios updated Documentation not working

I've been trying to integrate google sign into my ios project but as they have updated their API I Can't find the solution as everyone is using GIDSignIn.sharedInstance()?.delegate and it doesn't exist anymore.
Documentation
so what I understood from the documentation is:
after setting up GoogleSignIn dependency->
added URL Schemes.
Implement the application:openURL:options: method of your app delegate.
#available(iOS 9.0, *)
func application(_ application: UIApplication, open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any])
-> Bool {
return GIDSignIn.sharedInstance.handle(url)
}
Added a view in Storyboard and assigned class GIDSignInButton.
4.Pass the presenting view controller and client ID for your app to the Google Sign In sign-in method and create a Firebase auth
credential from the resulting Google auth token: (documentation)
(not quite sure where to add this, Right now I am adding it to the ViewDidLoad method of my loginViewController , without changing anything)
guard let clientID = FirebaseApp.app()?.options.clientID else { return }
// Create Google Sign In configuration object.
let config = GIDConfiguration(clientID: clientID)
// Start the sign in flow!
GIDSignIn.sharedInstance.signIn(with: config, presenting: self) { [unowned self] user, error in
if let error = error {
// ...
return
}
guard
let authentication = user?.authentication,
let idToken = authentication.idToken
else {
return
}
let credential = GoogleAuthProvider.credential(withIDToken: idToken,
accessToken: authentication.accessToken)
// ..AuthCode.
}
5. Then finally adding authentication below credential :
Auth.auth().signIn(with: credential) { authResult, error in
if let error = error {
let authError = error as NSError
if isMFAEnabled, authError.code == AuthErrorCode.secondFactorRequired.rawValue {
// The user is a multi-factor user. Second factor challenge is required.
let resolver = authError
.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
var displayNameString = ""
for tmpFactorInfo in resolver.hints {
displayNameString += tmpFactorInfo.displayName ?? ""
displayNameString += " "
}
self.showTextInputPrompt(
withMessage: "Select factor to sign in\n\(displayNameString)",
completionBlock: { userPressedOK, displayName in
var selectedHint: PhoneMultiFactorInfo?
for tmpFactorInfo in resolver.hints {
if displayName == tmpFactorInfo.displayName {
selectedHint = tmpFactorInfo as? PhoneMultiFactorInfo
}
}
PhoneAuthProvider.provider()
.verifyPhoneNumber(with: selectedHint!, uiDelegate: nil,
multiFactorSession: resolver
.session) { verificationID, error in
if error != nil {
print(
"Multi factor start sign in failed. Error: \(error.debugDescription)"
)
} else {
self.showTextInputPrompt(
withMessage: "Verification code for \(selectedHint?.displayName ?? "")",
completionBlock: { userPressedOK, verificationCode in
let credential: PhoneAuthCredential? = PhoneAuthProvider.provider()
.credential(withVerificationID: verificationID!,
verificationCode: verificationCode!)
let assertion: MultiFactorAssertion? = PhoneMultiFactorGenerator
.assertion(with: credential!)
resolver.resolveSignIn(with: assertion!) { authResult, error in
if error != nil {
print(
"Multi factor finanlize sign in failed. Error: \(error.debugDescription)"
)
} else {
self.navigationController?.popViewController(animated: true)
}
}
}
)
}
}
}
)
} else {
self.showMessagePrompt(error.localizedDescription)
return
}
// ...
return
}
// User is signed in
// ...
}
The auth code is taken from the documentation, it's not updated and surely I won't need this many details just UId.
I don't know what the problem is but it doesn't seem to work. On running, just Google sign-in button appears and on clicking it nothing happens
It appears that there is some problem with GIDSignInButton. Made A custom button and it is working fine

How to connect GoogleSignIn authentication to user profile?

I have 2 authentication methods in my app: 1) email (via FirebaseAuth), and 2) Google (GoogleSignIn).
With FirebaseAuth email authentication, I'm able to pull an automatically generated user id with Auth.auth().currentUser!.uid. That user id is then associated with the email used to sign up/in. Similarly, is there an automatically generated user id that I can pull for users who authenticate with GoogleSignIn?
This is how I'm authenticating users with Google:
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if error == nil {
// Create the user profile and store the user info
UserService.createProfile(userId: "wantThisToBeAnAutoGeneratedId", firstName: "firstName", lastName: "lastName", email: user.profile.email) { (user) in
}
}
}
Any support and guidance is much appreciated!
You can use the credential. GoogleSignIn have been updated to version 6.0.2 so my function “didSignInFor
“ it is different than yours. For that reason I check for an optional value with guard but you can get the idea regardless.
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in
guard error == nil else { return }
// If sign in succeeded, display the app's main content View.
guard let user = user else { return }
let authentication = user.authentication
if let token = authentication.idToken {
let credential = GoogleAuthProvider.credential(withIDToken: token, accessToken: authentication.accessToken)
FirebaseRef.shared.signinGoogleUser(credential, user) {
NotificationCenter.default.post(name: .googleSignInComplete, object: self, userInfo: nil)
} onError: { (error) in
let title = NSLocalizedString("alert_sorry", comment: "")
let errorMessage = [title : error]
NotificationCenter.default.post(name: .googleSignInError, object: self, userInfo: errorMessage)
}
}
}
I’m assuming your function UserService.createProfile
Is basically to get the user data just like my FirebaseRef.shared.signinGoogleUser
Below is how I use the function to populate the user data.
/// Gets data from Google Sign In Authentication. Checks if user exists, if so continues the App's navigation flow otherwise sends data to createUser function to write new user to database
/// - Parameters:
/// - credential: credential of type AuthCredential as Google
/// - user: GIDGoogleUser from Authentication
/// - onSuccess: Continues App's flow
/// - onError: Alerts user of issues
func signinGoogleUser(_ credential: AuthCredential, _ user: GIDGoogleUser, onSuccess: #escaping() -> Void, onError: #escaping(_ errorMessage: String) -> Void) {
auth.signIn(with: credential) { [self] (authResult, error) in
if let error = error {
print("authentication error \(error.localizedDescription)")
onError(error.localizedDescription)
return
}
guard let result = authResult else { return }
let docRef = documentSpecificUser(uid: result.user.uid)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
onSuccess()
} else {
print("Document does not exist. Create user.")
let id = result.user.uid
let displayName = user.profile?.name ?? ""
let firstName = user.profile?.givenName ?? ""
let lastName = user.profile?.familyName ?? ""
let email = user.profile?.email ?? ""
let imageURL = user.profile?.imageURL(withDimension: 400)?.absoluteString
createUser(with: firstName, lastName, id, email: email, displayName, imageURL) {
onSuccess()
} onError: { (error) in
onError(error)
}
}
}
}
}

How to logout a user before sending a user a verification email in Firebase

When creating a new user, I need to log them out and then send a verification email to ensure that they own the email address. Currently my code creates the user and executes the "sendEmailVerification" call but keeps the user logged in. How can I log my user out and check that they have verified their email?
func signUp(with email: String, password: String, firstName: String, lastName: String) {
self.presentActivityView()
Auth.auth().createUser(withEmail: email, password: password) {[unowned self] (user, error) in
DispatchQueue.main.async { [unowned self] in
self.dismissActivityView()
if let err = error {
self.addAlertController(title: "Error!", message: err.localizedDescription)
} else {
let changeReq = Auth.auth().currentUser?.createProfileChangeRequest()
changeReq?.displayName = firstName + " " + lastName
if let url = self.profilePicURLString {
changeReq?.photoURL = URL(string: url)
}
changeReq?.commitChanges(completion: { (error) in
if error == nil {
//Profile updated successfully
}else {
//Profile not updated successfully
}
})
Auth.auth().currentUser?.sendEmailVerification(completion: { (error) in
if error == nil {
//Verification initiated successfully
}else {
print("Error: \(error)")
}
})
let vc = MainStoryboard.instantiateViewController(withIdentifier: "SNStoryFeedController") as! SNStoryFeedController
let nc = UINavigationController(rootViewController: vc)
UIApplication.shared.keyWindow?.rootViewController = nc
}
}
}
}
The only thing you should do is sign out from your own application:
// for FIRAuth
try? Auth.auth()?.signOut()
// for GoogleSignIn
GIDSignIn.sharedInstance().signOut()
The safari part is handled by the system and you don't need to worry about.

Resources