Kaynağa Gözat

temp refactored code

Pragati 2 yıl önce
ebeveyn
işleme
9f8d3b2124

+ 36 - 39
FirebaseAuth/Sources/Swift/MultiFactor/MultiFactor.swift

@@ -118,51 +118,48 @@ import Foundation
         }
       } else if assertion.factorID != PhoneMultiFactorInfo.PhoneMultiFactorID {
         return
-      }
-      let phoneAssertion = assertion as? PhoneMultiFactorAssertion
-      guard let credential = phoneAssertion?.authCredential else {
-        fatalError("Internal Error: Missing credential")
-      }
-      switch credential.credentialKind {
-      case .phoneNumber: fatalError("Internal Error: Missing verificationCode")
-      case let .verification(verificationID, code):
-        let finalizeMFAPhoneRequestInfo =
-          AuthProtoFinalizeMFAPhoneRequestInfo(sessionInfo: verificationID, verificationCode: code)
-        guard let user = user, let auth = user.auth else {
-          fatalError("Internal Auth error: failed to get user enrolling in MultiFactor")
+      } else {
+        let phoneAssertion = assertion as? PhoneMultiFactorAssertion
+        guard let credential = phoneAssertion?.authCredential else {
+          fatalError("Internal Error: Missing credential")
         }
-        let request = FinalizeMFAEnrollmentRequest(
-          idToken: self.user?.rawAccessToken(),
-          displayName: displayName,
-          phoneVerificationInfo: finalizeMFAPhoneRequestInfo,
-          requestConfiguration: user.requestConfiguration
-        )
-
-        Task {
-          do {
-            let response = try await AuthBackend.call(with: request)
+        switch credential.credentialKind {
+        case .phoneNumber: fatalError("Internal Error: Missing verificationCode")
+        case let .verification(verificationID, code):
+          let finalizeMFAPhoneRequestInfo =
+          AuthProtoFinalizeMFAPhoneRequestInfo(sessionInfo: verificationID, verificationCode: code)
+          guard let user = user, let auth = user.auth else {
+            fatalError("Internal Auth error: failed to get user enrolling in MultiFactor")
+          }
+          let request = FinalizeMFAEnrollmentRequest(
+            idToken: self.user?.rawAccessToken(),
+            displayName: displayName,
+            phoneVerificationInfo: finalizeMFAPhoneRequestInfo,
+            requestConfiguration: user.requestConfiguration
+          )
+          
+          Task {
             do {
-              let user = try await auth.completeSignIn(withAccessToken: response.idToken,
-                                                       accessTokenExpirationDate: nil,
-                                                       refreshToken: response.refreshToken,
-                                                       anonymous: false)
-              try auth.updateCurrentUser(user, byForce: false, savingToDisk: true)
-              if let completion {
-                DispatchQueue.main.async {
-                  completion(nil)
-                }
-              }
-            } catch {
-              DispatchQueue.main.async {
+              let response = try await AuthBackend.call(with: request)
+              do {
+                let user = try await auth.completeSignIn(withAccessToken: response.idToken,
+                                                         accessTokenExpirationDate: nil,
+                                                         refreshToken: response.refreshToken,
+                                                         anonymous: false)
+                try auth.updateCurrentUser(user, byForce: false, savingToDisk: true)
                 if let completion {
-                  completion(error)
+                  DispatchQueue.main.async {
+                    completion(nil)
+                  }
+                }
+              } catch {
+                DispatchQueue.main.async {
+                  if let completion {
+                    completion(error)
+                  }
                 }
               }
             }
-          } catch {
-            if let completion {
-              completion(error)
-            }
           }
         }
       }

+ 3 - 0
FirebaseAuth/Sources/Swift/MultiFactor/TOTP/TOTPMultiFactorInfo.swift

@@ -42,6 +42,9 @@ import Foundation
     required init?(coder: NSCoder) {
       fatalError("init(coder:) has not been implemented")
     }
+    
+      // MARK: NSSecureCoding
+    override public class var supportsSecureCoding: Bool { return true }
   }
 
 #endif

+ 88 - 0
FirebaseAuth/Tests/Sample/Sample/Application.plist

@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundleIdentifier</key>
+	<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>CFBundleVersion</key>
+	<string>1.0</string>
+	<key>UIBackgroundModes</key>
+	<array>
+		<string>remote-notification</string>
+	</array>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>CFBundleDisplayName</key>
+	<string>FirebaseAuth Sample</string>
+	<key>LSApplicationQueriesSchemes</key>
+	<array>
+		<string>fbauth2</string>
+	</array>
+	<key>CFBundleURLTypes</key>
+	<array>
+		<dict>
+			<key>CFBundleURLName</key>
+			<string>com.googleusercontent.apps.585304629422-ab795evno6vsrj7i7o1a3gi6eo01508b</string>
+			<key>CFBundleURLSchemes</key>
+			<array>
+				<string>com.googleusercontent.apps.585304629422-ab795evno6vsrj7i7o1a3gi6eo01508b</string>
+			</array>
+			<key>CFBundleTypeRole</key>
+			<string>Editor</string>
+		</dict>
+		<dict>
+			<key>CFBundleURLName</key>
+			<string>com.googleusercontent.apps.585304629422-e7gpk5dr3ep29neig59oaksog6s5vpeu</string>
+			<key>CFBundleURLSchemes</key>
+			<array>
+				<string>com.googleusercontent.apps.585304629422-e7gpk5dr3ep29neig59oaksog6s5vpeu</string>
+			</array>
+			<key>CFBundleTypeRole</key>
+			<string>Editor</string>
+		</dict>
+		<dict>
+			<key>CFBundleURLName</key>
+			<string>com.google.FirebaseExperimental1.dev</string>
+			<key>CFBundleURLSchemes</key>
+			<array>
+				<string>com.google.FirebaseExperimental1.dev</string>
+			</array>
+			<key>CFBundleTypeRole</key>
+			<string>Editor</string>
+		</dict>
+	</array>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+</dict>
+</plist>

+ 34 - 0
FirebaseAuth/Tests/Sample/Sample/AuthCredentials.h

@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 Google
+ *
+ * Licensed under the Apache License,
+ * Ve/Users/pragatimodi/Desktop/firebase-ios-sdk/FirebaseAuth/Tests/Sample/Sample/AuthCredentialsTemplate.hrsion
+ * 2.0 (the "License"); you may not use this file except in compliance with the License. You may
+ * obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ Some of the Auth Credentials needs to be populated for the Sample build to work.
+
+ Please follow the following steps to populate the valid AuthCredentials
+ and copy it to AuthCredentials.h file
+
+ You will need to replace the following values:
+
+ $KFACEBOOK_APP_ID
+ FACEBOOK_APP_ID is the developer's Facebook app's ID, to be used to test the
+ 'Signing in with Facebook' feature of Firebase Auth. Follow the instructions
+ on the Facebook developer site: https://developers.facebook.com/docs/apps/register
+ to obtain such an id.
+ */
+
+#define KFACEBOOK_APP_ID @"189710277009416"
+#define KCONTINUE_URL @"adminsdkintegrationtest.page.linkqbvQ"

+ 34 - 0
FirebaseAuth/Tests/Sample/Sample/GoogleService-Info.plist

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CLIENT_ID</key>
+	<string>585304629422-ab795evno6vsrj7i7o1a3gi6eo01508b.apps.googleusercontent.com</string>
+	<key>REVERSED_CLIENT_ID</key>
+	<string>com.googleusercontent.apps.585304629422-ab795evno6vsrj7i7o1a3gi6eo01508b</string>
+	<key>API_KEY</key>
+	<string>AIzaSyB_IEBBC1GNzpBUJPTfO40OD3lXtPtC3r8</string>
+	<key>GCM_SENDER_ID</key>
+	<string>585304629422</string>
+	<key>PLIST_VERSION</key>
+	<string>1</string>
+	<key>BUNDLE_ID</key>
+	<string>com.google.FirebaseExperimental1.dev</string>
+	<key>PROJECT_ID</key>
+	<string>test-project-pragatimodi-9ce7c</string>
+	<key>STORAGE_BUCKET</key>
+	<string>test-project-pragatimodi-9ce7c.appspot.com</string>
+	<key>IS_ADS_ENABLED</key>
+	<false></false>
+	<key>IS_ANALYTICS_ENABLED</key>
+	<false></false>
+	<key>IS_APPINVITE_ENABLED</key>
+	<true></true>
+	<key>IS_GCM_ENABLED</key>
+	<true></true>
+	<key>IS_SIGNIN_ENABLED</key>
+	<true></true>
+	<key>GOOGLE_APP_ID</key>
+	<string>1:585304629422:ios:cb0e5761816443a9d98d3f</string>
+</dict>
+</plist>

+ 14 - 0
FirebaseAuth/Tests/Sample/Sample/Sample.entitlements

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>application-identifier</key>
+	<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
+	<key>aps-environment</key>
+	<string>development</string>
+	<key>com.apple.developer.associated-domains</key>
+	<array>
+		<string>applinks:&quot;https://adminsdkintegrationtest.page.link/eP3A&quot;</string>
+	</array>
+</dict>
+</plist>

+ 205 - 13
FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj

@@ -3,11 +3,18 @@
 	archiveVersion = 1;
 	classes = {
 	};
-	objectVersion = 51;
+	objectVersion = 54;
 	objects = {
 
 /* Begin PBXBuildFile section */
-		8848765129D314A400780FA6 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8848764F29D3149200780FA6 /* GoogleService-Info.plist */; };
+		24C7B2062B8FA0E60055CD63 /* GoogleService-Info_multi.plist in Resources */ = {isa = PBXBuildFile; fileRef = 24C7B2042B8FA0E60055CD63 /* GoogleService-Info_multi.plist */; };
+		24C7B2072B8FA0E60055CD63 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 24C7B2052B8FA0E60055CD63 /* GoogleService-Info.plist */; };
+		24C7B2152B9BC5860055CD63 /* CustomQRCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24C7B2142B9BC5860055CD63 /* CustomQRCodeView.swift */; };
+		24C7B2192BA7CDF80055CD63 /* AuthViewController+MultiFactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24C7B2182BA7CDF80055CD63 /* AuthViewController+MultiFactor.swift */; };
+		24C7B21B2BA7CE410055CD63 /* MultiFactorMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24C7B21A2BA7CE410055CD63 /* MultiFactorMenu.swift */; };
+		877659926192BE3D4B331E47 /* Pods_SwiftApiTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7AA6A5785C4380CD2D5B6C6 /* Pods_SwiftApiTests.framework */; };
+		B600069ED2DF526E6EC4A6F8 /* Pods_AuthenticationExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5B0F84A37EE77546B4F5811 /* Pods_AuthenticationExample.framework */; };
+		D2864C43DECE0AA472898710 /* Pods_AuthenticationExample_AuthenticationExampleUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95A02B3D0D4406B6AA0160CF /* Pods_AuthenticationExample_AuthenticationExampleUITests.framework */; };
 		DE8FD46F2A7D660B00B6831A /* Credentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8FD4682A7D660A00B6831A /* Credentials.swift */; };
 		DE8FD4702A7D660B00B6831A /* AccountInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8FD4692A7D660A00B6831A /* AccountInfoTests.swift */; };
 		DE8FD4712A7D660B00B6831A /* FacebookTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8FD46A2A7D660A00B6831A /* FacebookTests.swift */; };
