diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index ae3ad3b173..69e50191ff 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -8,6 +8,7 @@ public protocol ExternalAuthProvider { public protocol GoogleProviderAuthUIProtocol: ExternalAuthProvider { @MainActor func signInWithGoogle(clientID: String) async throws -> AuthCredential + @MainActor func deleteUser(user: User) async throws } public protocol FacebookProviderAuthUIProtocol: ExternalAuthProvider { @@ -268,7 +269,7 @@ public final class AuthService { try await handleAutoUpgradeAnonymousUser(credentials: credentials) } else { let result = try await auth.signIn(with: credentials) - signedInCredential = result.credential + signedInCredential = result.credential ?? credentials } updateAuthenticationState() } catch { @@ -301,11 +302,13 @@ public extension AuthService { func deleteUser() async throws { do { if let user = auth.currentUser, let providerId = signedInCredential?.provider { - if providerId == "password" { + if providerId == EmailAuthProviderID { let operation = EmailPasswordDeleteUserOperation(passwordPrompt: passwordPrompt) try await operation(on: user) - } else if providerId == "facebook.com" { + } else if providerId == FacebookAuthProviderID { try await facebookProvider.deleteUser(user: user) + } else if providerId == GoogleAuthProviderID { + try await googleProvider.deleteUser(user: user) } } diff --git a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/AccountService+Google.swift b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/AccountService+Google.swift new file mode 100644 index 0000000000..827c42183f --- /dev/null +++ b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/AccountService+Google.swift @@ -0,0 +1,54 @@ +// +// AccountService+Google.swift +// FirebaseUI +// +// Created by Russell Wheatley on 22/05/2025. +// + +// +// AccountService+Facebook.swift +// FirebaseUI +// +// Created by Russell Wheatley on 14/05/2025. +// + +@preconcurrency import FirebaseAuth +import FirebaseAuthSwiftUI +import Observation + +protocol GoogleOperationReauthentication { + var googleProvider: GoogleProviderAuthUI { get } +} + +extension GoogleOperationReauthentication { + @MainActor func reauthenticate() async throws -> AuthenticationToken { + guard let user = Auth.auth().currentUser else { + throw AuthServiceError.reauthenticationRequired("No user currently signed-in") + } + + do { + let credential = try await googleProvider + .signInWithGoogle(clientID: googleProvider.clientID) + try await user.reauthenticate(with: credential) + + return .firebase("") + } catch { + throw AuthServiceError.signInFailed(underlying: error) + } + } +} + +@MainActor +class GoogleDeleteUserOperation: AuthenticatedOperation, + @preconcurrency GoogleOperationReauthentication { + let googleProvider: GoogleProviderAuthUI + init(googleProvider: GoogleProviderAuthUI) { + self.googleProvider = googleProvider + } + + func callAsFunction(on user: User) async throws { + try await callAsFunction(on: user) { + try await user.delete() + } + } +} diff --git a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderAuthUI.swift b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderAuthUI.swift index 3fdde26635..308275f400 100644 --- a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderAuthUI.swift +++ b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderAuthUI.swift @@ -20,23 +20,20 @@ public class GoogleProviderAuthUI: @preconcurrency GoogleProviderAuthUIProtocol let scopes: [String] let shortName = "Google" let providerId = "google.com" - let clientID: String + public let clientID: String public init(scopes: [String]? = nil, clientID: String = FirebaseApp.app()!.options.clientID!) { self.scopes = scopes ?? kDefaultScopes self.clientID = clientID } @MainActor public func authButton() -> AnyView { - let customViewModel = GoogleSignInButtonViewModel( - scheme: .light, - style: .wide, - state: .normal - ) - return AnyView(GoogleSignInButton(viewModel: customViewModel) { - Task { - try await self.signInWithGoogle(clientID: self.clientID) - } - }) + // Moved to SignInWithGoogleButton so we could sign in via AuthService + AnyView(SignInWithGoogleButton()) + } + + public func deleteUser(user: User) async throws { + let operation = GoogleDeleteUserOperation(googleProvider: self) + try await operation(on: user) } @MainActor public func signInWithGoogle(clientID: String) async throws -> AuthCredential { diff --git a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Views/SignInWithGoogleButton.swift b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Views/SignInWithGoogleButton.swift new file mode 100644 index 0000000000..4f7c97cc93 --- /dev/null +++ b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Views/SignInWithGoogleButton.swift @@ -0,0 +1,37 @@ +// +// SignInWithGoogleButton.swift +// FirebaseUI +// +// Created by Russell Wheatley on 22/05/2025. +// +import FirebaseAuthSwiftUI +import FirebaseCore +import GoogleSignInSwift +import SwiftUI + +@MainActor +public struct SignInWithGoogleButton { + @Environment(AuthService.self) private var authService + + let customViewModel = GoogleSignInButtonViewModel( + scheme: .light, + style: .wide, + state: .normal + ) +} + +extension SignInWithGoogleButton: View { + public var body: some View { + GoogleSignInButton(viewModel: customViewModel) { + Task { + try await authService.signInWithGoogle() + } + } + } +} + +#Preview { + FirebaseOptions.dummyConfigurationForPreview() + return SignInWithGoogleButton() + .environment(AuthService()) +} diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/ContentView.swift b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/ContentView.swift index a9dc95f902..e481c91d7c 100644 --- a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/ContentView.swift +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/ContentView.swift @@ -25,7 +25,6 @@ struct ContentView: View { actionCodeSettings.linkDomain = "flutterfire-e2e-tests.firebaseapp.com" actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!) let configuration = AuthConfiguration( - shouldAutoUpgradeAnonymousUsers: true, tosUrl: URL(string: "https://example.com/tos"), privacyPolicyUrl: URL(string: "https://example.com/privacy"), emailLinkSignInActionCodeSettings: actionCodeSettings