Firebase google signin authentication AppDelegate- Use of unresolved identifier 'isMFAEnabled' - ios

I'm new to iOS development. I am trying to add google sign in to my app but i am facing an some problems.Code shows some "Use of unresolved identifier 'isMFAEnabled"and "Value of type 'AppDelegate' has no member 'showTextInputPrompt'".Please help me.I'm following this doc- https://firebase.google.com/docs/auth/ios/google-signin#swift_9 enter image description here
import UIKit
import Firebase
import GoogleSignIn
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,GIDSignInDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return GIDSignIn.sharedInstance().handle(url)
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let error = error {
print(error.localizedDescription)
return
}
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
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 {
print(error.localizedDescription)
return
}
// ...
return
}
// User is signed in
// ...
}
}
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
}

So the boilerplate is wayyyy more code than you need. This is my review of your AppDelegate.
didFinishLaunchingWithOptions looks good as-is.
Pro tip: add this line for staying logged in GIDSignIn.sharedInstance()?.restorePreviousSignIn()
open,options looks good as-is.
didSignInFor user is where it gets tangled. Delete the entire function and replace it with the following extension (outside the class' brackets):
(also delete GIDSignInDelegate in the class protocols)
extension AppDelegate: GIDSignInDelegate {
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
//handle sign-in errors
if let error = error {
if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
print("The user has not signed in before or they have since signed out.")
} else {
print("error signing into Google \(error.localizedDescription)")
}
return
}
// Get credential object using Google ID token and Google access token
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
// Authenticate with Firebase using the credential object
Auth.auth().signIn(with: credential) { (authResult, error) in
if let error = error {
print("authentication error \(error.localizedDescription)")
}
}
}
}
I have tested this in today and it's currently working (Swift 5/ios 13.6).

TL;DR; you can just delete that variable if you don't want to enable multi-factor authentication.
The Firebase documentation provides the variable as a way for you to enable/disable multi-factor (MF) authentication (i.e. when facebook sends you a text message for you to verify). It's more like they are giving you an incomplete template that doesn't compile unless you declare and set this variable (among other things like implementing showTextInputPrompt).
The code provided in firebase documentation is an example, so don't expect it to work out of the box.

Related

How to convert Google iOS Sign-In single page sample AppDelegate.h protocol -> to a segue to LoginPage ViewController AppDelegate.swift protocol?