@@ -25,7 +32,6 @@
 		DE8FD4942A7D9E2700B6831A /* PhoneMultiFactorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8FD4912A7D9D9E00B6831A /* PhoneMultiFactorTests.swift */; };
 		DEC2E5DD2A95331E0090260A /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC2E5DC2A95331D0090260A /* SettingsViewController.swift */; };
 		DEC2E5DF2A9583CA0090260A /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC2E5DE2A9583CA0090260A /* AppManager.swift */; };
-		DEC2E5E42A966DE20090260A /* GoogleService-Info_multi.plist in Resources */ = {isa = PBXBuildFile; fileRef = DEC2E5E32A966DE20090260A /* GoogleService-Info_multi.plist */; };
 		DED37F632AB0C4F7003A67E4 /* SettingsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED37F622AB0C4F7003A67E4 /* SettingsUITests.swift */; };
 		EA02F68524A000E00079D000 /* UserActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA02F68424A000E00079D000 /* UserActions.swift */; };
 		EA02F68D24A063E90079D000 /* LoginDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA02F68C24A063E90079D000 /* LoginDelegate.swift */; };
@@ -53,6 +59,7 @@
 		EAEBCE0F2489FFDE00FCEA92 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAEBCE0E2489FFDE00FCEA92 /* Extensions.swift */; };
 		EAEBCE11248A9AA000FCEA92 /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAEBCE10248A9AA000FCEA92 /* Section.swift */; };
 		EAFDF2BE2490439F0082B6F1 /* Animator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAFDF2BD2490439F0082B6F1 /* Animator.swift */; };
