From 699c134983bf5d07cadddf0504a117cb87454322 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 29 Apr 2025 13:31:55 +0100 Subject: [PATCH 01/11] chore: anonymous account upgrade --- .../Sources/Services/AuthService.swift | 20 +++++++++---------- .../FirebaseSwiftUIExampleApp.swift | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index c5ecdc1643..070710d211 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -169,19 +169,19 @@ public final class AuthService { public func signIn(credentials credentials: AuthCredential) async throws { authenticationState = .authenticating - if currentUser?.isAnonymous == true, configuration.shouldAutoUpgradeAnonymousUsers { - try await linkAccounts(credentials: credentials) - } else { - do { + do { + if currentUser?.isAnonymous == true, configuration.shouldAutoUpgradeAnonymousUsers { + try await linkAccounts(credentials: credentials) + } else { try await auth.signIn(with: credentials) updateAuthenticationState() - } catch { - authenticationState = .unauthenticated - errorMessage = string.localizedErrorMessage( - for: error - ) - throw error } + } catch { + authenticationState = .unauthenticated + errorMessage = string.localizedErrorMessage( + for: error + ) + throw error } } diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/FirebaseSwiftUIExampleApp.swift b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/FirebaseSwiftUIExampleApp.swift index c5f85d500f..da7ae917db 100644 --- a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/FirebaseSwiftUIExampleApp.swift +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/FirebaseSwiftUIExampleApp.swift @@ -78,7 +78,7 @@ struct ContentView: View { let authService: AuthService init() { - // Auth.auth().signInAnonymously() + Auth.auth().signInAnonymously() let actionCodeSettings = ActionCodeSettings() actionCodeSettings.handleCodeInApp = true From 52b92fb08b2d734f025e5c0595fb3492f78a7fdd Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 2 May 2025 14:25:45 +0100 Subject: [PATCH 02/11] implementation for handling errors when auto upgrading anonymous user --- .../Sources/AuthServiceError.swift | 14 ++++++++ .../Sources/Services/AuthService.swift | 36 ++++++++++++++++--- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift index 7302beeda7..31ee5b20f4 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift @@ -1,6 +1,17 @@ +import FirebaseAuth import SwiftUI +public struct AccountMergeConflictContext: LocalizedError { + public let credential: AuthCredential + public let underlyingError: Error + public let message: String + + public var errorDescription: String? { + return message + } +} + public enum AuthServiceError: LocalizedError { case invalidEmailLink case notConfiguredProvider(String) @@ -9,6 +20,7 @@ public enum AuthServiceError: LocalizedError { case reauthenticationRequired(String) case invalidCredentials(String) case signInFailed(underlying: Error) + case accountMergeConflict(context: AccountMergeConflictContext) public var errorDescription: String? { switch self { @@ -26,6 +38,8 @@ public enum AuthServiceError: LocalizedError { return description case let .signInFailed(underlying: error): return "Failed to sign in: \(error.localizedDescription)" + case let .accountMergeConflict(context): + return context.errorDescription } } } diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index 070710d211..019a3601fa 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -141,6 +141,10 @@ public final class AuthService { errorMessage = "" } + public var shouldHandleAnonymousUpgrade: Bool { + currentUser?.isAnonymous == true && configuration.shouldAutoUpgradeAnonymousUsers + } + public func signOut() async throws { do { try await auth.signOut() @@ -167,15 +171,31 @@ public final class AuthService { } } + public func handleAutoUpgradeAnonymousUser(credentials credentials: AuthCredential) async throws { + do { + try await currentUser?.link(with: credentials) + } catch let error as NSError { + if error.code == AuthErrorCode.emailAlreadyInUse.rawValue { + let context = AccountMergeConflictContext( + credential: credentials, + underlyingError: error, + message: "Unable to merge accounts. Use the credential in the context to resolve the conflict." + ) + throw AuthServiceError.accountMergeConflict(context: context) + } + throw error + } + } + public func signIn(credentials credentials: AuthCredential) async throws { authenticationState = .authenticating do { - if currentUser?.isAnonymous == true, configuration.shouldAutoUpgradeAnonymousUsers { - try await linkAccounts(credentials: credentials) + if shouldHandleAnonymousUpgrade { + try await handleAutoUpgradeAnonymousUser(credentials: credentials) } else { try await auth.signIn(with: credentials) - updateAuthenticationState() } + updateAuthenticationState() } catch { authenticationState = .unauthenticated errorMessage = string.localizedErrorMessage( @@ -231,7 +251,13 @@ public extension AuthService { authenticationState = .authenticating do { - try await auth.createUser(withEmail: email, password: password) + if shouldHandleAnonymousUpgrade { + // TODO: - check this works. This is how it is done in previous implementation, but I wonder if this would fail + let credential = EmailAuthProvider.credential(withEmail: email, password: password) + try await handleAutoUpgradeAnonymousUser(credentials: credential) + } else { + try await auth.createUser(withEmail: email, password: password) + } updateAuthenticationState() } catch { authenticationState = .unauthenticated @@ -278,6 +304,8 @@ public extension AuthService { throw AuthServiceError.invalidEmailLink } let link = url.absoluteString + // TODO: - get anonymous id here and check against current user before linking accounts + // put anonymous uid on link and get it back: https://github.com/firebase/FirebaseUI-iOS/blob/main/FirebaseEmailAuthUI/Sources/FUIEmailAuth.m#L822 if auth.isSignIn(withEmailLink: link) { let result = try await auth.signIn(withEmail: email, link: link) updateAuthenticationState() From 5614990402f30e3a530dfd83232f08e39b65b0be Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 6 May 2025 10:07:29 +0100 Subject: [PATCH 03/11] email link sign-in --- .../Sources/AuthServiceError.swift | 6 +-- .../Sources/Services/AuthService.swift | 49 +++++++++++++++++-- .../Sources/Utils/CommonUtils.swift | 8 +++ 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift index 31ee5b20f4..23daf6242b 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift @@ -16,7 +16,7 @@ public enum AuthServiceError: LocalizedError { case invalidEmailLink case notConfiguredProvider(String) case clientIdNotFound(String) - case notConfiguredActionCodeSettings + case notConfiguredActionCodeSettings(String) case reauthenticationRequired(String) case invalidCredentials(String) case signInFailed(underlying: Error) @@ -30,8 +30,8 @@ public enum AuthServiceError: LocalizedError { return description case let .clientIdNotFound(description): return description - case .notConfiguredActionCodeSettings: - return "ActionCodeSettings has not been configured for `AuthConfiguration.emailLinkSignInActionCodeSettings`" + case let .notConfiguredActionCodeSettings(description): + return description case let .reauthenticationRequired(description): return description case let .invalidCredentials(description): diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index 019a3601fa..623574b31e 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -124,7 +124,9 @@ public final class AuthService { guard let actionCodeSettings = configuration .emailLinkSignInActionCodeSettings else { throw AuthServiceError - .notConfiguredActionCodeSettings + .notConfiguredActionCodeSettings( + "ActionCodeSettings has not been configured for `AuthConfiguration.emailLinkSignInActionCodeSettings`" + ) } return actionCodeSettings } @@ -285,7 +287,7 @@ public extension AuthService { public extension AuthService { func sendEmailSignInLink(to email: String) async throws { do { - let actionCodeSettings = try safeActionCodeSettings() + let actionCodeSettings = try updateActionCodeSettings() try await auth.sendSignInLink( toEmail: email, actionCodeSettings: actionCodeSettings @@ -304,10 +306,15 @@ public extension AuthService { throw AuthServiceError.invalidEmailLink } let link = url.absoluteString - // TODO: - get anonymous id here and check against current user before linking accounts - // put anonymous uid on link and get it back: https://github.com/firebase/FirebaseUI-iOS/blob/main/FirebaseEmailAuthUI/Sources/FUIEmailAuth.m#L822 + if auth.isSignIn(withEmailLink: link) { - let result = try await auth.signIn(withEmail: email, link: link) + let anonymousUserID = CommonUtils.getQueryParamValue(from: link, paramName: "ui_auid") + if shouldHandleAnonymousUpgrade, anonymousUserID == currentUser?.uid { + let credential = EmailAuthProvider.credential(withEmail: email, link: link) + try await handleAutoUpgradeAnonymousUser(credentials: credential) + } else { + let result = try await auth.signIn(withEmail: email, link: link) + } updateAuthenticationState() emailLink = nil } @@ -318,6 +325,38 @@ public extension AuthService { throw error } } + + private func updateActionCodeSettings() throws -> ActionCodeSettings { + let actionCodeSettings = try safeActionCodeSettings() + guard var urlComponents = URLComponents(string: actionCodeSettings.url!.absoluteString) else { + throw AuthServiceError + .notConfiguredActionCodeSettings( + "ActionCodeSettings.url has not been configured for `AuthConfiguration.emailLinkSignInActionCodeSettings`" + ) + } + + var queryItems: [URLQueryItem] = [] + + if shouldHandleAnonymousUpgrade { + if let currentUser = currentUser { + let anonymousUID = currentUser.uid + let auidItem = URLQueryItem(name: "ui_auid", value: anonymousUID) + queryItems.append(auidItem) + } + } + + // We don't have config for forceSameDevice so it is set as default + let forceSameDevice = "1" + let sameDeviceItem = URLQueryItem(name: "ui_sd", value: forceSameDevice) + queryItems.append(sameDeviceItem) + + urlComponents.queryItems = queryItems + if let finalURL = urlComponents.url { + actionCodeSettings.url = finalURL + } + + return actionCodeSettings + } } // MARK: - Google Sign In diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift index 308b12bd77..c8bd78409f 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift @@ -46,4 +46,12 @@ public class CommonUtils { } return hash.map { String(format: "%02x", $0) }.joined() } + + public static func getQueryParamValue(from urlString: String, paramName: String) -> String? { + guard let urlComponents = URLComponents(string: urlString) else { + return nil + } + + return urlComponents.queryItems?.first(where: { $0.name == paramName })?.value + } } From 89db7b0afe382be7a7d242d440229ef84b0503aa Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 6 May 2025 10:33:10 +0100 Subject: [PATCH 04/11] checked - it does work --- .../FirebaseAuthSwiftUI/Sources/Services/AuthService.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index 623574b31e..ce2954e0fc 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -254,7 +254,6 @@ public extension AuthService { do { if shouldHandleAnonymousUpgrade { - // TODO: - check this works. This is how it is done in previous implementation, but I wonder if this would fail let credential = EmailAuthProvider.credential(withEmail: email, password: password) try await handleAutoUpgradeAnonymousUser(credentials: credential) } else { From 7d75ca2ec89130b07a1fa811d61ee6a0f36a68ba Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 6 May 2025 11:43:51 +0100 Subject: [PATCH 05/11] get anonymousId from continueUrl --- .../FirebaseAuthSwiftUI/Sources/Services/AuthService.swift | 3 ++- .../FirebaseAuthSwiftUI/Sources/Views/EmailAuthView.swift | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index ce2954e0fc..9f7846eb23 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -307,7 +307,8 @@ public extension AuthService { let link = url.absoluteString if auth.isSignIn(withEmailLink: link) { - let anonymousUserID = CommonUtils.getQueryParamValue(from: link, paramName: "ui_auid") + let anonymousUserID = CommonUtils.getQueryParamValue(from: link, paramName: "continueUrl") + .flatMap { CommonUtils.getQueryParamValue(from: $0, paramName: "ui_auid") } if shouldHandleAnonymousUpgrade, anonymousUserID == currentUser?.uid { let credential = EmailAuthProvider.credential(withEmail: email, link: link) try await handleAutoUpgradeAnonymousUser(credentials: credential) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailAuthView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailAuthView.swift index f09a043226..99b405fe25 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailAuthView.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailAuthView.swift @@ -125,7 +125,7 @@ extension EmailAuthView: View { .frame(maxWidth: .infinity) .buttonStyle(.borderedProminent) Button(action: { - authService.authView = .passwordRecovery + authService.authView = .emailLink }) { Text("Prefer Email link sign-in?") } From 89e3b6fab5bc756c37fd6a3f3ca4d29aca6f80ee Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Wed, 7 May 2025 08:22:35 +0100 Subject: [PATCH 06/11] create function for getting anonymous ID --- .../FirebaseAuthSwiftUI/Sources/Services/AuthService.swift | 3 +-- .../FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index 9f7846eb23..8c33c2e106 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -307,8 +307,7 @@ public extension AuthService { let link = url.absoluteString if auth.isSignIn(withEmailLink: link) { - let anonymousUserID = CommonUtils.getQueryParamValue(from: link, paramName: "continueUrl") - .flatMap { CommonUtils.getQueryParamValue(from: $0, paramName: "ui_auid") } + let anonymousUserID = CommonUtils.getAnonymousUserIdFromUrl(from: link) if shouldHandleAnonymousUpgrade, anonymousUserID == currentUser?.uid { let credential = EmailAuthProvider.credential(withEmail: email, link: link) try await handleAutoUpgradeAnonymousUser(credentials: credential) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift index c8bd78409f..18e6daca6c 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift @@ -54,4 +54,9 @@ public class CommonUtils { return urlComponents.queryItems?.first(where: { $0.name == paramName })?.value } + + public static func getAnonymousUserIdFromUrl(from urlString: String) -> String? { + getQueryParamValue(from: urlString, paramName: "continueUrl") + .flatMap { getQueryParamValue(from: $0, paramName: "ui_auid") } + } } From 32d82c23d32089225dd26a61bac5bbc08f3c8af0 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Wed, 7 May 2025 08:35:34 +0100 Subject: [PATCH 07/11] chore: check continurUrl is present before continuing --- .../Sources/AuthServiceError.swift | 6 +++--- .../Sources/Services/AuthService.swift | 12 ++++++++++-- .../Sources/Utils/CommonUtils.swift | 5 ----- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift index 23daf6242b..e020420288 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift @@ -13,7 +13,7 @@ public struct AccountMergeConflictContext: LocalizedError { } public enum AuthServiceError: LocalizedError { - case invalidEmailLink + case invalidEmailLink(String) case notConfiguredProvider(String) case clientIdNotFound(String) case notConfiguredActionCodeSettings(String) @@ -24,8 +24,8 @@ public enum AuthServiceError: LocalizedError { public var errorDescription: String? { switch self { - case .invalidEmailLink: - return "Invalid sign in link. Most likely, the link you used has expired. Try signing in again." + case let .invalidEmailLink(description): + return description case let .notConfiguredProvider(description): return description case let .clientIdNotFound(description): diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index 8c33c2e106..e2f1de41fd 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -302,12 +302,20 @@ public extension AuthService { func handleSignInLink(url url: URL) async throws { do { guard let email = emailLink else { - throw AuthServiceError.invalidEmailLink + throw AuthServiceError.invalidEmailLink("email address is missing from local storage") } let link = url.absoluteString + guard let continueUrl = CommonUtils.getQueryParamValue(from: link, paramName: "continueUrl") + else { + throw AuthServiceError + .invalidEmailLink("`continueUrl` parameter is missing from the email link URL") + } if auth.isSignIn(withEmailLink: link) { - let anonymousUserID = CommonUtils.getAnonymousUserIdFromUrl(from: link) + let anonymousUserID = CommonUtils.getQueryParamValue( + from: continueUrl, + paramName: "ui_auid" + ) if shouldHandleAnonymousUpgrade, anonymousUserID == currentUser?.uid { let credential = EmailAuthProvider.credential(withEmail: email, link: link) try await handleAutoUpgradeAnonymousUser(credentials: credential) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift index 18e6daca6c..c8bd78409f 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/CommonUtils.swift @@ -54,9 +54,4 @@ public class CommonUtils { return urlComponents.queryItems?.first(where: { $0.name == paramName })?.value } - - public static func getAnonymousUserIdFromUrl(from urlString: String) -> String? { - getQueryParamValue(from: urlString, paramName: "continueUrl") - .flatMap { getQueryParamValue(from: $0, paramName: "ui_auid") } - } } From 3f796c6cb8cee2726a4a660aac13d204bb6ddba9 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Wed, 7 May 2025 08:38:00 +0100 Subject: [PATCH 08/11] chore: update message --- .../FirebaseAuthSwiftUI/Sources/Services/AuthService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index e2f1de41fd..82227370fa 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -302,7 +302,7 @@ public extension AuthService { func handleSignInLink(url url: URL) async throws { do { guard let email = emailLink else { - throw AuthServiceError.invalidEmailLink("email address is missing from local storage") + throw AuthServiceError.invalidEmailLink("email address is missing from app storage. Is this the same device?") } let link = url.absoluteString guard let continueUrl = CommonUtils.getQueryParamValue(from: link, paramName: "continueUrl") From fbd0d4f939c5fce21f5a4e5d6e2101fd6105ed92 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Wed, 7 May 2025 09:19:26 +0100 Subject: [PATCH 09/11] removed unused logic for force same device for now --- .../Sources/Services/AuthService.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index 82227370fa..fb90733c1b 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -302,7 +302,8 @@ public extension AuthService { func handleSignInLink(url url: URL) async throws { do { guard let email = emailLink else { - throw AuthServiceError.invalidEmailLink("email address is missing from app storage. Is this the same device?") + throw AuthServiceError + .invalidEmailLink("email address is missing from app storage. Is this the same device?") } let link = url.absoluteString guard let continueUrl = CommonUtils.getQueryParamValue(from: link, paramName: "continueUrl") @@ -352,11 +353,6 @@ public extension AuthService { } } - // We don't have config for forceSameDevice so it is set as default - let forceSameDevice = "1" - let sameDeviceItem = URLQueryItem(name: "ui_sd", value: forceSameDevice) - queryItems.append(sameDeviceItem) - urlComponents.queryItems = queryItems if let finalURL = urlComponents.url { actionCodeSettings.url = finalURL From b26351cc9364d7ca230a2c582076b014f4202a6d Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Thu, 8 May 2025 09:44:32 +0100 Subject: [PATCH 10/11] throw exception if there is no currentUser. rm duplicate arg labels --- .../FirebaseAuthSwiftUI/Sources/AuthServiceError.swift | 3 +++ .../FirebaseAuthSwiftUI/Sources/Services/AuthService.swift | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift index e020420288..052e1e07c9 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift @@ -13,6 +13,7 @@ public struct AccountMergeConflictContext: LocalizedError { } public enum AuthServiceError: LocalizedError { + case noCurrentUser case invalidEmailLink(String) case notConfiguredProvider(String) case clientIdNotFound(String) @@ -24,6 +25,8 @@ public enum AuthServiceError: LocalizedError { public var errorDescription: String? { switch self { + case .noCurrentUser: + return "No user is currently signed in." case let .invalidEmailLink(description): return description case let .notConfiguredProvider(description): diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index 24e93a556b..696964ad99 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -180,7 +180,10 @@ public final class AuthService { } } - public func handleAutoUpgradeAnonymousUser(credentials credentials: AuthCredential) async throws { + public func handleAutoUpgradeAnonymousUser(credentials: AuthCredential) async throws { + if currentUser == nil { + throw AuthServiceError.noCurrentUser + } do { try await currentUser?.link(with: credentials) } catch let error as NSError { @@ -196,7 +199,7 @@ public final class AuthService { } } - public func signIn(credentials credentials: AuthCredential) async throws { + public func signIn(credentials: AuthCredential) async throws { authenticationState = .authenticating do { if shouldHandleAnonymousUpgrade { From a014c7a548f11031ee086877c1e37f9e43790d2d Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Wed, 14 May 2025 10:58:40 +0100 Subject: [PATCH 11/11] chore: pass in uid with note to upgrade to User when possible --- .../FirebaseAuthSwiftUI/Sources/AuthServiceError.swift | 2 ++ .../FirebaseAuthSwiftUI/Sources/Services/AuthService.swift | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift index 052e1e07c9..58e72fde12 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/AuthServiceError.swift @@ -6,6 +6,8 @@ public struct AccountMergeConflictContext: LocalizedError { public let credential: AuthCredential public let underlyingError: Error public let message: String + // TODO: - should make this User type once fixed upstream in firebase-ios-sdk. See: https://github.com/firebase/FirebaseUI-iOS/pull/1247#discussion_r2085455355 + public let uid: String? public var errorDescription: String? { return message diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index 696964ad99..ac9051783b 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -191,7 +191,8 @@ public final class AuthService { let context = AccountMergeConflictContext( credential: credentials, underlyingError: error, - message: "Unable to merge accounts. Use the credential in the context to resolve the conflict." + message: "Unable to merge accounts. Use the credential in the context to resolve the conflict.", + uid: currentUser?.uid ) throw AuthServiceError.accountMergeConflict(context: context) }