Google's Sign-In sample on GitHub SignInSampleForPod.xcworkspace creates a single page sign-in using the AppDelegate.h SignInViewController.m etc protocol.
However many apps, such as mine, prefer to segue to a Login Page only when a user makes a choice requiring verification. I just want the basic Google Profile info and authentication token.
I have the Google iOS ClientID configured enough so a segue to my LoginPage.swift shows the Google Sign-In button via AppDelegate.swift:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(
_ app: UIApplication,
open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
var handled: Bool
handled = GIDSignIn.sharedInstance.handle(url)
if handled {
return true
}
// Handle other custom URL types.
// If not handled by this app, return false.
return false
}
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
if error != nil || user == nil {
// Show the app's signed-out state.
} else {
// Show the app's signed-in state.
}
}
return true
}
And LoginPage.swift:
class LoginViewController: UIViewController {
let signInConfig = GIDConfiguration.init(clientID: "foo-bar85.apps.googleusercontent.com")
override func viewDidLoad() {
super.viewDidLoad()
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in
guard error == nil else { return }
guard let user = user else { return }
let emailAddress = user.profile?.email
let fullName = user.profile?.name
let givenName = user.profile?.givenName
let familyName = user.profile?.familyName
let profilePicUrl = user.profile?.imageURL(withDimension: 320)
}
So my question is what is the AppDelegate.swift Google Sign-In code for the fields shown below to display the basic profile info:
// Show the app's signed-out state.
} else {
// Show the app's signed-in state.
I may not able to understand your problem clearly.
But I am trying to answer based on my understanding.
You can create a class (GoogleLoginManager) for all google login related stuff and create a button in UI then call this method (signIn) from button action.
#IBAction func googleButtonAction(_ sender: Any) {
GoogleLoginManager.shared.signIn(controller: self) { (profile) in
print("GoogleLogin profile : \(String(describing: profile.name)), \(String(describing: profile.email))")
} onFailure: { (error) in
print("GoogleLogin error : \(String(describing: error.localizedDescription))")
}
}
import Foundation
import GoogleSignIn
class GoogleLoginManager: SocialLogin {
fileprivate var onSuccess : success?
fileprivate var onFailure : failure?
static let shared = GoogleLoginManager()
private override init() { }
func signIn(controller: UIViewController, onSuccess : #escaping success, onFailure : #escaping failure) {
self.onSuccess = onSuccess
self.onFailure = onFailure
GIDSignIn.sharedInstance().clientID = GOOGLE_CLIENT_ID
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().presentingViewController = controller
GIDSignIn.sharedInstance().signIn()
// Automatically sign in the user.
// GIDSignIn.sharedInstance()?.restorePreviousSignIn()
}
func signOut() {
GIDSignIn.sharedInstance().signOut()
}
}
extension GoogleLoginManager : GIDSignInDelegate {
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!,
withError error: Error!) {
if let error = error {
if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
print("The user has not signed in before or they have since signed out.")
}
else if (error as NSError).code == GIDSignInErrorCode.canceled.rawValue {
print("user canceled the sign in request")
}
else {
print("\(error.localizedDescription)")
}
self.onFailure?(error)
return
}
var profile = SocialProfileModel.init(user: user)
profile.loginSuccess = true
self.onSuccess?(profile)
}
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!,
withError error: Error!) {
// Perform any operations when the user disconnects from app here.
print("GIDSignIn : didDisconnectWith")
}
}
I just had to modify my above AppDelegate.swift slightly - adding a standard UIbutton linked to the following action - gets the profile info:
#IBAction func LogInButtonTouched(_ sender: UIButton) {
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in
guard error == nil else { return }
guard let user = user else { return }
let emailAddress = user.profile?.email
let fullName = user.profile?.name
let givenName = user.profile?.givenName
let familyName = user.profile?.familyName
let profilePicUrl = user.profile?.imageURL(withDimension: 320)
print("GoogleLogin profile : \(String(describing: user.profile?.name)), \(String(describing: user.profile?.email))")
}
}

iOS GoogleSignIn is returning an expired id token

I am trying to retrieve from Google an id token to send to my backend server.
The problem is that the id token that I am receiving is already expired, so it is not valid to my server.
// MARK: - Google auth
extension LoginView: GIDSignInDelegate {
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
// Check for sign in error
if let error = error {
if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
print("The user has not signed in before or they have since signed out.")
} else {
print("\(error.localizedDescription)")
}
return
}
guard let u = GIDSignIn.sharedInstance()?.currentUser, let email = u.profile.email, let eDate = u.authentication.idTokenExpirationDate, let idToken = u.authentication.idToken else { return }
print(">> ", eDate)
self.register(email: email, token: idToken)
}
}
The date: >> 2020-10-01 08:09:19 +0000(Currently is 09:10 hours)
Thanks

How does Google OAuth2.0 callback works in iOS app?

I wanna know how an iOS app grabs authorization grant after being called back from Safari after authorization with Google when using GoogleSignIn package.
I followed instructions on google's developer site that tells me to add following code to AppDelegate, which I believe is responsible for handling that.
func application(application: UIApplication,
openURL url: NSURL, options: [String: AnyObject]) -> Bool {
return GIDSignIn.sharedInstance().handleURL(url,
sourceApplication: options[UIApplicationOpenURLOptionsSourceApplicationKey] as? String,
annotation: options[UIApplicationOpenURLOptionsAnnotationKey])
}
But when I placed a breakpoint to this method, so I could see the url, it was never called. I even tried to delete this method and it still works!
Can somebody explain to me what kind of magic is that?
I have implemented Google Login in one of my application in following way...
Required pod
pod 'GoogleSignIn'
User Tap On Google Login Button
I've created custom button.
#IBAction func btnGoogleSignInTapped(_ sender: UIButton) {
GIDSignIn.sharedInstance().clientID = kGoogleClientID
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().signIn()
}
GIDSignInDelegate Implementation
extension MyVC: GIDSignInDelegate {
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if (error == nil) {
// Perform any operations on signed in user here.
let userId = user.userID // For client-side use only!
let idToken = user.authentication.idToken // Safe to send to the server
let fullName = user.profile.name
let givenName = user.profile.givenName
let familyName = user.profile.familyName
let email = user.profile.email
debugPrint("user id = \(String(describing: userId))")
debugPrint("idToken = \(String(describing: idToken))")
debugPrint("fullName = \(String(describing: fullName))")
debugPrint("givenName = \(String(describing: givenName))")
debugPrint("familyName = \(String(describing: familyName))")
debugPrint("email = \(String(describing: email))")
//sign in successfull......
} else {
debugPrint("Google sign in error :\(error.localizedDescription)")
}
}
//-----------------------------------------------------
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
debugPrint("User did disconnecte with error !!")
}
//-----------------------------------------------------
}
you need to set URL Type under info.plist file of your project...