+		F1D162C4DD854E9814EB31A3 /* Pods_ObjCApiTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ECA2A75FD0AC5BB876C75D5 /* Pods_ObjCApiTests.framework */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -80,7 +87,21 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
-		8848764F29D3149200780FA6 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info.plist"; sourceTree = SOURCE_ROOT; };
+		0E8714DC0F0E59EE0A8E7B9B /* Pods-ObjCApiTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ObjCApiTests.debug.xcconfig"; path = "Target Support Files/Pods-ObjCApiTests/Pods-ObjCApiTests.debug.xcconfig"; sourceTree = "<group>"; };
+		0ECA2A75FD0AC5BB876C75D5 /* Pods_ObjCApiTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ObjCApiTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		174F2A059457D01B253ABFDD /* Pods-AuthenticationExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AuthenticationExample.release.xcconfig"; path = "Target Support Files/Pods-AuthenticationExample/Pods-AuthenticationExample.release.xcconfig"; sourceTree = "<group>"; };
+		24C7B2042B8FA0E60055CD63 /* GoogleService-Info_multi.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info_multi.plist"; sourceTree = "<group>"; };
+		24C7B2052B8FA0E60055CD63 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
+		24C7B2142B9BC5860055CD63 /* CustomQRCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomQRCodeView.swift; sourceTree = "<group>"; };
+		24C7B2182BA7CDF80055CD63 /* AuthViewController+MultiFactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AuthViewController+MultiFactor.swift"; sourceTree = "<group>"; };
+		24C7B21A2BA7CE410055CD63 /* MultiFactorMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiFactorMenu.swift; sourceTree = "<group>"; };
+		274B4AD0EDFB249DC7942BCF /* Pods-SwiftApiTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftApiTests.debug.xcconfig"; path = "Target Support Files/Pods-SwiftApiTests/Pods-SwiftApiTests.debug.xcconfig"; sourceTree = "<group>"; };
+		5FCA462A653F4A643BEEC832 /* Pods-SwiftApiTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftApiTests.release.xcconfig"; path = "Target Support Files/Pods-SwiftApiTests/Pods-SwiftApiTests.release.xcconfig"; sourceTree = "<group>"; };
+		7E3FF7CCFE89B70FC5B51C99 /* Pods-AuthenticationExample-AuthenticationExampleUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AuthenticationExample-AuthenticationExampleUITests.release.xcconfig"; path = "Target Support Files/Pods-AuthenticationExample-AuthenticationExampleUITests/Pods-AuthenticationExample-AuthenticationExampleUITests.release.xcconfig"; sourceTree = "<group>"; };
+		95A02B3D0D4406B6AA0160CF /* Pods_AuthenticationExample_AuthenticationExampleUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AuthenticationExample_AuthenticationExampleUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		A3FEEF874D705F97CB76500B /* Pods-AuthenticationExample-AuthenticationExampleUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AuthenticationExample-AuthenticationExampleUITests.debug.xcconfig"; path = "Target Support Files/Pods-AuthenticationExample-AuthenticationExampleUITests/Pods-AuthenticationExample-AuthenticationExampleUITests.debug.xcconfig"; sourceTree = "<group>"; };
+		A7AA6A5785C4380CD2D5B6C6 /* Pods_SwiftApiTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftApiTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		DE11518ACF28A8E3DBF0092C /* Pods-ObjCApiTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ObjCApiTests.release.xcconfig"; path = "Target Support Files/Pods-ObjCApiTests/Pods-ObjCApiTests.release.xcconfig"; sourceTree = "<group>"; };
 		DE4D8E1F2A8B0311001952B6 /* SwiftApplication.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = SwiftApplication.plist; sourceTree = "<group>"; };
 		DE8FD4682A7D660A00B6831A /* Credentials.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = "<group>"; };
 		DE8FD4692A7D660A00B6831A /* AccountInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountInfoTests.swift; sourceTree = "<group>"; };
@@ -102,7 +123,6 @@
 		DE8FD4972A7DB00600B6831A /* AuthCredentials.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuthCredentials.h; sourceTree = "<group>"; };
 		DEC2E5DC2A95331D0090260A /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
 		DEC2E5DE2A9583CA0090260A /* AppManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = "<group>"; };
-		DEC2E5E32A966DE20090260A /* GoogleService-Info_multi.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info_multi.plist"; sourceTree = SOURCE_ROOT; };
 		DED37F622AB0C4F7003A67E4 /* SettingsUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsUITests.swift; sourceTree = "<group>"; };
 		EA02F68424A000E00079D000 /* UserActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActions.swift; sourceTree = "<group>"; };
 		EA02F68C24A063E90079D000 /* LoginDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginDelegate.swift; sourceTree = "<group>"; };
@@ -135,6 +155,8 @@
 		EAEBCE0E2489FFDE00FCEA92 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
 		EAEBCE10248A9AA000FCEA92 /* Section.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = "<group>"; };
 		EAFDF2BD2490439F0082B6F1 /* Animator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Animator.swift; sourceTree = "<group>"; };
+		F5B0F84A37EE77546B4F5811 /* Pods_AuthenticationExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AuthenticationExample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		FD88E272757946CF8EDFD573 /* Pods-AuthenticationExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AuthenticationExample.debug.xcconfig"; path = "Target Support Files/Pods-AuthenticationExample/Pods-AuthenticationExample.debug.xcconfig"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -142,6 +164,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				F1D162C4DD854E9814EB31A3 /* Pods_ObjCApiTests.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -149,6 +172,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				B600069ED2DF526E6EC4A6F8 /* Pods_AuthenticationExample.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -156,6 +180,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				877659926192BE3D4B331E47 /* Pods_SwiftApiTests.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -163,6 +188,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				D2864C43DECE0AA472898710 /* Pods_AuthenticationExample_AuthenticationExampleUITests.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -172,10 +198,29 @@
 		2EBFAC992DA393BBD4494A15 /* Pods */ = {
 			isa = PBXGroup;
 			children = (
+				FD88E272757946CF8EDFD573 /* Pods-AuthenticationExample.debug.xcconfig */,
+				174F2A059457D01B253ABFDD /* Pods-AuthenticationExample.release.xcconfig */,
+				A3FEEF874D705F97CB76500B /* Pods-AuthenticationExample-AuthenticationExampleUITests.debug.xcconfig */,
+				7E3FF7CCFE89B70FC5B51C99 /* Pods-AuthenticationExample-AuthenticationExampleUITests.release.xcconfig */,
+				0E8714DC0F0E59EE0A8E7B9B /* Pods-ObjCApiTests.debug.xcconfig */,
+				DE11518ACF28A8E3DBF0092C /* Pods-ObjCApiTests.release.xcconfig */,
+				274B4AD0EDFB249DC7942BCF /* Pods-SwiftApiTests.debug.xcconfig */,
+				5FCA462A653F4A643BEEC832 /* Pods-SwiftApiTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
 		};
+		A794FF4020255C0605FBD1AD /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				F5B0F84A37EE77546B4F5811 /* Pods_AuthenticationExample.framework */,
+				95A02B3D0D4406B6AA0160CF /* Pods_AuthenticationExample_AuthenticationExampleUITests.framework */,
+				0ECA2A75FD0AC5BB876C75D5 /* Pods_ObjCApiTests.framework */,
+				A7AA6A5785C4380CD2D5B6C6 /* Pods_SwiftApiTests.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
 		DE8FD47B2A7D96B900B6831A /* ObjCApiTests */ = {
 			isa = PBXGroup;
 			children = (
@@ -212,6 +257,7 @@
 				EAB3A17B2494628200385291 /* UserViewController.swift */,
 				EA02F68E24A0714B0079D000 /* OtherAuthMethodControllers */,
 				DEC2E5DC2A95331D0090260A /* SettingsViewController.swift */,
+				24C7B2182BA7CDF80055CD63 /* AuthViewController+MultiFactor.swift */,
 			);
 			path = ViewControllers;
 			sourceTree = "<group>";
@@ -220,6 +266,7 @@
 			isa = PBXGroup;
 			children = (
 				EA20B50B249E8F0700B5E581 /* AuthMenu.swift */,
+				24C7B21A2BA7CE410055CD63 /* MultiFactorMenu.swift */,
 				EA20B50F249FDB4400B5E581 /* OtherAuthMethods.swift */,
 				EA02F68424A000E00079D000 /* UserActions.swift */,
 				EAEBCE10248A9AA000FCEA92 /* Section.swift */,
@@ -232,6 +279,7 @@
 			children = (
 				EA20B46B2495A9F900B5E581 /* SignedOutView.swift */,
 				EA527CAB24A0EE9600ADB9A2 /* LoginView.swift */,
+				24C7B2142B9BC5860055CD63 /* CustomQRCodeView.swift */,
 			);
 			path = CustomViews;
 			sourceTree = "<group>";
@@ -274,6 +322,7 @@
 				DE8FD47B2A7D96B900B6831A /* ObjCApiTests */,
 				EAE4CBC224855E3A00245E92 /* Products */,
 				2EBFAC992DA393BBD4494A15 /* Pods */,
+				A794FF4020255C0605FBD1AD /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
@@ -291,9 +340,10 @@
 		EAE4CBC324855E3A00245E92 /* AuthenticationExample */ = {
 			isa = PBXGroup;
 			children = (
-				DEC2E5E32A966DE20090260A /* GoogleService-Info_multi.plist */,
 				DE4D8E1F2A8B0311001952B6 /* SwiftApplication.plist */,
 				EA062D5C24A0FEB6006714D3 /* README.md */,
+				24C7B2042B8FA0E60055CD63 /* GoogleService-Info_multi.plist */,
+				24C7B2052B8FA0E60055CD63 /* GoogleService-Info.plist */,
 				EA20B506249CA63300B5E581 /* AuthenticationExample.entitlements */,
 				EAE4CBC424855E3A00245E92 /* AppDelegate.swift */,
 				EAE4CBC624855E3A00245E92 /* SceneDelegate.swift */,
@@ -302,7 +352,6 @@
 				EA20B47724973BB100B5E581 /* CustomViews */,
 				EAB3A17A2494626F00385291 /* Utility */,
 				EAE4CBCD24855E3D00245E92 /* Assets.xcassets */,
-				8848764F29D3149200780FA6 /* GoogleService-Info.plist */,
 				EA217894248979E200736757 /* LaunchScreen.storyboard */,
 				DEC2E5DE2A9583CA0090260A /* AppManager.swift */,
 			);
@@ -341,6 +390,7 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = DE8FD4802A7D96BB00B6831A /* Build configuration list for PBXNativeTarget "ObjCApiTests" */;
 			buildPhases = (
+				E3CD5F9D10D2B48314C4CCF0 /* [CP] Check Pods Manifest.lock */,
 				DE8FD4762A7D96B900B6831A /* Sources */,
 				DE8FD4772A7D96B900B6831A /* Frameworks */,
 				DE8FD4782A7D96B900B6831A /* Resources */,
@@ -359,9 +409,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = EAE4CBEB24855E3E00245E92 /* Build configuration list for PBXNativeTarget "AuthenticationExample" */;
 			buildPhases = (
+				C460DD7E6CE790777EBAC309 /* [CP] Check Pods Manifest.lock */,
 				EAE4CBBD24855E3A00245E92 /* Sources */,
 				EAE4CBBE24855E3A00245E92 /* Frameworks */,
 				EAE4CBBF24855E3A00245E92 /* Resources */,
+				480713011020E8031CAD2B7A /* [CP] Embed Pods Frameworks */,
 			);
 			buildRules = (
 			);
@@ -376,6 +428,7 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = EAE4CBEE24855E3E00245E92 /* Build configuration list for PBXNativeTarget "SwiftApiTests" */;
 			buildPhases = (
+				B789BC89EF112D031ADBC466 /* [CP] Check Pods Manifest.lock */,
 				EAE4CBD324855E3E00245E92 /* Sources */,
 				EAE4CBD424855E3E00245E92 /* Frameworks */,
 				EAE4CBD524855E3E00245E92 /* Resources */,
@@ -394,9 +447,11 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = EAE4CBF124855E3E00245E92 /* Build configuration list for PBXNativeTarget "AuthenticationExampleUITests" */;
 			buildPhases = (
+				577207719C3D7E5BCBFCA86F /* [CP] Check Pods Manifest.lock */,
 				EAE4CBDE24855E3E00245E92 /* Sources */,
 				EAE4CBDF24855E3E00245E92 /* Frameworks */,
 				EAE4CBE024855E3E00245E92 /* Resources */,
+				11EDAF85CB22C72703F88976 /* [CP] Embed Pods Frameworks */,
 			);
 			buildRules = (
 			);
@@ -414,6 +469,9 @@
 		EAE4CBB924855E3A00245E92 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
+				KnownAssetTags = (
+					"com.apple.gamecenter.Dashboard Image",
+				);
 				LastSwiftUpdateCheck = 1130;
 				LastUpgradeCheck = 1130;
 				ORGANIZATIONNAME = Firebase;
@@ -470,8 +528,8 @@
 			buildActionMask = 2147483647;
 			files = (
 				EA217895248979E200736757 /* LaunchScreen.storyboard in Resources */,
-				8848765129D314A400780FA6 /* GoogleService-Info.plist in Resources */,
-				DEC2E5E42A966DE20090260A /* GoogleService-Info_multi.plist in Resources */,
+				24C7B2062B8FA0E60055CD63 /* GoogleService-Info_multi.plist in Resources */,
+				24C7B2072B8FA0E60055CD63 /* GoogleService-Info.plist in Resources */,
 				EAE4CBCE24855E3D00245E92 /* Assets.xcassets in Resources */,
 				EA062D5D24A0FEB6006714D3 /* README.md in Resources */,
 			);
@@ -493,6 +551,131 @@
 		};
 /* End PBXResourcesBuildPhase section */
 
+/* Begin PBXShellScriptBuildPhase section */
+		11EDAF85CB22C72703F88976 /* [CP] Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-AuthenticationExample-AuthenticationExampleUITests/Pods-AuthenticationExample-AuthenticationExampleUITests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+			);
+			name = "[CP] Embed Pods Frameworks";
+			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-AuthenticationExample-AuthenticationExampleUITests/Pods-AuthenticationExample-AuthenticationExampleUITests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AuthenticationExample-AuthenticationExampleUITests/Pods-AuthenticationExample-AuthenticationExampleUITests-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		480713011020E8031CAD2B7A /* [CP] Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-AuthenticationExample/Pods-AuthenticationExample-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+			);
+			name = "[CP] Embed Pods Frameworks";
+			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-AuthenticationExample/Pods-AuthenticationExample-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AuthenticationExample/Pods-AuthenticationExample-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		577207719C3D7E5BCBFCA86F /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-AuthenticationExample-AuthenticationExampleUITests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		B789BC89EF112D031ADBC466 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-SwiftApiTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		C460DD7E6CE790777EBAC309 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-AuthenticationExample-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+		E3CD5F9D10D2B48314C4CCF0 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-ObjCApiTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
 /* Begin PBXSourcesBuildPhase section */
 		DE8FD4762A7D96B900B6831A /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
@@ -520,6 +703,7 @@
 				EAB3A17C2494628200385291 /* UserViewController.swift in Sources */,
 				EA20B50C249E8F0700B5E581 /* AuthMenu.swift in Sources */,
 				EAE4CBC924855E3A00245E92 /* AuthViewController.swift in Sources */,
+				24C7B21B2BA7CE410055CD63 /* MultiFactorMenu.swift in Sources */,
 				EA02F68524A000E00079D000 /* UserActions.swift in Sources */,
 				EA02F68D24A063E90079D000 /* LoginDelegate.swift in Sources */,
 				EA20B50A249D3D8600B5E581 /* PasswordlessViewController.swift in Sources */,
@@ -531,8 +715,10 @@
 				DEC2E5DD2A95331E0090260A /* SettingsViewController.swift in Sources */,
 				EA20B503249C6C3D00B5E581 /* CustomAuthViewController.swift in Sources */,
 				EAE4CBC524855E3A00245E92 /* AppDelegate.swift in Sources */,
+				24C7B2192BA7CDF80055CD63 /* AuthViewController+MultiFactor.swift in Sources */,
 				EAFDF2BE2490439F0082B6F1 /* Animator.swift in Sources */,
 				EAE4CBC724855E3A00245E92 /* SceneDelegate.swift in Sources */,
+				24C7B2152B9BC5860055CD63 /* CustomQRCodeView.swift in Sources */,
 				EAE08EB524CF5D09006FA3A5 /* AccountLinkingViewController.swift in Sources */,
 				EA527CAC24A0EE9600ADB9A2 /* LoginView.swift in Sources */,
 				EAEBCE0F2489FFDE00FCEA92 /* Extensions.swift in Sources */,
@@ -586,6 +772,7 @@
 /* Begin XCBuildConfiguration section */
 		DE8FD4812A7D96BB00B6831A /* Debug */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = 0E8714DC0F0E59EE0A8E7B9B /* Pods-ObjCApiTests.debug.xcconfig */;
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				BUNDLE_LOADER = "$(TEST_HOST)";
@@ -606,6 +793,7 @@
 		};
 		DE8FD4822A7D96BB00B6831A /* Release */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = DE11518ACF28A8E3DBF0092C /* Pods-ObjCApiTests.release.xcconfig */;
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				BUNDLE_LOADER = "$(TEST_HOST)";
@@ -740,13 +928,13 @@
 		};
 		EAE4CBEC24855E3E00245E92 /* Debug */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = FD88E272757946CF8EDFD573 /* Pods-AuthenticationExample.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CODE_SIGN_ENTITLEMENTS = AuthenticationExample/AuthenticationExample.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
-				"CODE_SIGN_IDENTITY[sdk=*]" = "iPhone Developer";
 				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_TEAM = "";
+				DEVELOPMENT_TEAM = BVWXHG3S28;
 				INFOPLIST_FILE = AuthenticationExample/SwiftApplication.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -763,13 +951,13 @@
 		};
 		EAE4CBED24855E3E00245E92 /* Release */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = 174F2A059457D01B253ABFDD /* Pods-AuthenticationExample.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CODE_SIGN_ENTITLEMENTS = AuthenticationExample/AuthenticationExample.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
-				"CODE_SIGN_IDENTITY[sdk=*]" = "iPhone Developer";
 				CODE_SIGN_STYLE = Automatic;
-				DEVELOPMENT_TEAM = "";
+				DEVELOPMENT_TEAM = BVWXHG3S28;
 				INFOPLIST_FILE = AuthenticationExample/SwiftApplication.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -786,6 +974,7 @@
 		};
 		EAE4CBEF24855E3E00245E92 /* Debug */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = 274B4AD0EDFB249DC7942BCF /* Pods-SwiftApiTests.debug.xcconfig */;
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
 				BUNDLE_LOADER = "$(TEST_HOST)";
@@ -811,6 +1000,7 @@
 		};
 		EAE4CBF024855E3E00245E92 /* Release */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = 5FCA462A653F4A643BEEC832 /* Pods-SwiftApiTests.release.xcconfig */;
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
 				BUNDLE_LOADER = "$(TEST_HOST)";
@@ -835,6 +1025,7 @@
 		};
 		EAE4CBF224855E3E00245E92 /* Debug */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = A3FEEF874D705F97CB76500B /* Pods-AuthenticationExample-AuthenticationExampleUITests.debug.xcconfig */;
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
 				CODE_SIGN_STYLE = Automatic;
@@ -856,6 +1047,7 @@
 		};
 		EAE4CBF324855E3E00245E92 /* Release */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7E3FF7CCFE89B70FC5B51C99 /* Pods-AuthenticationExample-AuthenticationExampleUITests.release.xcconfig */;
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
 				CODE_SIGN_STYLE = Automatic;

+ 23 - 0
FirebaseAuth/Tests/SampleSwift/AuthenticationExample/CustomViews/CustomQRCodeView.swift

@@ -0,0 +1,23 @@
+import UIKit
+
+class CustomQRCodeAlertViewController: UIViewController {
+  
+  @IBOutlet weak var qrCodeImageView: UIImageView!
+  @IBOutlet weak var textField: UITextField!
+  
+  var completion: ((String) -> Void)?
+  
+  override func viewDidLoad() {
+    super.viewDidLoad()
+  }
+  
+  @IBAction func submitButtonTapped(_ sender: UIButton) {
+    guard let text = textField.text else { return }
+    completion?(text)
+    dismiss(animated: true, completion: nil)
+  }
+  
+  @IBAction func cancelButtonTapped(_ sender: UIButton) {
+    dismiss(animated: true, completion: nil)
+  }
+}

+ 34 - 0
FirebaseAuth/Tests/SampleSwift/AuthenticationExample/GoogleService-Info.plist

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CLIENT_ID</key>
+	<string>585304629422-ab795evno6vsrj7i7o1a3gi6eo01508b.apps.googleusercontent.com</string>
+	<key>REVERSED_CLIENT_ID</key>
+	<string>com.googleusercontent.apps.585304629422-ab795evno6vsrj7i7o1a3gi6eo01508b</string>
+	<key>API_KEY</key>
+	<string>AIzaSyB_IEBBC1GNzpBUJPTfO40OD3lXtPtC3r8</string>
+	<key>GCM_SENDER_ID</key>
+	<string>585304629422</string>
+	<key>PLIST_VERSION</key>
+	<string>1</string>
+	<key>BUNDLE_ID</key>
+	<string>com.google.FirebaseExperimental1.dev</string>
+	<key>PROJECT_ID</key>
+	<string>test-project-pragatimodi-9ce7c</string>
+	<key>STORAGE_BUCKET</key>
+	<string>test-project-pragatimodi-9ce7c.appspot.com</string>
+	<key>IS_ADS_ENABLED</key>
+	<false/>
+	<key>IS_ANALYTICS_ENABLED</key>
+	<false/>
+	<key>IS_APPINVITE_ENABLED</key>
+	<true/>
+	<key>IS_GCM_ENABLED</key>
+	<true/>
+	<key>IS_SIGNIN_ENABLED</key>
+	<true/>
+	<key>GOOGLE_APP_ID</key>
+	<string>1:585304629422:ios:cb0e5761816443a9d98d3f</string>
+</dict>
+</plist>

+ 34 - 0
FirebaseAuth/Tests/SampleSwift/AuthenticationExample/GoogleService-Info_multi.plist

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CLIENT_ID</key>
+	<string>585304629422-e7gpk5dr3ep29neig59oaksog6s5vpeu.apps.googleusercontent.com</string>
+	<key>REVERSED_CLIENT_ID</key>
+	<string>com.googleusercontent.apps.585304629422-e7gpk5dr3ep29neig59oaksog6s5vpeu</string>
+	<key>API_KEY</key>
+	<string>AIzaSyB_IEBBC1GNzpBUJPTfO40OD3lXtPtC3r8</string>
+	<key>GCM_SENDER_ID</key>
+	<string>585304629422</string>
+	<key>PLIST_VERSION</key>
+	<string>1</string>
+	<key>BUNDLE_ID</key>
+	<string>com.google.FirebaseExperimental2.dev</string>
+	<key>PROJECT_ID</key>
+	<string>test-project-pragatimodi-9ce7c</string>
+	<key>STORAGE_BUCKET</key>
+	<string>test-project-pragatimodi-9ce7c.appspot.com</string>
+	<key>IS_ADS_ENABLED</key>
+	<false></false>
+	<key>IS_ANALYTICS_ENABLED</key>
+	<false></false>
+	<key>IS_APPINVITE_ENABLED</key>
+	<true></true>
+	<key>IS_GCM_ENABLED</key>
+	<true></true>
+	<key>IS_SIGNIN_ENABLED</key>
+	<true></true>
+	<key>GOOGLE_APP_ID</key>
+	<string>1:585304629422:ios:d283e0ba72159148d98d3f</string>
+</dict>
+</plist>

+ 17 - 7
FirebaseAuth/Tests/SampleSwift/AuthenticationExample/LaunchScreen.storyboard

@@ -1,9 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="9HG-hg-zZf">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="9HG-hg-zZf">
     <device id="retina6_1" orientation="portrait" appearance="light"/>
     <dependencies>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <scenes>
@@ -14,8 +16,8 @@
                     <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
                         <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
-                        <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
                         <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                     </view>
                     <navigationItem key="navigationItem" title="Firebase Auth" largeTitleDisplayMode="always" id="6Fc-9p-NcN"/>
                 </viewController>
@@ -29,15 +31,15 @@
                 <navigationController title="Firebase Auth" automaticallyAdjustsScrollViewInsets="NO" id="9HG-hg-zZf" sceneMemberID="viewController">
                     <toolbarItems/>
                     <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="7WM-IN-qHh">
-                        <rect key="frame" x="0.0" y="44" width="414" height="96"/>
+                        <rect key="frame" x="0.0" y="48" width="414" height="96"/>
                         <autoresizingMask key="autoresizingMask"/>
                         <textAttributes key="titleTextAttributes">
                             <fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/>
-                            <color key="textColor" systemColor="systemOrangeColor" red="1" green="0.58431372550000005" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <color key="textColor" systemColor="systemOrangeColor"/>
                         </textAttributes>
                         <textAttributes key="largeTitleTextAttributes">
                             <fontDescription key="fontDescription" type="boldSystem" pointSize="33"/>
-                            <color key="textColor" systemColor="systemOrangeColor" red="1" green="0.58431372550000005" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <color key="textColor" systemColor="systemOrangeColor"/>
                         </textAttributes>
                     </navigationBar>
                     <nil name="viewControllers"/>
@@ -50,5 +52,13 @@
             <point key="canvasLocation" x="52.173913043478265" y="375"/>
         </scene>
     </scenes>
-    <color key="tintColor" systemColor="systemOrangeColor" red="1" green="0.58431372550000005" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+    <color key="tintColor" systemColor="systemOrangeColor"/>
+    <resources>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+        <systemColor name="systemOrangeColor">
+            <color red="1" green="0.58431372550000005" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
+    </resources>
 </document>

+ 19 - 27
FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Models/AuthMenu.swift

@@ -48,9 +48,9 @@ enum AuthMenu: String {
   case checkActionCode
   case applyActionCode
   case verifyPasswordResetCode
-  case phoneEnroll
-  case totpEnroll
-  case multifactorUnenroll
+//  case phoneEnroll
+//  case totpEnroll
+//  case multifactorUnenroll
 
   // More intuitively named getter for `rawValue`.
   var id: String { rawValue }
@@ -126,13 +126,13 @@ enum AuthMenu: String {
       return "Apply Action Code"
     case .verifyPasswordResetCode:
       return "Verify Password Reset Code"
-    // Multi factor
-    case .phoneEnroll:
-      return "Phone Enroll"
-    case .totpEnroll:
-      return "TOTP Enroll"
-    case .multifactorUnenroll:
-      return "Multifactor unenroll"
+//    // Multi factor
+//    case .phoneEnroll:
+//      return "Phone Enroll"
+//    case .totpEnroll:
+//      return "TOTP Enroll"
+//    case .multifactorUnenroll:
+//      return "Multifactor unenroll"
     }
   }
 
@@ -204,12 +204,12 @@ enum AuthMenu: String {
       self = .applyActionCode
     case "Verify Password Reset Code":
       self = .verifyPasswordResetCode
-    case "Phone Enroll":
-      self = .phoneEnroll
-    case "TOTP Enroll":
-      self = .totpEnroll
-    case "Multifactor unenroll":
-      self = .multifactorUnenroll
+//    case "Phone Enroll":
+//      self = .phoneEnroll
+//    case "TOTP Enroll":
+//      self = .totpEnroll
+//    case "Multifactor unenroll":
+//      self = .multifactorUnenroll
     default:
       return nil
     }
@@ -332,20 +332,12 @@ class AuthMenuData: DataSourceProvidable {
     ]
     return Section(headerDescription: header, items: items)
   }
