|
16 | 16 | // [START auth_import] |
17 | 17 | import FirebaseCore |
18 | 18 |
|
| 19 | +import SwiftUI |
| 20 | + |
19 | 21 | // For Sign in with Facebook |
20 | 22 | import FBSDKLoginKit |
21 | 23 |
|
@@ -178,7 +180,7 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { |
178 | 180 | phoneEnroll() |
179 | 181 |
|
180 | 182 | case .totpEnroll: |
181 | | - totpEnroll() |
| 183 | + Task { await totpEnroll() } |
182 | 184 |
|
183 | 185 | case .multifactorUnenroll: |
184 | 186 | mfaUnenroll() |
@@ -338,9 +340,9 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { |
338 | 340 | } |
339 | 341 |
|
340 | 342 | private func performDemoEmailPasswordLoginFlow() { |
341 | | - let loginController = LoginController() |
342 | | - loginController.delegate = self |
343 | | - navigationController?.pushViewController(loginController, animated: true) |
| 343 | + let loginView = LoginViewSwiftUI() |
| 344 | + let hostingController = UIHostingController(rootView: loginView) |
| 345 | + navigationController?.pushViewController(hostingController, animated: true) |
344 | 346 | } |
345 | 347 |
|
346 | 348 | private func performPasswordlessLoginFlow() { |
@@ -780,89 +782,53 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { |
780 | 782 | } |
781 | 783 | } |
782 | 784 |
|
783 | | - private func totpEnroll() { |
784 | | - guard let user = AppManager.shared.auth().currentUser else { |
785 | | - print("Error: User must be logged in first.") |
| 785 | + private func totpEnroll() async { |
| 786 | + guard |
| 787 | + let user = AppManager.shared.auth().currentUser, |
| 788 | + let accountName = user.email |
| 789 | + else { |
| 790 | + showAlert(for: "Enrollment failed: User must be logged and have email address.") |
786 | 791 | return |
787 | 792 | } |
788 | 793 |
|
789 | | - user.multiFactor.getSessionWithCompletion { session, error in |
790 | | - guard let session = session, error == nil else { |
791 | | - if let error = error { |
792 | | - self.showAlert(for: "Enrollment failed") |
793 | | - print("Multi factor start enroll failed. Error: \(error.localizedDescription)") |
794 | | - } else { |
795 | | - self.showAlert(for: "Enrollment failed") |
796 | | - print("Multi factor start enroll failed with unknown error.") |
797 | | - } |
| 794 | + guard let issuer = AppManager.shared.auth().app?.name else { |
| 795 | + showAlert(for: "Enrollment failed: Firebase app is missing name.") |
| 796 | + return |
| 797 | + } |
| 798 | + |
| 799 | + do { |
| 800 | + let session = try await user.multiFactor.session() |
| 801 | + let secret = try await TOTPMultiFactorGenerator.generateSecret(with: session) |
| 802 | + print("Secret: " + secret.sharedSecretKey()) |
| 803 | + |
| 804 | + let url = secret.generateQRCodeURL(withAccountName: accountName, issuer: issuer) |
| 805 | + guard !url.isEmpty else { |
| 806 | + showAlert(for: "Enrollment failed") |
| 807 | + print("Multi factor finalize enroll failed. Could not generate URL.") |
798 | 808 | return |
799 | 809 | } |
| 810 | + secret.openInOTPApp(withQRCodeURL: url) |
800 | 811 |
|
801 | | - TOTPMultiFactorGenerator.generateSecret(with: session) { secret, error in |
802 | | - guard let secret = secret, error == nil else { |
803 | | - if let error = error { |
804 | | - self.showAlert(for: "Enrollment failed") |
805 | | - print("Error generating TOTP secret. Error: \(error.localizedDescription)") |
806 | | - } else { |
807 | | - self.showAlert(for: "Enrollment failed") |
808 | | - print("Error generating TOTP secret.") |
809 | | - } |
810 | | - return |
811 | | - } |
812 | | - |
813 | | - guard let accountName = user.email, let issuer = Auth.auth().app?.name else { |
814 | | - self.showAlert(for: "Enrollment failed") |
815 | | - print("Multi factor finalize enroll failed. Could not get account details.") |
816 | | - return |
817 | | - } |
818 | | - |
819 | | - DispatchQueue.main.async { |
820 | | - let url = secret.generateQRCodeURL(withAccountName: accountName, issuer: issuer) |
821 | | - |
822 | | - guard !url.isEmpty else { |
823 | | - self.showAlert(for: "Enrollment failed") |
824 | | - print("Multi factor finalize enroll failed. Could not generate URL.") |
825 | | - return |
826 | | - } |
827 | | - |
828 | | - secret.openInOTPApp(withQRCodeURL: url) |
829 | | - |
830 | | - self |
831 | | - .showQRCodePromptWithTextInput(with: "Scan this QR code and enter OTP:", |
832 | | - url: url) { oneTimePassword in |
833 | | - guard !oneTimePassword.isEmpty else { |
834 | | - self.showAlert(for: "Display name must not be empty") |
835 | | - print("OTP not entered.") |
836 | | - return |
837 | | - } |
| 812 | + guard |
| 813 | + let oneTimePassword = await showTextInputPrompt(with: "Enter the one time passcode.") |
| 814 | + else { |
| 815 | + showAlert(for: "Enrollment failed: one time passcode not entered.") |
| 816 | + return |
| 817 | + } |
838 | 818 |
|
839 | | - let assertion = TOTPMultiFactorGenerator.assertionForEnrollment( |
840 | | - with: secret, |
841 | | - oneTimePassword: oneTimePassword |
842 | | - ) |
| 819 | + let assertion = TOTPMultiFactorGenerator.assertionForEnrollment( |
| 820 | + with: secret, |
| 821 | + oneTimePassword: oneTimePassword |
| 822 | + ) |
843 | 823 |
|
844 | | - self.showTextInputPrompt(with: "Display Name") { displayName in |
845 | | - guard !displayName.isEmpty else { |
846 | | - self.showAlert(for: "Display name must not be empty") |
847 | | - print("Display name not entered.") |
848 | | - return |
849 | | - } |
| 824 | + // TODO(nickcooke): Provide option to enter display name. |
| 825 | + try await user.multiFactor.enroll(with: assertion, displayName: "TOTP") |
850 | 826 |
|
851 | | - user.multiFactor.enroll(with: assertion, displayName: displayName) { error in |
852 | | - if let error = error { |
853 | | - self.showAlert(for: "Enrollment failed") |
854 | | - print( |
855 | | - "Multi factor finalize enroll failed. Error: \(error.localizedDescription)" |
856 | | - ) |
857 | | - } else { |
858 | | - self.showAlert(for: "Successfully enrolled: \(displayName)") |
859 | | - print("Multi factor finalize enroll succeeded.") |
860 | | - } |
861 | | - } |
862 | | - } |
863 | | - } |
864 | | - } |
865 | | - } |
| 827 | + showAlert(for: "Successfully enrolled: TOTP") |
| 828 | + print("Multi factor finalize enroll succeeded.") |
| 829 | + } catch { |
| 830 | + print(error) |
| 831 | + showAlert(for: "Enrollment failed: \(error.localizedDescription)") |
866 | 832 | } |
867 | 833 | } |
868 | 834 |
|
@@ -958,60 +924,12 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { |
958 | 924 | present(editController, animated: true, completion: nil) |
959 | 925 | } |
960 | 926 |
|
961 | | - private func showQRCodePromptWithTextInput(with message: String, url: String, |
962 | | - completion: ((String) -> Void)? = nil) { |
963 | | - // Create a UIAlertController |
964 | | - let alertController = UIAlertController( |
965 | | - title: "QR Code Prompt", |
966 | | - message: message, |
967 | | - preferredStyle: .alert |
968 | | - ) |
969 | | - |
970 | | - // Add a text field for input |
971 | | - alertController.addTextField { textField in |
972 | | - textField.placeholder = "Enter text" |
973 | | - } |
974 | | - |
975 | | - // Create a UIImage from the URL |
976 | | - guard let image = generateQRCode(from: url) else { |
977 | | - print("Failed to generate QR code") |
978 | | - return |
979 | | - } |
980 | | - |
981 | | - // Create an image view to display the QR code |
982 | | - let imageView = UIImageView(image: image) |
983 | | - imageView.contentMode = .scaleAspectFit |
984 | | - imageView.translatesAutoresizingMaskIntoConstraints = false |
985 | | - |
986 | | - // Add the image view to the alert controller |
987 | | - alertController.view.addSubview(imageView) |
988 | | - |
989 | | - // Add constraints to position the image view |
990 | | - NSLayoutConstraint.activate([ |
991 | | - imageView.topAnchor.constraint(equalTo: alertController.view.topAnchor, constant: 20), |
992 | | - imageView.centerXAnchor.constraint(equalTo: alertController.view.centerXAnchor), |
993 | | - imageView.widthAnchor.constraint(equalToConstant: 200), |
994 | | - imageView.heightAnchor.constraint(equalToConstant: 200), |
995 | | - ]) |
996 | | - |
997 | | - // Add actions |
998 | | - let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) |
999 | | - let submitAction = UIAlertAction(title: "Submit", style: .default) { _ in |
1000 | | - if let completion, |
1001 | | - let text = alertController.textFields?.first?.text { |
1002 | | - completion(text) |
| 927 | + private func showTextInputPrompt(with message: String) async -> String? { |
| 928 | + await withCheckedContinuation { continuation in |
| 929 | + showTextInputPrompt(with: message) { inputText in |
| 930 | + continuation.resume(returning: inputText.isEmpty ? nil : inputText) |
1003 | 931 | } |
1004 | 932 | } |
1005 | | - |
1006 | | - alertController.addAction(cancelAction) |
1007 | | - alertController.addAction(submitAction) |
1008 | | - |
1009 | | - // Present the alert controller |
1010 | | - UIApplication.shared.windows.first?.rootViewController?.present( |
1011 | | - alertController, |
1012 | | - animated: true, |
1013 | | - completion: nil |
1014 | | - ) |
1015 | 933 | } |
1016 | 934 |
|
1017 | 935 | // Function to generate QR code from a string |
|
0 commit comments