diff --git a/auth/src/android/auth_android.cc b/auth/src/android/auth_android.cc index e0a9a669cb..9c38f43cbc 100644 --- a/auth/src/android/auth_android.cc +++ b/auth/src/android/auth_android.cc @@ -670,6 +670,11 @@ void Auth::UseEmulator(std::string host, uint32_t port) { SetEmulatorJni(auth_data_, host.c_str(), port); } +AuthError Auth::UseUserAccessGroup(const char* access_group) { + // Not implemented on Android. + return kAuthErrorUnimplemented; +} + // Not implemented for Android. void EnableTokenAutoRefresh(AuthData* auth_data) {} void DisableTokenAutoRefresh(AuthData* auth_data) {} diff --git a/auth/src/desktop/auth_desktop.cc b/auth/src/desktop/auth_desktop.cc index dc9aca2950..c4f24c4667 100644 --- a/auth/src/desktop/auth_desktop.cc +++ b/auth/src/desktop/auth_desktop.cc @@ -575,6 +575,11 @@ void Auth::UseEmulator(std::string host, uint32_t port) { auth_impl->assigned_emulator_url.append(std::to_string(port)); } +AuthError Auth::UseUserAccessGroup(const char* access_group) { + // Not implemented on desktop. + return kAuthErrorUnimplemented; +} + void InitializeTokenRefresher(AuthData* auth_data) { auto auth_impl = static_cast(auth_data->auth_impl); auth_impl->token_refresh_thread.Initialize(auth_data); diff --git a/auth/src/include/firebase/auth.h b/auth/src/include/firebase/auth.h index f6809c4a57..fddb2e1f53 100644 --- a/auth/src/include/firebase/auth.h +++ b/auth/src/include/firebase/auth.h @@ -173,6 +173,28 @@ class Auth { /// set_language_code(). void UseAppLanguage(); + /// @brief Uses the specified user access group for keychain operations on + /// iOS. + /// + /// This method should be called before any other Firebase Auth operations that + /// might interact with the keychain, such as sign-in or sign-out. + /// + /// On iOS, this method corresponds to `[FIRAuth useUserAccessGroup:]`. + /// If a value is provided, it will be used to set the user's access group, + /// which will be used to share credentials across apps from the same + /// developer. If `nullptr` is provided, it will clear any previously set + /// access group. + /// + /// On other platforms (Android, desktop), this method is a no-op and will + /// always return `kAuthErrorNone`. + /// + /// @param[in] access_group The access group to use, or `nullptr` to clear + /// the access group. + /// + /// @return `kAuthErrorNone` on success, or an `AuthError` code if an error + /// occurred on iOS (e.g., keychain error). + AuthError UseUserAccessGroup(const char* access_group); + // ----- Providers ------------------------------------------------------- /// Asynchronously requests the IDPs (identity providers) that can be used /// for the given email address. diff --git a/auth/src/ios/auth_ios.mm b/auth/src/ios/auth_ios.mm index a0292ba3b8..67c7221e8f 100644 --- a/auth/src/ios/auth_ios.mm +++ b/auth/src/ios/auth_ios.mm @@ -590,6 +590,26 @@ void SignInCallback(FIRUser *_Nullable user, NSError *_Nullable error, SetEmulatorJni(auth_data_, host.c_str(), port); } +AuthError Auth::UseUserAccessGroup(const char* access_group) { + if (!auth_data_) { + return kAuthErrorUninitialized; + } + FIRAuth* fir_auth = AuthImpl(auth_data_); + NSString* ns_access_group = nil; + if (access_group && strlen(access_group) > 0) { + ns_access_group = [NSString stringWithUTF8String:access_group]; + } + + NSError* ns_error = nil; + BOOL success = [fir_auth useUserAccessGroup:ns_access_group error:&ns_error]; + + if (success) { + return kAuthErrorNone; + } else { + return AuthErrorFromNSError(ns_error); + } +} + // Remap iOS SDK errors reported by the UIDelegate. While these errors seem like // user interaction errors, they are actually caused by bad provider ids. NSError *RemapBadProviderIDErrors(NSError *_Nonnull error) {