-  
-  static var multifactorSection: Section {
-    let header = "Multi Factor"
-    let items: [Item] = [
-      Item(title: AuthMenu.phoneEnroll.name),
-      Item(title: AuthMenu.totpEnroll.name),
-      Item(title: AuthMenu.multifactorUnenroll.name)
-    ]
-    return Section(headerDescription: header, items: items)
-  }
+
+  //static var multifactorSection: MultiFactorMenuData.
 
   static var sections: [Section] =
     [settingsSection, providerSection, emailPasswordSection, otherSection, recaptchaSection,
-     customAuthDomainSection, appSection, oobSection, multifactorSection]
+     customAuthDomainSection, appSection, oobSection, MultiFactorMenuData.multifactorSection]
 
   static var authLinkSections: [Section] {
     let allItems = AuthMenuData.sections.flatMap { $0.items }

+ 83 - 0
FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Models/MultiFactorMenu.swift

@@ -0,0 +1,83 @@
+  // Copyright 2020 Google LLC
+  //
+  // Licensed under the Apache License, Version 2.0 (the "License");
+  // you may not use this file except in compliance with the License.
+  // You may obtain a copy of the License at
+  //
+  //      http://www.apache.org/licenses/LICENSE-2.0
+  //
+  // Unless required by applicable law or agreed to in writing, software
+  // distributed under the License is distributed on an "AS IS" BASIS,
+  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  // See the License for the specific language governing permissions and
+  // limitations under the License.
+
+import UIKit
+
+  /// Firebase Auth supported identity providers and other methods of authentication
+enum MultiFactorMenu: String {
+  case phoneEnroll
+  case totpEnroll
+  case multifactorUnenroll
+  
+    // More intuitively named getter for `rawValue`.
+  var id: String { rawValue }
+  
+    // The UI friendly name of the `AuthMenu`. Used for display.
+  var name: String {
+    switch self {
+        // Multi factor
+    case .phoneEnroll:
+      return "Phone Enroll"
+    case .totpEnroll:
+      return "TOTP Enroll"
+    case .multifactorUnenroll:
+      return "Multifactor unenroll"
+    }
+  }
+  
+    // Failable initializer to create an `AuthMenu` from its corresponding `name` value.
+    // - Parameter rawValue: String value representing `AuthMenu`'s name or type.
+  init?(rawValue: String) {
+    switch rawValue {
+    case "Phone Enroll":
+      self = .phoneEnroll
+    case "TOTP Enroll":
+      self = .totpEnroll
+    case "Multifactor unenroll":
+      self = .multifactorUnenroll
+    default:
+      return nil
+    }
+  }
+}
+
+  // MARK: DataSourceProvidable
+
+class MultiFactorMenuData : AuthMenuData {
+  
+  static var multifactorSection: Section {
+    let header = "Multi Factor"
+    let items: [Item] = [
+      Item(title: MultiFactorMenu.phoneEnroll.name),
+      Item(title: MultiFactorMenu.totpEnroll.name),
+      Item(title: MultiFactorMenu.multifactorUnenroll.name),
+    ]
+    return Section(headerDescription: header, items: items)
+  }
+  
+  
+}
+  
+//  static var sections: [Section] =
+//  [multifactorSection]
+  
+//  static var authLinkSections: [Section] {
+//    let allItems = AuthMenuData.sections.flatMap { $0.items }
+//    let header = "Manage linking between providers"
+//    let footer =
+//    "Select an unchecked row to link the currently signed in user to that auth provider. To unlink the user from a linked provider, select its corresponding row marked with a checkmark."
+//    return [Section(headerDescription: header, footerDescription: footer, items: allItems)]
+//  }
+  
+//  var sections: [Section] = AuthMenuData.sections

+ 3 - 0
FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist

@@ -24,6 +24,9 @@
 			<key>CFBundleURLName</key>
 			<string></string>
 			<key>CFBundleURLSchemes</key>
+			<array>
+				<string>com.googleusercontent.apps.585304629422-ab795evno6vsrj7i7o1a3gi6eo01508b</string>
+			</array>
 		</dict>
 	</array>
 	<key>CFBundleVersion</key>

+ 38 - 0
FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController+MultiFactor.swift

@@ -0,0 +1,38 @@
+//
+//  AuthViewController+MultiFactor.swift
+//  AuthenticationExample
+//
+//  Created by Pragati Modi on 17/03/24.
+//  Copyright © 2024 Firebase. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+class MultiFactorViewController: AuthViewController {
+  override func didSelectRowAt(_ indexPath: IndexPath, on tableView: UITableView) {
+    let item = dataSourceProvider.item(at: indexPath)
+    
+    let itemName = item.title!
+    
+      //    guard let provider = AuthMenu(rawValue: providerName) else {
+      //      // The row tapped has no affiliated action.
+      //      return
+      //    }
+    if let mfaOption = MultiFactorMenu(rawValue: itemName) {
+      switch mfaOption {
+      case .phoneEnroll:
+        return
+        
+      case .totpEnroll:
+        return
+        
+      case .multifactorUnenroll:
+        return
+      }
+    } else {
+      return
+    }
+  }
+
+}