Firebase google auth (swift)

Following the firebase docs i added the authentication with google account and this is part of the code that i have in my app delegate
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
if let error = error {
print("Failed to log into Google: ", error)
return
}
print("Successfully logged into Google", user)
guard let idToken = user.authentication.idToken else { return }
guard let accessToken = user.authentication.accessToken else { return }
let credentials = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)
Auth.auth().signIn(with: credentials, completion: { (user, error) in
if let err = error {
print("Failed to create a Firebase User with Google account: ", err)
return
}
guard let uid = user?.uid else { return }
print("Successfully logged into Firebase with Google", uid)
})
}
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
// Perform any operations when the user disconnects from app here.
// ...
}
but the google button
fileprivate func setupGoogleButtons() {
googleButton.frame = CGRect(x: 189, y: 532, width: 118, height: 41)
view.addSubview(googleButton)
GIDSignIn.sharedInstance().uiDelegate = self
}
is obviously in a viewController, what i would like to do is an automatically self.performSegue(withIdentifier: "goToHome1", sender: self) as soon as the user logs in with his google account, because at the moment after login the user always finds on the same VC. How can i do this?
UPDATE
I solve my problem following this question Google and Facebook Firebase Auth using swift 3
If there is no error then the user signed in successfully so you should segue. The button itself just calls the sign in function. Depending on whether the sign in fails or succeeds you alert or segue.
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
if let error = error {
print("Failed to log into Google: ", error)
return
}
print("Successfully logged into Google", user)
guard let idToken = user.authentication.idToken else { return }
guard let accessToken = user.authentication.accessToken else { return }
let credentials = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)
Auth.auth().signIn(with: credentials, completion: { (user, error) in
if let err = error {
print("Failed to create a Firebase User with Google account: ", err)
return
}
guard let uid = user?.uid else { return }
print("Successfully logged into Firebase with Google", uid)
// segue here
DispatchQueue.main.async {
self.performSegue(withIdentifier: "goToHome1", sender: self)
}
})
}
The Auth class has a function addAuthStateDidChangeListener:, check it here.
It will trigger any time the user changes, particularly when the user logs in, the callback will return a non-nil user. That's the time you want to perform the segue.

Google Sign In Crashes If Declined

I'm using a google sign-in and when the user clicks the button but, does not sign in with google (Clicking the button and then cancelling), the app crashes. I'm not sure why.
Here's the code behind my button:
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let authentication = user.authentication {
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential, completion: { (user, error) -> Void in
if error != nil {
print("Problem at signing in with google with error : \(String(describing: error))")
} else if error == nil {
print("user successfully signed in through GOOGLE! uid:\(Auth.auth().currentUser!.uid)")
print("signed in")
self.firebaseAuth(credential)
}
})
}
}
The error I get is:
"Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value"
at this line:
if let authentication = user.authentication {
As a good sign of developer always check for values which you want to use.
like if you want to user user object then check if user != nil don't go with if error == nil.
Here is my code for google sign in with firebase. Hope it'll help you.
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
// ...
if error != nil {
// ...
return
}
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential) { (user, error) in
if user == nil {
// ...
return
} else {
print(user!.displayName ?? "")
print(user!.email ?? "")
print(user!.phoneNumber ?? "")
// User is signed in
// ...
}
}
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!,
withError error: Error?) {
if user == nil {
print("Error, user pressed cancel button")
} else {
// Start your code here.......
}
}

Resources