+ 328 - 189
FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController.swift

@@ -73,120 +73,224 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
 
   func didSelectRowAt(_ indexPath: IndexPath, on tableView: UITableView) {
     let item = dataSourceProvider.item(at: indexPath)
-
-    let providerName = item.title!
-
-    guard let provider = AuthMenu(rawValue: providerName) else {
-      // The row tapped has no affiliated action.
+    
+    let itemName = item.title!
+    
+      //    guard let provider = AuthMenu(rawValue: providerName) else {
+      //      // The row tapped has no affiliated action.
+      //      return
+      //    }
+    if let provider = AuthMenu(rawValue: itemName) {
+      switch provider {
+      case .settings:
+        performSettings()
+        
+      case .google:
+        performGoogleSignInFlow()
+        
+      case .apple:
+        performAppleSignInFlow()
+        
+      case .facebook:
+        performFacebookSignInFlow()
+        
+      case .twitter, .microsoft, .gitHub, .yahoo:
+        performOAuthLoginFlow(for: provider)
+        
+      case .gameCenter:
+        performGameCenterLoginFlow()
+        
+      case .emailPassword:
+        performDemoEmailPasswordLoginFlow()
+        
+      case .passwordless:
+        performPasswordlessLoginFlow()
+        
+      case .phoneNumber:
+        performPhoneNumberLoginFlow()
+        
+      case .anonymous:
+        performAnonymousLoginFlow()
+        
+      case .custom:
+        performCustomAuthLoginFlow()
+        
+      case .initRecaptcha:
+        performInitRecaptcha()
+        
+      case .customAuthDomain:
+        performCustomAuthDomainFlow()
+        
+      case .getToken:
+        getUserTokenResult(force: false)
+        
+      case .getTokenForceRefresh:
+        getUserTokenResult(force: true)
+        
+      case .addAuthStateChangeListener:
+        addAuthStateListener()
+        
+      case .removeLastAuthStateChangeListener:
+        removeAuthStateListener()
+        
+      case .addIdTokenChangeListener:
+        addIDTokenListener()
+        
+      case .removeLastIdTokenChangeListener:
+        removeIDTokenListener()
+        
+      case .verifyClient:
+        verifyClient()
+        
+      case .deleteApp:
+        deleteApp()
+        
+      case .actionType:
+        toggleActionCodeRequestType(at: indexPath)
+        
+      case .continueURL:
+        changeActionCodeContinueURL(at: indexPath)
+        
+      case .requestVerifyEmail:
+        requestVerifyEmail()
+        
+      case .requestPasswordReset:
+        requestPasswordReset()
+        
+      case .resetPassword:
+        resetPassword()
+        
+      case .checkActionCode:
+        checkActionCode()
+        
+      case .applyActionCode:
+        applyActionCode()
+        
+      case .verifyPasswordResetCode:
+        verifyPasswordResetCode()
+      }
+//    } else if let mfaOption = MultiFactorMenu(rawValue: itemName) {
+//      switch mfaOption {
+//      case .phoneEnroll:
+//        phoneEnroll()
+//        
+//      case .totpEnroll:
+//        totpEnroll()
+//        
+//      case .multifactorUnenroll:
+//        mfaUnenroll()
+//      }
+    } else {
       return
     }
+  }
 
-    switch provider {
-    case .settings:
-      performSettings()
-
-    case .google:
-      performGoogleSignInFlow()
-
-    case .apple:
-      performAppleSignInFlow()
-
-    case .facebook:
-      performFacebookSignInFlow()
-
-    case .twitter, .microsoft, .gitHub, .yahoo:
-      performOAuthLoginFlow(for: provider)
-
-    case .gameCenter:
-      performGameCenterLoginFlow()
-
-    case .emailPassword:
-      performDemoEmailPasswordLoginFlow()
-
-    case .passwordless:
-      performPasswordlessLoginFlow()
-
-    case .phoneNumber:
-      performPhoneNumberLoginFlow()
-
-    case .anonymous:
-      performAnonymousLoginFlow()
-
-    case .custom:
-      performCustomAuthLoginFlow()
-
-    case .initRecaptcha:
-      performInitRecaptcha()
-
-    case .customAuthDomain:
-      performCustomAuthDomainFlow()
-
-    case .getToken:
-      getUserTokenResult(force: false)
-
-    case .getTokenForceRefresh:
-      getUserTokenResult(force: true)
+  // MARK: - Firebase 🔥
 
-    case .addAuthStateChangeListener:
-      addAuthStateListener()
+  private func performSettings() {
+    let settingsController = SettingsViewController()
+    navigationController?.pushViewController(settingsController, animated: true)
+  }
 
-    case .removeLastAuthStateChangeListener:
-      removeAuthStateListener()
+  func authenticate(withSecondFactorError error: Error?, workflow: String) {
+    guard let error = error as NSError?,
+          let resolver = error.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as? MultiFactorResolver else {
+      // No resolver found, possibly no multi-factor setup required.
+      return
+    }
 
-    case .addIdTokenChangeListener:
-      addIDTokenListener()
+    var factorTypes: [String] = []
+    var selectedFactor: MultiFactorInfo?
 
-    case .removeLastIdTokenChangeListener:
-      removeIDTokenListener()
+    for hint in resolver.hints {
+      if let displayName = hint.displayName {
+        factorTypes.append(displayName)
+      }
+    }
 
-    case .verifyClient:
-      verifyClient()
+    let alertController = UIAlertController(
+      title: "Select factor type to \(workflow)",
+      message: nil,
+      preferredStyle: .actionSheet
+    )
 
-    case .deleteApp:
-      deleteApp()
+    for factorType in factorTypes {
+      let action = UIAlertAction(title: factorType, style: .default) { _ in
+        selectedFactor = resolver.hints.first(where: { $0.displayName == factorType })
+        self.handleSelectedFactor(selectedFactor, forWorkflow: workflow, withResolver: resolver)
+      }
+      alertController.addAction(action)
+    }
 
-    case .actionType:
-      toggleActionCodeRequestType(at: indexPath)
+    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
+    alertController.addAction(cancelAction)
 
-    case .continueURL:
-      changeActionCodeContinueURL(at: indexPath)
+    present(alertController, animated: true, completion: nil)
+  }
 
-    case .requestVerifyEmail:
-      requestVerifyEmail()
+  private func handleSelectedFactor(_ factor: MultiFactorInfo?, forWorkflow workflow: String,
+                                    withResolver resolver: MultiFactorResolver) {
+    guard let factor = factor else { return }
 
-    case .requestPasswordReset:
-      requestPasswordReset()
+    if factor.factorID == "phone", let phoneHint = factor as? PhoneMultiFactorInfo {
+      PhoneAuthProvider.provider().verifyPhoneNumber(with: phoneHint, uiDelegate: nil, multiFactorSession: resolver.session, completion: {
+        verificationID, error in
+        if let error = error {
+          self.showAlert(for: "Multi factor signin failed.")
+          print("Multi factor start \(workflow) failed. Error: \(error)")
+        } else {
+          self.showTextInputPrompt(with: "Verification code for \(phoneHint.displayName)") {
+            verificationCode in
+            guard !verificationCode.isEmpty else {
+              print("Verification code not entered.")
+              return
+            }
 
-    case .resetPassword:
-      resetPassword()
+            let credential = PhoneAuthProvider.provider().credential(
+              withVerificationID: verificationID!,
+              verificationCode: verificationCode
+            )
+            let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
 
-    case .checkActionCode:
-      checkActionCode()
+            resolver.resolveSignIn(with: assertion) { 
+              authResult, error in
+              if let error = error {
+                print("Phone Multi factor finalize \(workflow) failed. Error: \(error)")
+              } else {
+                print("Phone Multi factor finalize \(workflow) succeeded.")
+                self.transitionToUserViewController()
+              }
+            }
+          }
+        }
+      })
+    } else if factor.factorID == "totp" {
+      showTextInputPrompt(with: "TOTP Verification code for \(factor.displayName)") { oneTimePassword in
+        guard !oneTimePassword.isEmpty else {
+          print("TOTP Verification code not entered.")
+          return
+        }
 
-    case .applyActionCode:
-      applyActionCode()
+        let assertion = TOTPMultiFactorGenerator.assertionForSignIn(
+          withEnrollmentID: factor.uid,
+          oneTimePassword: oneTimePassword
+        )
 
-    case .verifyPasswordResetCode:
-      verifyPasswordResetCode()
-    
-    case .phoneEnroll:
-      phoneEnroll()
-      
-    case .totpEnroll:
-      totpEnroll()
-      
-    case .multifactorUnenroll:
-      mfaUnenroll()
+        resolver.resolveSignIn(with: assertion) { authResult, error in
+          if let error = error {
+            print("TOTP Multi factor finalize \(workflow) failed. Error: \(error)")
+          } else {
+            print("TOTP Multi factor finalize \(workflow) succeeded.")
+            self.transitionToUserViewController()
+          }
+        }
+      }
+    } else {
+      showAlert(for: "Factor ID not supported.")
+      print("Multi factor sign in does not support factor ID: \(factor.factorID).")
     }
   }
 
-  // MARK: - Firebase 🔥
-
-  private func performSettings() {
-    let settingsController = SettingsViewController()
-    navigationController?.pushViewController(settingsController, animated: true)
-  }
-
   private func performGoogleSignInFlow() {
     // [START headless_google_auth]
     guard let clientID = FirebaseApp.app()?.options.clientID else { return }
@@ -235,7 +339,14 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
     // [START signin_google_credential]
     AppManager.shared.auth().signIn(with: credential) { result, error in
       // [START_EXCLUDE silent]
-      guard error == nil else { return self.displayError(error) }
+      guard error == nil else {
+        if let error = error as NSError?, error.code == AuthErrorCode.secondFactorRequired.rawValue {
+          self.authenticate(withSecondFactorError: error, workflow: "sign in")
+          return
+        } else {
+          return self.displayError(error)
+        }
+      }
       // [END_EXCLUDE]
 
       // At this point, our user is signed in
@@ -726,14 +837,14 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
       AppManager.shared.auth().verifyPasswordResetCode(oobCode, completion: completionHandler)
     }
   }
-  
+
   private func phoneEnroll() {
     guard let user = AppManager.shared.auth().currentUser else {
       showAlert(for: "No user logged in!")
       print("Error: User must be logged in first.")
       return
     }
-    
+
     showTextInputPrompt(with: "Phone Number:") { phoneNumber in
       user.multiFactor.getSessionWithCompletion { session, error in
         guard let session = session else { return }
@@ -742,41 +853,45 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
           print("Multi factor start enroll failed. Error: \(error!)")
           return
         }
-        
-        PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, multiFactorSession: session) { verificationID, error in
-          guard error == nil else {
-            self.showAlert(for: "Enrollment failed")
-            print("Multi factor start enroll failed. Error: \(error!)")
-            return
-          }
-          
-          self.showTextInputPrompt(with: "Verification Code: ") { verificationCode in
-            let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationID!, verificationCode: verificationCode)
-            let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
-            
-            self.showTextInputPrompt(with: "Display Name:") { displayName in
-              user.multiFactor.enroll(with: assertion, displayName: displayName) { error in
-                if let error = error {
-                  self.showAlert(for: "Enrollment failed")
-                  print("Multi factor finalize enroll failed. Error: \(error)")
-                } else {
-                  self.showAlert(for: "Successfully enrolled: \(displayName)")
-                  print("Multi factor finalize enroll succeeded.")
+
+        PhoneAuthProvider.provider()
+          .verifyPhoneNumber(phoneNumber, multiFactorSession: session) { verificationID, error in
+            guard error == nil else {
+              self.showAlert(for: "Enrollment failed")
+              print("Multi factor start enroll failed. Error: \(error!)")
+              return
+            }
+
+            self.showTextInputPrompt(with: "Verification Code: ") { verificationCode in
+              let credential = PhoneAuthProvider.provider().credential(
+                withVerificationID: verificationID!,
+                verificationCode: verificationCode
+              )
+              let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
+
+              self.showTextInputPrompt(with: "Display Name:") { displayName in
+                user.multiFactor.enroll(with: assertion, displayName: displayName) { error in
+                  if let error = error {
+                    self.showAlert(for: "Enrollment failed")
+                    print("Multi factor finalize enroll failed. Error: \(error)")
+                  } else {
+                    self.showAlert(for: "Successfully enrolled: \(displayName)")
+                    print("Multi factor finalize enroll succeeded.")
+                  }
                 }
               }
             }
           }
-        }
       }
     }
   }
-  
+
   private func totpEnroll() {
     guard let user = AppManager.shared.auth().currentUser else {
       print("Error: User must be logged in first.")
       return
     }
-    
+
     user.multiFactor.getSessionWithCompletion { session, error in
       guard let session = session, error == nil else {
         if let error = error {
@@ -788,113 +903,121 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
         }
         return
       }
-      
+
       TOTPMultiFactorGenerator.generateSecret(with: session) { secret, error in
         guard let secret = secret, error == nil else {
           if let error = error {
             self.showAlert(for: "Enrollment failed")
             print("Error generating TOTP secret. Error: \(error.localizedDescription)")
-          } else {
-            self.showAlert(for: "Enrollment failed")
-            print("Error generating TOTP secret.")
           }
           return
         }
-        
+
         guard let accountName = user.email, let issuer = Auth.auth().app?.name else {
           self.showAlert(for: "Enrollment failed")
           print("Multi factor finalize enroll failed. Could not get account details.")
           return
         }
-        
+
         DispatchQueue.main.async {
           let url = secret.generateQRCodeURL(withAccountName: accountName, issuer: issuer)
-          
+
           guard !url.isEmpty else {
             self.showAlert(for: "Enrollment failed")
             print("Multi factor finalize enroll failed. Could not generate URL.")
             return
           }
-          
+
           secret.openInOTPApp(withQRCodeURL: url)
-          
-          self.showQRCodePromptWithTextInput(with: "Scan this QR code and enter OTP:", url: url) { oneTimePassword in
-            guard !oneTimePassword.isEmpty else {
-              self.showAlert(for: "Display name must not be empty")
-              print("OTP not entered.")
-              return
-            }
-            
-            let assertion = TOTPMultiFactorGenerator.assertionForEnrollment(with: secret, oneTimePassword: oneTimePassword)
-            
-            self.showTextInputPrompt(with: "Display Name") { displayName in
-              guard !displayName.isEmpty else {
+
+          self
+            .showQRCodePromptWithTextInput(with: "Scan this QR code and enter OTP:",
+                                           url: url) { oneTimePassword in
+              guard !oneTimePassword.isEmpty else {
                 self.showAlert(for: "Display name must not be empty")
-                print("Display name not entered.")
+                print("OTP not entered.")
                 return
               }
-              
-              user.multiFactor.enroll(with: assertion, displayName: displayName) { error in
-                if let error = error {
-                  self.showAlert(for: "Enrollment failed")
-                  print("Multi factor finalize enroll failed. Error: \(error.localizedDescription)")
-                } else {
-                  self.showAlert(for: "Successfully enrolled: \(displayName)")
-                  print("Multi factor finalize enroll succeeded.")
+
+              let assertion = TOTPMultiFactorGenerator.assertionForEnrollment(
+                with: secret,
+                oneTimePassword: oneTimePassword
+              )
+
+              self.showTextInputPrompt(with: "Display Name") { displayName in
+                guard !displayName.isEmpty else {
+                  self.showAlert(for: "Display name must not be empty")
+                  print("Display name not entered.")
+                  return
+                }
+
+                user.multiFactor.enroll(with: assertion, displayName: displayName) { error in
+                  if let error = error {
+                    self.showAlert(for: "Enrollment failed")
+                    print(
+                      "Multi factor finalize enroll failed. Error: \(error.localizedDescription)"
+                    )
+                  } else {
+                    self.showAlert(for: "Successfully enrolled: \(displayName)")
+                    print("Multi factor finalize enroll succeeded.")
+                  }
                 }
               }
             }
-          }
         }
       }
     }
   }
-  
+
   func mfaUnenroll() {
     var displayNames: [String] = []
-    
+
     guard let currentUser = Auth.auth().currentUser else {
       print("Error: No current user")
       return
     }
-    
+
     for factorInfo in currentUser.multiFactor.enrolledFactors {
       if let displayName = factorInfo.displayName {
         displayNames.append(displayName)
       }
     }
-    
-    let alertController = UIAlertController(title: "Select Multi Factor to Unenroll", message: nil, preferredStyle: .actionSheet)
-    
+
+    let alertController = UIAlertController(
+      title: "Select Multi Factor to Unenroll",
+      message: nil,
+      preferredStyle: .actionSheet
+    )
+
     for displayName in displayNames {
       let action = UIAlertAction(title: displayName, style: .default) { _ in
         self.unenrollFactor(with: displayName)
       }
       alertController.addAction(action)
     }
-    
+
     let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
     alertController.addAction(cancelAction)
-    
+
     present(alertController, animated: true, completion: nil)
   }
-  
+
   private func unenrollFactor(with displayName: String) {
     guard let currentUser = Auth.auth().currentUser else {
-      self.showAlert(for: "User must be logged in")
+      showAlert(for: "User must be logged in")
       print("Error: No current user")
       return
     }
-    
+
     var factorInfoToUnenroll: MultiFactorInfo?
-    
+
     for factorInfo in currentUser.multiFactor.enrolledFactors {
       if factorInfo.displayName == displayName {
         factorInfoToUnenroll = factorInfo
         break
       }
     }
-    
+
     if let factorInfo = factorInfoToUnenroll {
       currentUser.multiFactor.unenroll(withFactorUID: factorInfo.uid) { error in
         if let error = error {
@@ -908,9 +1031,6 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
     }
   }
 
-
-
-
   // MARK: - Private Helpers
 
   private func showTextInputPrompt(with message: String, completion: ((String) -> Void)? = nil) {
@@ -940,35 +1060,54 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
   }
   
   private func showQRCodePromptWithTextInput(with message: String, url: String, completion: ((String) -> Void)? = nil) {
-      // Create a UIAlertController
-    let alertController = UIAlertController(title: "QR Code Prompt", message: message, preferredStyle: .alert)
-    
-      // Add a text field for input
-    alertController.addTextField { (textField) in
-      textField.placeholder = "Enter text"
-    }
-    
+//      // Create a UIAlertController
+//    let alertController = UIAlertController(title: "QR Code Prompt", message: message, preferredStyle: .alert)
+//    
+//      // Add a text field for input
+//    alertController.addTextField { (textField) in
+//      textField.placeholder = "Enter text"
+//    }
+//    
       // Create a UIImage from the URL
     guard let image = generateQRCode(from: url) else {
       print("Failed to generate QR code")
       return
     }
-    
-      // Create an image view to display the QR code
+//    
+//      // Create an image view to display the QR code
     let imageView = UIImageView(image: image)
-    imageView.contentMode = .scaleAspectFit
-    imageView.translatesAutoresizingMaskIntoConstraints = false
+//    imageView.contentMode = .scaleAspectFit
+//    imageView.translatesAutoresizingMaskIntoConstraints = false
+//      // Add the image view to the alert controller
+//    alertController.view.addSubview(imageView)
+//    alertController.view.heightAnchor.constraint(equalToConstant: 240)
+//      // Add constraints to position the image view
+//    NSLayoutConstraint.activate([
+//      imageView.topAnchor.constraint(equalTo: alertController.view.topAnchor, constant: 20),
+//      imageView.centerXAnchor.constraint(equalTo: alertController.view.centerXAnchor),
+//      imageView.widthAnchor.constraint(equalToConstant: 200),
+//      imageView.heightAnchor.constraint(equalToConstant: 200)
+//    ])
+    
+    let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .alert)
     
-      // Add the image view to the alert controller
+      // Add image view
+    imageView.contentMode = .scaleAspectFit
+    let imageSize = CGSize(width: 200, height: 200) // Adjust the size as needed
+    imageView.frame = CGRect(origin: .zero, size: imageSize)
     alertController.view.addSubview(imageView)
     
-      // Add constraints to position the image view
-    NSLayoutConstraint.activate([
-      imageView.topAnchor.constraint(equalTo: alertController.view.topAnchor, constant: 20),
-      imageView.centerXAnchor.constraint(equalTo: alertController.view.centerXAnchor),
-      imageView.widthAnchor.constraint(equalToConstant: 200),
-      imageView.heightAnchor.constraint(equalToConstant: 200)
-    ])
+      // Add message label
+    let messageLabel = UILabel()
+    messageLabel.text = message
+    messageLabel.textAlignment = .center
+    messageLabel.numberOfLines = 0 // Allow multiple lines
+    alertController.view.addSubview(messageLabel)
+    
+      // Add text field
+    alertController.addTextField { textField in
+      textField.placeholder = "Enter text"
+    }
     
       // Add actions
     let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
@@ -988,16 +1127,16 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
     // Function to generate QR code from a string
   private func generateQRCode(from string: String) -> UIImage? {
     let data = string.data(using: String.Encoding.ascii)
-    
+
     if let filter = CIFilter(name: "CIQRCodeGenerator") {
       filter.setValue(data, forKey: "inputMessage")
       let transform = CGAffineTransform(scaleX: 10, y: 10)
-      
+
       if let output = filter.outputImage?.transformed(by: transform) {
         return UIImage(ciImage: output)
       }
     }
-    
+
     return nil
   }
 

+ 2 - 0
xcodebuild.log

@@ -0,0 +1,2 @@
+Command line invocation:
+