diff --git a/auth/integration_test/src/integration_test.cc b/auth/integration_test/src/integration_test.cc index 3727bcab9d..0de7405094 100644 --- a/auth/integration_test/src/integration_test.cc +++ b/auth/integration_test/src/integration_test.cc @@ -99,7 +99,7 @@ class FirebaseAuthTest : public FirebaseTest { void SignOut(); // Delete the current user if it's currently signed in. - void DeleteUser(); + void DeleteUser(firebase::auth::User* user = nullptr); // Passthrough method to the base class's WaitForCompletion. bool WaitForCompletion(firebase::Future future, const char* fn, @@ -261,11 +261,15 @@ void FirebaseAuthTest::SignOut() { EXPECT_EQ(auth_->current_user_DEPRECATED(), nullptr); } -void FirebaseAuthTest::DeleteUser() { - if (auth_ != nullptr && auth_->current_user_DEPRECATED() != nullptr) { - FirebaseTest::WaitForCompletion(auth_->current_user_DEPRECATED()->Delete(), - "Delete User"); - ProcessEvents(100); +void FirebaseAuthTest::DeleteUser(firebase::auth::User* user) { + if (auth_ != nullptr) { + if (user == nullptr) { + user = auth_->current_user_DEPRECATED(); + } + if (user != nullptr) { + FirebaseTest::WaitForCompletion(user->Delete(), "Delete User"); + ProcessEvents(100); + } } } @@ -454,6 +458,37 @@ TEST_F(FirebaseAuthTest, TestEmailAndPasswordSignin) { EXPECT_EQ(auth_->current_user_DEPRECATED(), nullptr); } +TEST_F(FirebaseAuthTest, TestRetainUser) { + WaitForCompletion(auth_->SignInAnonymously_DEPRECATED(), "SignInAnonymously"); + ASSERT_NE(auth_->current_user_DEPRECATED(), nullptr); + firebase::auth::User anonymous_user = *auth_->current_user_DEPRECATED(); + EXPECT_EQ(anonymous_user.is_anonymous(), true); + EXPECT_EQ(anonymous_user.email().size(), 0); + + SignOut(); + + std::string email = GenerateEmailAddress(); + // Register a random email and password. This signs us in as that user. + std::string password = kTestPassword; + firebase::Future create_user_future = + auth_->CreateUserWithEmailAndPassword_DEPRECATED(email.c_str(), + password.c_str()); + WaitForCompletion(create_user_future, + "CreateUserWithEmailAndPassword_DEPRECATED"); + EXPECT_NE(auth_->current_user_DEPRECATED(), nullptr); + firebase::auth::User* email_user = auth_->current_user_DEPRECATED(); + + // Ensure the users are distinct objects. + EXPECT_EQ(anonymous_user.is_anonymous(), true); + EXPECT_EQ(email_user->is_anonymous(), false); + + EXPECT_EQ(anonymous_user.email().size(), 0); + EXPECT_NE(email_user->email().size(), 0); + + DeleteUser(); + DeleteUser(&anonymous_user); +} + TEST_F(FirebaseAuthTest, TestUpdateUserProfile) { std::string email = GenerateEmailAddress(); firebase::Future create_user = diff --git a/auth/src/android/auth_android.cc b/auth/src/android/auth_android.cc index 4691b3ada6..a5a1614575 100644 --- a/auth/src/android/auth_android.cc +++ b/auth/src/android/auth_android.cc @@ -114,8 +114,6 @@ METHOD_LOOKUP_DEFINITION( JNI_ID_TOKEN_LISTENER_CALLBACK_METHODS) static int g_initialized_count = 0; -static const char* kErrorEmptyEmailPassword = - "Empty email or password are not allowed."; JNIEXPORT void JNICALL JniAuthStateListener_nativeOnAuthStateChanged( JNIEnv* env, jobject clazz, jlong callback_data); @@ -162,27 +160,20 @@ void ReleaseAuthClasses(JNIEnv* env) { jni_id_token_listener::ReleaseClass(env); } -void UpdateCurrentUser(AuthData* auth_data) { - JNIEnv* env = Env(auth_data); - +// Grab the user value from the Android SDK and remember it locally. +void UpdateCurrentUser(JNIEnv* env, AuthData* auth_data) { MutexLock lock(auth_data->future_impl.mutex()); - - const void* original_user_impl = auth_data->user_impl; - // Update our pointer to the Android FirebaseUser that we're wrapping. jobject j_user = env->CallObjectMethod( AuthImpl(auth_data), auth::GetMethodId(auth::kGetCurrentUser)); if (firebase::util::CheckAndClearJniExceptions(env)) { j_user = nullptr; } - SetImplFromLocalRef(env, j_user, &auth_data->user_impl); - - // Log debug message when user sign-in status has changed. - if (original_user_impl != auth_data->user_impl) { - LogDebug("CurrentUser changed from %X to %X", - reinterpret_cast(original_user_impl), - reinterpret_cast(auth_data->user_impl)); - } + SetImplFromLocalRef(env, j_user, + &auth_data->deprecated_fields.android_user_impl); + SetUserImpl( + env, auth_data, + static_cast(auth_data->deprecated_fields.android_user_impl)); } // Release cached Java classes. @@ -237,6 +228,15 @@ void* CreatePlatformAuth(App* app) { void Auth::InitPlatformAuth(AuthData* auth_data) { JNIEnv* env = Env(auth_data); + // Create persistent User data to continue to facilitate deprecated aysnc + // methods which return a pointer to a User. Remove this structure when those + // deprecated methods are removed. + auth_data->deprecated_fields.android_user_impl = (jobject) nullptr; + auth_data->deprecated_fields.user_internal_deprecated = + new UserInternal(auth_data, (jobject) nullptr); + auth_data->deprecated_fields.user_deprecated = new User( + auth_data, auth_data->deprecated_fields.user_internal_deprecated); + // Create the JniAuthStateListener class to redirect the state-change // from Java to C++. jobject j_listener = @@ -268,10 +268,12 @@ void Auth::InitPlatformAuth(AuthData* auth_data) { // Ensure our User is in-line with underlying API's user. // It's possible for a user to already be logged-in on start-up. - UpdateCurrentUser(auth_data); + UpdateCurrentUser(env, auth_data); } void Auth::DestroyPlatformAuth(AuthData* auth_data) { + // Note: auth_data->auth_mutex is already locked by Auth::DeleteInternal(). + // Remove references from listener blocks. JNIEnv* env = Env(auth_data); util::CancelCallbacks(env, auth_data->future_api_id.c_str()); @@ -294,11 +296,23 @@ void Auth::DestroyPlatformAuth(AuthData* auth_data) { static_cast(auth_data->id_token_listener_impl)); assert(env->ExceptionCheck() == false); - // Deleting our global references should trigger the FirebaseAuth class and - // FirebaseUser Java objects to be deleted. + // Clear the retained User object, which is used to support those deprecated + // Auth methods which return User pointer. + SetImplFromLocalRef(env, nullptr, + &auth_data->deprecated_fields.android_user_impl); + SetUserImpl(env, auth_data, nullptr); + + auth_data->deprecated_fields.user_internal_deprecated = nullptr; + + // This also deletes auth_data->deprecated_fields.user_internal_deprecated + // since User has ownership of the UserInternal allocation. + delete auth_data->deprecated_fields.user_deprecated; + auth_data->deprecated_fields.user_deprecated = nullptr; + + // Deleting our global references should trigger the FirebaseAuth class to be + // deleted. SetImplFromLocalRef(env, nullptr, &auth_data->listener_impl); SetImplFromLocalRef(env, nullptr, &auth_data->id_token_listener_impl); - SetImplFromLocalRef(env, nullptr, &auth_data->user_impl); SetImplFromLocalRef(env, nullptr, &auth_data->auth_impl); FIREBASE_ASSERT(g_initialized_count); @@ -324,7 +338,7 @@ JNIEXPORT void JNICALL JniAuthStateListener_nativeOnAuthStateChanged( JNIEnv* env, jobject clazz, jlong callback_data) { AuthData* auth_data = reinterpret_cast(callback_data); // Update our pointer to the Android FirebaseUser that we're wrapping. - UpdateCurrentUser(auth_data); + UpdateCurrentUser(env, auth_data); NotifyAuthStateListeners(auth_data); } @@ -333,7 +347,7 @@ JNIEXPORT void JNICALL JniIdTokenListener_nativeOnIdTokenChanged( AuthData* auth_data = reinterpret_cast(callback_data); auth_data->SetExpectIdTokenListenerCallback(false); // Update our pointer to the Android FirebaseUser that we're wrapping. - UpdateCurrentUser(auth_data); + UpdateCurrentUser(env, auth_data); NotifyIdTokenListeners(auth_data); } @@ -375,6 +389,8 @@ static void ReadProviderResult( Future Auth::FetchProvidersForEmail( const char* email) { + FIREBASE_ASSERT_RETURN(Future(), auth_data_); + MutexLock(auth_data_->auth_mutex); ReferenceCountedFutureImpl& futures = auth_data_->future_impl; const auto handle = futures.SafeAlloc( kAuthFn_FetchProvidersForEmail); @@ -387,34 +403,42 @@ Future Auth::FetchProvidersForEmail( env->DeleteLocalRef(j_email); if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, ReadProviderResult); + RegisterCallback(auth_data_, pending_result, handle, + auth_data_->future_api_id, &futures, ReadProviderResult); env->DeleteLocalRef(pending_result); } return MakeFuture(&futures, handle); } Future Auth::SignInWithCustomToken_DEPRECATED(const char* token) { + FIREBASE_ASSERT_RETURN(Future(), auth_data_); + MutexLock(auth_data_->auth_mutex); ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = - futures.SafeAlloc(kAuthFn_SignInWithCustomToken_DEPRECATED); - JNIEnv* env = Env(auth_data_); + SafeFutureHandle future_handle = + auth_data_->future_impl.SafeAlloc( + kAuthFn_SignInWithCustomToken_DEPRECATED); + Future future = MakeFuture(&auth_data_->future_impl, future_handle); + JNIEnv* env = Env(auth_data_); jstring j_token = env->NewStringUTF(token); jobject pending_result = env->CallObjectMethod( AuthImpl(auth_data_), auth::GetMethodId(auth::kSignInWithCustomToken), j_token); env->DeleteLocalRef(j_token); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, + if (!CheckAndCompleteFutureOnError(env, &futures, future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, + auth_data_->future_api_id, &auth_data_->future_impl, ReadUserFromSignInResult); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return future; } Future Auth::SignInWithCredential_DEPRECATED( const Credential& credential) { + FIREBASE_ASSERT_RETURN(Future(), auth_data_); + MutexLock(auth_data_->auth_mutex); ReferenceCountedFutureImpl& futures = auth_data_->future_impl; const auto handle = futures.SafeAlloc(kAuthFn_SignInWithCredential_DEPRECATED); @@ -430,7 +454,8 @@ Future Auth::SignInWithCredential_DEPRECATED( CredentialFromImpl(credential.impl_)); if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, + RegisterCallback(auth_data_, pending_result, handle, + auth_data_->future_api_id, &futures, ReadUserFromSignInResult); env->DeleteLocalRef(pending_result); } @@ -440,54 +465,63 @@ Future Auth::SignInWithCredential_DEPRECATED( Future Auth::SignInAndRetrieveDataWithCredential_DEPRECATED( const Credential& credential) { - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc( + FIREBASE_ASSERT_RETURN(Future(), auth_data_); + MutexLock(auth_data_->auth_mutex); + const auto future_handle = auth_data_->future_impl.SafeAlloc( kAuthFn_SignInAndRetrieveDataWithCredential_DEPRECATED); JNIEnv* env = Env(auth_data_); // If the credential itself is in an error state, don't try signing in. if (credential.error_code_ != kAuthErrorNone) { - futures.Complete(handle, credential.error_code_, - credential.error_message_.c_str()); + auth_data_->future_impl.Complete(future_handle, credential.error_code_, + credential.error_message_.c_str()); } else { jobject pending_result = env->CallObjectMethod( AuthImpl(auth_data_), auth::GetMethodId(auth::kSignInWithCredential), CredentialFromImpl(credential.impl_)); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, ReadSignInResult); + if (!CheckAndCompleteFutureOnError(env, &auth_data_->future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, + auth_data_->future_api_id, &auth_data_->future_impl, + ReadSignInResult); env->DeleteLocalRef(pending_result); } } - return MakeFuture(&futures, handle); + return MakeFuture(&auth_data_->future_impl, future_handle); } Future Auth::SignInWithProvider_DEPRECATED( FederatedAuthProvider* provider) { + FIREBASE_ASSERT_RETURN(Future(), auth_data_); FIREBASE_ASSERT_RETURN(Future(), provider); + MutexLock(auth_data_->auth_mutex); return provider->SignIn(auth_data_); } Future Auth::SignInAnonymously_DEPRECATED() { - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = - futures.SafeAlloc(kAuthFn_SignInAnonymously_DEPRECATED); + const auto handle = auth_data_->future_impl.SafeAlloc( + kAuthFn_SignInAnonymously_DEPRECATED); + FIREBASE_ASSERT_RETURN(Future(), auth_data_); + MutexLock(auth_data_->auth_mutex); JNIEnv* env = Env(auth_data_); - jobject pending_result = env->CallObjectMethod( AuthImpl(auth_data_), auth::GetMethodId(auth::kSignInAnonymously)); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, + if (!CheckAndCompleteFutureOnError(env, &auth_data_->future_impl, handle)) { + RegisterCallback(auth_data_, pending_result, handle, + auth_data_->future_api_id, &auth_data_->future_impl, ReadUserFromSignInResult); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return MakeFuture(&auth_data_->future_impl, handle); } Future Auth::SignInWithEmailAndPassword_DEPRECATED( const char* email, const char* password) { + FIREBASE_ASSERT_RETURN(Future(), auth_data_); + MutexLock(auth_data_->auth_mutex); ReferenceCountedFutureImpl& futures = auth_data_->future_impl; const auto handle = futures.SafeAlloc(kAuthFn_SignInWithEmailAndPassword_DEPRECATED); @@ -496,7 +530,7 @@ Future Auth::SignInWithEmailAndPassword_DEPRECATED( futures.Complete(handle, (!email || strlen(email) == 0) ? kAuthErrorMissingEmail : kAuthErrorMissingPassword, - kErrorEmptyEmailPassword); + kErrorEmptyEmailPasswordErrorMessage); return MakeFuture(&futures, handle); } JNIEnv* env = Env(auth_data_); @@ -511,7 +545,8 @@ Future Auth::SignInWithEmailAndPassword_DEPRECATED( env->DeleteLocalRef(j_password); if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, + RegisterCallback(auth_data_, pending_result, handle, + auth_data_->future_api_id, &futures, ReadUserFromSignInResult); env->DeleteLocalRef(pending_result); } @@ -521,16 +556,18 @@ Future Auth::SignInWithEmailAndPassword_DEPRECATED( Future Auth::CreateUserWithEmailAndPassword_DEPRECATED( const char* email, const char* password) { - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc( + FIREBASE_ASSERT_RETURN(Future(), auth_data_); + MutexLock(auth_data_->auth_mutex); + const auto future_handle = auth_data_->future_impl.SafeAlloc( kAuthFn_CreateUserWithEmailAndPassword_DEPRECATED); if (!email || strlen(email) == 0 || !password || strlen(password) == 0) { - futures.Complete(handle, - (!email || strlen(email) == 0) ? kAuthErrorMissingEmail - : kAuthErrorMissingPassword, - kErrorEmptyEmailPassword); - return MakeFuture(&futures, handle); + auth_data_->future_impl.Complete(future_handle, + (!email || strlen(email) == 0) + ? kAuthErrorMissingEmail + : kAuthErrorMissingPassword, + kErrorEmptyEmailPasswordErrorMessage); + return MakeFuture(&auth_data_->future_impl, future_handle); } JNIEnv* env = Env(auth_data_); @@ -543,34 +580,33 @@ Future Auth::CreateUserWithEmailAndPassword_DEPRECATED( env->DeleteLocalRef(j_email); env->DeleteLocalRef(j_password); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, + if (!CheckAndCompleteFutureOnError(env, &auth_data_->future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, + auth_data_->future_api_id, &auth_data_->future_impl, ReadUserFromSignInResult); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return MakeFuture(&auth_data_->future_impl, future_handle); } // It's safe to return a direct pointer to `current_user` because that class // holds nothing but a pointer to AuthData, which never changes. // All User functions that require synchronization go through AuthData's mutex. User* Auth::current_user_DEPRECATED() { - if (!auth_data_) return nullptr; - MutexLock lock(auth_data_->future_impl.mutex()); - - // auth_data_->current_user should be available after Auth is created because - // persistent is loaded during the constructor of Android FirebaseAuth. - // This may change to make FirebaseAuth.getCurrentUser() to block and wait for - // persistent loading. However, it is safe to access auth_data_->current_user - // here since FirebaseAuth.getCurrentUser() (Android) is called in - // InitPlatformAuth(). - User* user = - auth_data_->user_impl == nullptr ? nullptr : &auth_data_->current_user; - return user; + FIREBASE_ASSERT_RETURN(nullptr, auth_data_); + MutexLock lock(auth_data_->auth_mutex); + if (auth_data_->deprecated_fields.user_deprecated == nullptr || + !auth_data_->deprecated_fields.user_deprecated->is_valid()) { + return nullptr; + } else { + return auth_data_->deprecated_fields.user_deprecated; + } } std::string Auth::language_code() const { - if (!auth_data_) return std::string(); + FIREBASE_ASSERT_RETURN(std::string(), auth_data_); + MutexLock(auth_data_->auth_mutex); JNIEnv* env = Env(auth_data_); jobject j_pending_result = env->CallObjectMethod( AuthImpl(auth_data_), auth::GetMethodId(auth::kGetLanguageCode)); @@ -582,7 +618,8 @@ std::string Auth::language_code() const { } void Auth::set_language_code(const char* language_code) { - if (!auth_data_) return; + FIREBASE_ASSERT_RETURN_VOID(auth_data_); + MutexLock(auth_data_->auth_mutex); JNIEnv* env = Env(auth_data_); jstring j_language_code = nullptr; if (language_code != nullptr) { @@ -598,7 +635,7 @@ void Auth::set_language_code(const char* language_code) { } void Auth::UseAppLanguage() { - if (!auth_data_) return; + FIREBASE_ASSERT_RETURN_VOID(auth_data_); JNIEnv* env = Env(auth_data_); env->CallVoidMethod(AuthImpl(auth_data_), auth::GetMethodId(auth::kUseAppLanguage)); @@ -606,16 +643,22 @@ void Auth::UseAppLanguage() { } void Auth::SignOut() { + FIREBASE_ASSERT_RETURN_VOID(auth_data_); + MutexLock(auth_data_->auth_mutex); JNIEnv* env = Env(auth_data_); env->CallVoidMethod(AuthImpl(auth_data_), auth::GetMethodId(auth::kSignOut)); firebase::util::CheckAndClearJniExceptions(env); // Release our current user implementation in Java. MutexLock lock(auth_data_->future_impl.mutex()); - SetImplFromLocalRef(env, nullptr, &auth_data_->user_impl); + SetImplFromLocalRef(env, nullptr, + &auth_data_->deprecated_fields.android_user_impl); + SetUserImpl(env, auth_data_, nullptr); } Future Auth::SendPasswordResetEmail(const char* email) { + FIREBASE_ASSERT_RETURN(Future(), auth_data_); + MutexLock(auth_data_->auth_mutex); ReferenceCountedFutureImpl& futures = auth_data_->future_impl; const auto handle = futures.SafeAlloc(kAuthFn_SendPasswordResetEmail); @@ -631,7 +674,8 @@ Future Auth::SendPasswordResetEmail(const char* email) { env->DeleteLocalRef(j_email); if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, nullptr); + RegisterCallback(auth_data_, pending_result, handle, + auth_data_->future_api_id, &futures, nullptr); env->DeleteLocalRef(pending_result); } return MakeFuture(&futures, handle); diff --git a/auth/src/android/common_android.cc b/auth/src/android/common_android.cc index f1f2587cea..f0dea43cf8 100644 --- a/auth/src/android/common_android.cc +++ b/auth/src/android/common_android.cc @@ -483,9 +483,13 @@ void ReadSignInResult(jobject result, FutureCallbackData* d, util::CheckAndClearJniExceptions(env); // Update our pointer to the Android FirebaseUser that we're wrapping. - // Note: Cannot call UpdateCurrentUser(d->auth_data) because the Java + // Note: Cannot call UpdateCurrentUser(env, d->auth_data) because the Java // Auth class has not been updated at this point. - SetImplFromLocalRef(env, j_user, &d->auth_data->user_impl); + SetImplFromLocalRef(env, j_user, + &d->auth_data->deprecated_fields.android_user_impl); + SetUserImpl(env, d->auth_data, + static_cast( + d->auth_data->deprecated_fields.android_user_impl)); // Grab the additional user info too. // Additional user info is not guaranteed to exist, so could be nullptr. @@ -519,9 +523,13 @@ void ReadUserFromSignInResult(jobject result, FutureCallbackData* d, util::CheckAndClearJniExceptions(env); // Update our pointer to the Android FirebaseUser that we're wrapping. - // Note: Cannot call UpdateCurrentUser(d->auth_data) because the Java + // Note: Cannot call UpdateCurrentUser(env, d->auth_data) because the Java // Auth class has not been updated at this point. - SetImplFromLocalRef(env, j_user, &d->auth_data->user_impl); + SetImplFromLocalRef(env, j_user, + &d->auth_data->deprecated_fields.android_user_impl); + SetUserImpl(env, d->auth_data, + static_cast( + d->auth_data->deprecated_fields.android_user_impl)); } // Return a pointer to the current user, if the current user is valid. diff --git a/auth/src/android/common_android.h b/auth/src/android/common_android.h index 6cbe4ac9c0..a54ba0d586 100644 --- a/auth/src/android/common_android.h +++ b/auth/src/android/common_android.h @@ -50,20 +50,136 @@ template struct FutureCallbackData { // During the callback, read `result` data from Java into the returned // C++ data in `d->future_data->Data()`. - typedef void ReadFutureResultFn(jobject result, FutureCallbackData* d, + typedef void ReadFutureResultFn(jobject result, + FutureCallbackData* future_callback_Data, bool success, void* void_data); - FutureCallbackData(const SafeFutureHandle& handle, AuthData* auth_data, + FutureCallbackData(AuthData* auth_data, const SafeFutureHandle& handle, + ReferenceCountedFutureImpl* future_impl, ReadFutureResultFn* future_data_read_fn) - : handle(handle), - auth_data(auth_data), + : auth_data(auth_data), + handle(handle), + future_impl(future_impl), future_data_read_fn(future_data_read_fn) {} - - SafeFutureHandle handle; AuthData* auth_data; + SafeFutureHandle handle; + ReferenceCountedFutureImpl* future_impl; ReadFutureResultFn* future_data_read_fn; }; +// Contains the interface between the public API and the underlying +// Android Java SDK FirebaseUser implemention. +class UserInternal { + public: + // Constructor + explicit UserInternal(AuthData* auth_data, jobject android_user); + + // Copy constructor. + UserInternal(const UserInternal& user_internal); + + ~UserInternal(); + + // @deprecated + // + // Provides a mechanism for the deprecated auth-contained user object to + // update its underlying Android Java SDK FirebaseUser object. Assumes that + // a global ref has already been set on android_user. Releases the global + // ref on any previously held user. + void set_native_user_object_deprecated(jobject android_user); + + bool is_valid() const; + + Future GetToken(bool force_refresh); + Future GetTokenLastResult() const; + + Future UpdateEmail(const char* email); + Future UpdateEmailLastResult() const; + + std::vector provider_data() const; + const std::vector& provider_data_DEPRECATED(); + + Future UpdatePassword(const char* password); + Future UpdatePasswordLastResult() const; + + Future UpdateUserProfile(const User::UserProfile& profile); + Future UpdateUserProfileLastResult() const; + + Future SendEmailVerification(); + Future SendEmailVerificationLastResult() const; + + Future LinkWithCredential_DEPRECATED(const Credential& credential); + Future LinkWithCredentialLastResult_DEPRECATED() const; + + Future LinkAndRetrieveDataWithCredential_DEPRECATED( + const Credential& credential); + Future LinkAndRetrieveDataWithCredentialLastResult_DEPRECATED() + const; + + Future LinkWithProvider_DEPRECATED( + FederatedAuthProvider* provider); + Future LinkWithProviderLastResult_DEPRECATED() const; + + Future Unlink_DEPRECATED(const char* provider); + Future UnlinkLastResult_DEPRECATED() const; + + Future UpdatePhoneNumberCredential_DEPRECATED( + const Credential& credential); + Future UpdatePhoneNumberCredentialLastResult_DEPRECATED() const; + + Future Reload(); + Future ReloadLastResult() const; + + Future Reauthenticate(const Credential& credential); + Future ReauthenticateLastResult() const; + + Future ReauthenticateAndRetrieveData_DEPRECATED( + const Credential& credential); + Future ReauthenticateAndRetrieveDataLastResult_DEPRECATED() + const; + + Future ReauthenticateWithProvider_DEPRECATED( + FederatedAuthProvider* provider); + Future ReauthenticateWithProviderLastResult_DEPRECATED() const; + + Future Delete(); + Future DeleteLastResult() const; + + UserMetadata metadata() const; + bool is_email_verified() const; + bool is_anonymous() const; + std::string uid() const; + std::string email() const; + std::string display_name() const; + std::string phone_number() const; + std::string photo_url() const; + std::string provider_id() const; + + private: + friend class firebase::auth::FederatedOAuthProvider; + friend class firebase::auth::User; + + void clear_user_infos(); + + // Pointer to the originating auth context. + AuthData* auth_data_; + + // Android Java SDK Implementation of a FirebaseUser object. + jobject user_; + + // Future data used to synchronize asynchronous calls. + FutureData future_data_; + + // Used to support older method invocation of provider_data_DEPRECATED(). + std::vector user_infos_; + + // Guard against changes to the user_ object. + Mutex user_mutex_; + + // Used to identify on-going futures in case we need to cancel them + // upon UserInternal destruction. + std::string future_api_id_; +}; + // The `ReadFutureResultFn` for `SignIn` APIs. // Reads the `AuthResult` in `result` and initialize the `User*` in `void_data`. void ReadSignInResult(jobject result, FutureCallbackData* d, @@ -112,18 +228,28 @@ inline JNIEnv* Env(AuthData* auth_data) { return auth_data->app->GetJNIEnv(); } // Delete the existing impl pointer global reference, if it already exists. void SetImplFromLocalRef(JNIEnv* env, jobject j_local, void** impl); +// Synchronize the current user. +void UpdateCurrentUser(JNIEnv* env, AuthData* auth_data); + +/// @deprecated +/// +/// Replace the platform-dependent FirebaseUser Android SDK object. +/// Note: this function is only used to support DEPRECATED methods which return +/// User*. This functionality should be removed when those deprecated methods +/// are removed. +inline void SetUserImpl(JNIEnv* env, AuthData* _Nonnull auth_data, + jobject j_user) { + assert(auth_data->deprecated_fields.user_internal_deprecated); + auth_data->deprecated_fields.user_internal_deprecated + ->set_native_user_object_deprecated(j_user); +} + // Return the Java FirebaseAuth class from our platform-independent // representation. inline jobject AuthImpl(AuthData* auth_data) { return static_cast(auth_data->auth_impl); } -// Return the Java FirebaseUser class from our platform-independent -// representation. -inline jobject UserImpl(AuthData* auth_data) { - return static_cast(auth_data->user_impl); -} - // Return a platform-independent representation of Java's FirebaseUser class. inline void* ImplFromUser(jobject user) { return static_cast(user); } @@ -177,24 +303,25 @@ AuthError MapFutureCallbackResultToAuthError(JNIEnv* env, jobject result, template void FutureCallback(JNIEnv* env, jobject result, util::FutureResult result_code, const char* status_message, void* callback_data) { - FutureCallbackData* data = + FutureCallbackData* future_callback_data = static_cast*>(callback_data); bool success = false; const AuthError error = MapFutureCallbackResultToAuthError(env, result, result_code, &success); // Finish off the asynchronous call so that the caller can read it. - data->auth_data->future_impl.Complete( - data->handle, error, status_message, - [result, success, data](void* user_data) { - if (data->future_data_read_fn != nullptr) { - data->future_data_read_fn(result, data, success, user_data); + future_callback_data->future_impl->Complete( + future_callback_data->handle, error, status_message, + [result, success, future_callback_data](void* user_data) { + if (future_callback_data->future_data_read_fn != nullptr) { + future_callback_data->future_data_read_fn( + result, future_callback_data, success, user_data); } }); // Remove the callback structure that was allocated when the callback was // created in SetupFuture(). - delete data; - data = nullptr; + delete future_callback_data; + future_callback_data = nullptr; } // The function called by the Java thread when a result completes. @@ -203,7 +330,7 @@ void FederatedAuthProviderFutureCallback(JNIEnv* env, jobject result, util::FutureResult result_code, const char* status_message, void* callback_data) { - FutureCallbackData* data = + FutureCallbackData* future_callback_data = static_cast*>(callback_data); bool success = false; AuthError error = @@ -215,18 +342,19 @@ void FederatedAuthProviderFutureCallback(JNIEnv* env, jobject result, error = kAuthErrorInvalidProviderId; } // Finish off the asynchronous call so that the caller can read it. - data->auth_data->future_impl.Complete( - data->handle, error, status_message, - [result, success, data](void* user_data) { - if (data->future_data_read_fn != nullptr) { - data->future_data_read_fn(result, data, success, user_data); + future_callback_data->future_impl->Complete( + future_callback_data->handle, error, status_message, + [result, success, future_callback_data](void* user_data) { + if (future_callback_data->future_data_read_fn != nullptr) { + future_callback_data->future_data_read_fn( + result, future_callback_data, success, user_data); } }); // Remove the callback structure that was allocated when the callback was // created in SetupFuture(). - delete data; - data = nullptr; + delete future_callback_data; + future_callback_data = nullptr; } // Ensure `FutureCallback` gets called when `pending_result` completes. @@ -234,13 +362,14 @@ void FederatedAuthProviderFutureCallback(JNIEnv* env, jobject result, // data from Java, and then complete the Future for `handle`. template void RegisterCallback( - jobject pending_result, SafeFutureHandle handle, AuthData* auth_data, + AuthData* auth_data, jobject pending_result, SafeFutureHandle handle, + const std::string& future_api_id, ReferenceCountedFutureImpl* future_impl, typename FutureCallbackData::ReadFutureResultFn read_result_fn) { // The FutureCallbackData structure is deleted in FutureCallback(). util::RegisterCallbackOnTask( Env(auth_data), pending_result, FutureCallback, - new FutureCallbackData(handle, auth_data, read_result_fn), - auth_data->future_api_id.c_str()); + new FutureCallbackData(auth_data, handle, future_impl, read_result_fn), + future_api_id.c_str()); } // Akin to RegisterCallback above, but has a special callback handler @@ -250,14 +379,15 @@ void RegisterCallback( // with the existing API behavior for other sign in events. template void RegisterFederatedAuthProviderCallback( - jobject pending_result, SafeFutureHandle handle, AuthData* auth_data, + AuthData* auth_data, jobject pending_result, SafeFutureHandle handle, + const std::string& future_api_id, ReferenceCountedFutureImpl* future_impl, typename FutureCallbackData::ReadFutureResultFn read_result_fn) { // The FutureCallbackData structure is deleted in // FederatedAuthProviderFutureCallback(). util::RegisterCallbackOnTask( Env(auth_data), pending_result, FederatedAuthProviderFutureCallback, - new FutureCallbackData(handle, auth_data, read_result_fn), - auth_data->future_api_id.c_str()); + new FutureCallbackData(auth_data, handle, future_impl, read_result_fn), + future_api_id.c_str()); } // Checks if there was an error, and if so, completes the given future with the diff --git a/auth/src/android/credential_android.cc b/auth/src/android/credential_android.cc index e9512f9bf6..1c06f266b7 100644 --- a/auth/src/android/credential_android.cc +++ b/auth/src/android/credential_android.cc @@ -652,24 +652,24 @@ Future GameCenterAuthProvider::GetCredential() { // Game Center is not available on Android bool is_gamecenter_available_on_android = false; - auto future_api = GetCredentialFutureImpl(); - const auto handle = - future_api->SafeAlloc(kCredentialFn_GameCenterGetCredential); + ReferenceCountedFutureImpl* future_impl = GetCredentialFutureImpl(); + const auto future_handle = + future_impl->SafeAlloc(kCredentialFn_GameCenterGetCredential); - future_api->Complete(handle, kAuthErrorInvalidCredential, - "GameCenter is not supported on Android."); + future_impl->Complete(future_handle, kAuthErrorInvalidCredential, + "GameCenter is not supported on Android."); - FIREBASE_ASSERT_RETURN(MakeFuture(future_api, handle), + FIREBASE_ASSERT_RETURN(MakeFuture(future_impl, future_handle), is_gamecenter_available_on_android); - return MakeFuture(future_api, handle); + return MakeFuture(future_impl, future_handle); } // static Future GameCenterAuthProvider::GetCredentialLastResult() { - auto future_api = GetCredentialFutureImpl(); + ReferenceCountedFutureImpl* future_impl = GetCredentialFutureImpl(); auto last_result = - future_api->LastResult(kCredentialFn_GameCenterGetCredential); + future_impl->LastResult(kCredentialFn_GameCenterGetCredential); return static_cast&>(last_result); } @@ -1013,75 +1013,80 @@ Future FederatedOAuthProvider::SignIn(AuthData* auth_data) { assert(auth_data); JNIEnv* env = Env(auth_data); - ReferenceCountedFutureImpl& futures = auth_data->future_impl; - const auto handle = futures.SafeAlloc( + ReferenceCountedFutureImpl& future_impl = auth_data->future_impl; + const auto future_handle = future_impl.SafeAlloc( kAuthFn_SignInWithProvider_DEPRECATED, SignInResult()); jobject oauthprovider = ConstructOAuthProvider(auth_data, provider_data_); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { + if (!CheckAndCompleteFutureOnError(env, &future_impl, future_handle)) { jobject task = env->CallObjectMethod( AuthImpl(auth_data), auth_idp::GetMethodId(auth_idp::kStartActivityForSignInWithProvider), auth_data->app->activity(), oauthprovider); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterFederatedAuthProviderCallback(task, handle, auth_data, - ReadSignInResult); + + if (!CheckAndCompleteFutureOnError(env, &future_impl, future_handle)) { + RegisterFederatedAuthProviderCallback( + auth_data, task, future_handle, auth_data->future_api_id, + &auth_data->future_impl, ReadSignInResult); } env->DeleteLocalRef(task); } env->DeleteLocalRef(oauthprovider); - return MakeFuture(&futures, handle); + return MakeFuture(&future_impl, future_handle); } -Future FederatedOAuthProvider::Link(AuthData* auth_data) { - assert(auth_data); - JNIEnv* env = Env(auth_data); - ReferenceCountedFutureImpl& futures = auth_data->future_impl; - const auto handle = futures.SafeAlloc( +Future FederatedOAuthProvider::Link(AuthData* auth_data, + UserInternal* user_internal) { + ReferenceCountedFutureImpl& future_impl = + user_internal->future_data_.future_impl; + const auto future_handle = future_impl.SafeAlloc( kUserFn_LinkWithProvider_DEPRECATED, SignInResult()); + JNIEnv* env = Env(auth_data); jobject oauthprovider = ConstructOAuthProvider(auth_data, provider_data_); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { + if (!CheckAndCompleteFutureOnError(env, &future_impl, future_handle)) { jobject task = env->CallObjectMethod( - UserImpl(auth_data), + user_internal->user_, user_idp::GetMethodId(user_idp::kStartActivityForLinkWithProvider), auth_data->app->activity(), oauthprovider); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterFederatedAuthProviderCallback(task, handle, auth_data, - ReadSignInResult); + if (!CheckAndCompleteFutureOnError(env, &future_impl, future_handle)) { + RegisterFederatedAuthProviderCallback( + auth_data, task, future_handle, auth_data->future_api_id, + &auth_data->future_impl, ReadSignInResult); } env->DeleteLocalRef(task); } env->DeleteLocalRef(oauthprovider); - return MakeFuture(&futures, handle); + return MakeFuture(&future_impl, future_handle); } Future FederatedOAuthProvider::Reauthenticate( - AuthData* auth_data) { - assert(auth_data); - JNIEnv* env = Env(auth_data); - ReferenceCountedFutureImpl& futures = auth_data->future_impl; - const auto handle = futures.SafeAlloc( + AuthData* auth_data, UserInternal* user_internal) { + ReferenceCountedFutureImpl& future_impl = + user_internal->future_data_.future_impl; + const auto future_handle = future_impl.SafeAlloc( kUserFn_ReauthenticateWithProvider_DEPRECATED, SignInResult()); + JNIEnv* env = Env(auth_data); jobject oauthprovider = ConstructOAuthProvider(auth_data, provider_data_); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { + if (!CheckAndCompleteFutureOnError(env, &future_impl, future_handle)) { jobject task = env->CallObjectMethod( - UserImpl(auth_data), + user_internal->user_, user_idp::GetMethodId( user_idp::kStartActivityForReauthenticateWithProvider), auth_data->app->activity(), oauthprovider); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterFederatedAuthProviderCallback(task, handle, auth_data, - ReadSignInResult); + if (!CheckAndCompleteFutureOnError(env, &future_impl, future_handle)) { + RegisterFederatedAuthProviderCallback( + auth_data, task, future_handle, auth_data->future_api_id, + &auth_data->future_impl, ReadSignInResult); } env->DeleteLocalRef(task); } env->DeleteLocalRef(oauthprovider); - return MakeFuture(&futures, handle); + return MakeFuture(&future_impl, future_handle); } } // namespace auth diff --git a/auth/src/android/user_android.cc b/auth/src/android/user_android.cc index 2df7db018e..9dfc927f03 100644 --- a/auth/src/android/user_android.cc +++ b/auth/src/android/user_android.cc @@ -128,7 +128,7 @@ METHOD_LOOKUP_DEFINITION( "com/google/firebase/auth/UserProfileChangeRequest$Builder", USER_PROFILE_BUILDER_METHODS) -bool CacheUserMethodIds(JNIEnv* env, jobject activity) { +bool CacheUserMethodIds(JNIEnv *env, jobject activity) { return phonecredential::CacheMethodIds(env, activity) && tokenresult::CacheMethodIds(env, activity) && user::CacheMethodIds(env, activity) && @@ -137,7 +137,7 @@ bool CacheUserMethodIds(JNIEnv* env, jobject activity) { userprofilebuilder::CacheMethodIds(env, activity); } -void ReleaseUserClasses(JNIEnv* env) { +void ReleaseUserClasses(JNIEnv *env) { phonecredential::ReleaseClass(env); tokenresult::ReleaseClass(env); user::ReleaseClass(env); @@ -146,137 +146,407 @@ void ReleaseUserClasses(JNIEnv* env) { userprofilebuilder::ReleaseClass(env); } +/// +/// AndroidWrappedUserInfo Class. +/// Queries data out of Java Android SDK UserInfo objects. +/// enum PropertyType { kPropertyTypeString, kPropertyTypeUri }; -static std::string GetUserProperty(AuthData* auth_data, jobject impl, - userinfo::Method method_id, - PropertyType type = kPropertyTypeString) { - JNIEnv* env = Env(auth_data); - jobject property = - impl ? env->CallObjectMethod(impl, userinfo::GetMethodId(method_id)) - : nullptr; - if (firebase::util::CheckAndClearJniExceptions(env) || !property) { - return std::string(); +class AndroidWrappedUserInfo : public UserInfoInterface { + public: + AndroidWrappedUserInfo(AuthData *auth_data, jobject user_info) + : auth_data_(auth_data), user_info_(user_info) { + // Convert `user_info` to a global reference. + JNIEnv *env = Env(auth_data_); + user_info_ = env->NewGlobalRef(user_info); + env->DeleteLocalRef(user_info); } - if (type == kPropertyTypeUri) { - return JniUriToString(env, property); + + virtual ~AndroidWrappedUserInfo() { + // Delete global reference. + JNIEnv *env = Env(auth_data_); + env->DeleteGlobalRef(user_info_); + user_info_ = nullptr; + auth_data_ = nullptr; + } + + std::string uid() const override { + return GetUserProperty(userinfo::kGetUid); + } + + std::string email() const override { + return GetUserProperty(userinfo::kGetEmail); + } + + std::string display_name() const override { + return GetUserProperty(userinfo::kGetDisplayName); + } + + std::string phone_number() const override { + return GetUserProperty(userinfo::kGetPhoneNumber); + } + + std::string photo_url() const override { + return GetUserProperty(userinfo::kGetPhotoUrl, kPropertyTypeUri); + } + + std::string provider_id() const override { + return GetUserProperty(userinfo::kGetProviderId); + } + + private: + std::string GetUserProperty(userinfo::Method method_id, + PropertyType type = kPropertyTypeString) const { + JNIEnv *env = Env(auth_data_); + jobject property = user_info_ + ? env->CallObjectMethod( + user_info_, userinfo::GetMethodId(method_id)) + : nullptr; + if (firebase::util::CheckAndClearJniExceptions(env) || !property) { + return std::string(); + } + if (type == kPropertyTypeUri) { + return JniUriToString(env, property); + } else { + // type == kPropertyTypeString + return JniStringToString(env, property); + } + } + + /// The AuthData context, required JNI environment data. + AuthData *auth_data_; + + /// Pointer to the main class. + /// Needed for context in implementation of virtuals. + jobject user_info_; +}; + +/// +/// User Class +/// Platform specific implementation of UserInternal. +/// +User::User(AuthData *auth_data, UserInternal *user_internal) { + assert(auth_data); + auth_data_ = auth_data; + if (user_internal == nullptr) { + // Create an invalid user internal. + // This will return is_valid() false, and operations will fail. + user_internal_ = new UserInternal(auth_data_, nullptr); + } else { + user_internal_ = user_internal; + } +} + +User::User(const User &user) { + assert(user.auth_data_); + auth_data_ = user.auth_data_; + if (user.user_internal_ != nullptr) { + user_internal_ = new UserInternal(auth_data_, user.user_internal_->user_); + } else { + user_internal_ = new UserInternal(auth_data_, nullptr); + } +} + +User::~User() { + delete user_internal_; + user_internal_ = nullptr; + auth_data_ = nullptr; +} + +User &User::operator=(const User &user) { + assert(user_internal_); + delete user_internal_; + + auth_data_ = user.auth_data_; + + if (user.user_internal_ != nullptr) { + user_internal_ = new UserInternal(auth_data_, user.user_internal_->user_); } else { - // type == kPropertyTypeString - return JniStringToString(env, property); + user_internal_ = new UserInternal(auth_data_, nullptr); } + + return *this; } -static std::string GetUID(AuthData* auth_data, jobject impl) { - return GetUserProperty(auth_data, impl, userinfo::kGetUid); +bool User::is_valid() const { + assert(user_internal_); + return user_internal_->is_valid(); } -static std::string GetEmail(AuthData* auth_data, jobject impl) { - return GetUserProperty(auth_data, impl, userinfo::kGetEmail); +Future User::GetToken(bool force_refresh) { + assert(user_internal_); + return user_internal_->GetToken(force_refresh); +} + +std::vector User::provider_data() const { + assert(user_internal_); + return user_internal_->provider_data(); } -static std::string GetDisplayName(AuthData* auth_data, jobject impl) { - return GetUserProperty(auth_data, impl, userinfo::kGetDisplayName); +const std::vector &User::provider_data_DEPRECATED() { + assert(user_internal_); + return user_internal_->provider_data_DEPRECATED(); } -static std::string GetPhoneNumber(AuthData* auth_data, jobject impl) { - return GetUserProperty(auth_data, impl, userinfo::kGetPhoneNumber); +Future User::UpdateEmail(const char *email) { + assert(user_internal_); + return user_internal_->UpdateEmail(email); } -static std::string GetPhotoUrl(AuthData* auth_data, jobject impl) { - return GetUserProperty(auth_data, impl, userinfo::kGetPhotoUrl, - kPropertyTypeUri); +Future User::UpdateEmailLastResult() const { + assert(user_internal_); + return user_internal_->UpdateEmailLastResult(); } -static std::string GetProviderId(AuthData* auth_data, jobject impl) { - return GetUserProperty(auth_data, impl, userinfo::kGetProviderId); +Future User::UpdatePassword(const char *password) { + assert(user_internal_); + return user_internal_->UpdatePassword(password); } -User::~User() {} +Future User::UpdatePasswordLastResult() const { + assert(user_internal_); + return user_internal_->UpdatePasswordLastResult(); +} + +Future User::Reauthenticate(const Credential &credential) { + assert(user_internal_); + return user_internal_->Reauthenticate(credential); +} + +Future User::ReauthenticateLastResult() const { + assert(user_internal_); + return user_internal_->ReauthenticateLastResult(); +} + +Future User::ReauthenticateAndRetrieveData_DEPRECATED( + const Credential &credential) { + assert(user_internal_); + return user_internal_->ReauthenticateAndRetrieveData_DEPRECATED(credential); +} + +Future User::ReauthenticateAndRetrieveDataLastResult_DEPRECATED() + const { + assert(user_internal_); + return user_internal_->ReauthenticateAndRetrieveDataLastResult_DEPRECATED(); +} + +Future User::ReauthenticateWithProvider_DEPRECATED( + FederatedAuthProvider *provider) const { + assert(user_internal_); + return user_internal_->ReauthenticateWithProvider_DEPRECATED(provider); +} + +Future User::SendEmailVerification() { + assert(user_internal_); + return user_internal_->SendEmailVerification(); +} + +Future User::SendEmailVerificationLastResult() const { + assert(user_internal_); + return user_internal_->SendEmailVerificationLastResult(); +} + +Future User::UpdateUserProfile(const UserProfile &profile) { + assert(user_internal_); + return user_internal_->UpdateUserProfile(profile); +} + +Future User::UpdateUserProfileLastResult() const { + assert(user_internal_); + return user_internal_->UpdateUserProfileLastResult(); +} + +Future User::LinkWithCredential_DEPRECATED( + const Credential &credential) { + assert(user_internal_); + return user_internal_->LinkWithCredential_DEPRECATED(credential); +} + +Future User::LinkWithCredentialLastResult_DEPRECATED() const { + assert(user_internal_); + return user_internal_->LinkWithCredentialLastResult_DEPRECATED(); +} + +Future User::LinkAndRetrieveDataWithCredential_DEPRECATED( + const Credential &credential) { + assert(user_internal_); + return user_internal_->LinkAndRetrieveDataWithCredential_DEPRECATED( + credential); +} + +Future +User::LinkAndRetrieveDataWithCredentialLastResult_DEPRECATED() const { + assert(user_internal_); + return user_internal_ + ->LinkAndRetrieveDataWithCredentialLastResult_DEPRECATED(); +} + +Future User::LinkWithProvider_DEPRECATED( + FederatedAuthProvider *provider) { + assert(user_internal_); + return user_internal_->LinkWithProvider_DEPRECATED(provider); +} + +Future User::Unlink_DEPRECATED(const char *provider) { + assert(user_internal_); + return user_internal_->Unlink_DEPRECATED(provider); +} + +Future User::UnlinkLastResult_DEPRECATED() const { + assert(user_internal_); + return user_internal_->UnlinkLastResult_DEPRECATED(); +} + +Future User::UpdatePhoneNumberCredential_DEPRECATED( + const Credential &credential) { + assert(user_internal_); + return user_internal_->UpdatePhoneNumberCredential_DEPRECATED(credential); +} + +Future User::UpdatePhoneNumberCredentialLastResult_DEPRECATED() const { + assert(user_internal_); + return user_internal_->UpdatePhoneNumberCredentialLastResult_DEPRECATED(); +} + +Future User::Reload() { + assert(user_internal_); + return user_internal_->Reload(); +} + +Future User::ReloadLastResult() const { + assert(user_internal_); + return user_internal_->ReloadLastResult(); +} + +Future User::Delete() { + assert(user_internal_); + return user_internal_->Delete(); +} + +Future User::DeleteLastResult() const { + assert(user_internal_); + return user_internal_->DeleteLastResult(); +} + +UserMetadata User::metadata() const { + assert(user_internal_); + return user_internal_->metadata(); +} + +bool User::is_email_verified() const { + assert(user_internal_); + return user_internal_->is_email_verified(); +} + +bool User::is_anonymous() const { + assert(user_internal_); + return user_internal_->is_anonymous(); +} std::string User::uid() const { - return ValidUser(auth_data_) ? GetUID(auth_data_, UserImpl(auth_data_)) : ""; + assert(user_internal_); + return user_internal_->uid(); } std::string User::email() const { - return ValidUser(auth_data_) ? GetEmail(auth_data_, UserImpl(auth_data_)) - : ""; + assert(user_internal_); + return user_internal_->email(); } std::string User::display_name() const { - return ValidUser(auth_data_) - ? GetDisplayName(auth_data_, UserImpl(auth_data_)) - : ""; -} - -std::string User::phone_number() const { - return ValidUser(auth_data_) - ? GetPhoneNumber(auth_data_, UserImpl(auth_data_)) - : ""; + assert(user_internal_); + return user_internal_->display_name(); } std::string User::photo_url() const { - return ValidUser(auth_data_) ? GetPhotoUrl(auth_data_, UserImpl(auth_data_)) - : ""; + assert(user_internal_); + return user_internal_->photo_url(); } std::string User::provider_id() const { - return ValidUser(auth_data_) ? GetProviderId(auth_data_, UserImpl(auth_data_)) - : ""; + assert(user_internal_); + return user_internal_->provider_id(); } -class AndroidWrappedUserInfo : public UserInfoInterface { - public: - AndroidWrappedUserInfo(AuthData* auth_data, jobject user_info) - : auth_data_(auth_data), user_info_(user_info) { - // Convert `user_info` to a global reference. - JNIEnv* env = Env(auth_data_); - user_info_ = env->NewGlobalRef(user_info); - env->DeleteLocalRef(user_info); - } - - virtual ~AndroidWrappedUserInfo() { - // Delete global reference. - JNIEnv* env = Env(auth_data_); - env->DeleteGlobalRef(user_info_); - user_info_ = nullptr; - } +std::string User::phone_number() const { + assert(user_internal_); + return user_internal_->phone_number(); +} - std::string uid() const override { return GetUID(auth_data_, user_info_); } +/// +/// UserInternal Class +/// +void assign_future_id(UserInternal *user_internal, std::string *future_api_id) { + static const char *kApiIdentifier = "UserInternal"; + future_api_id->reserve(strlen(kApiIdentifier) + + 16 /* hex characters in the pointer */ + + 1 /* null terminator */); + snprintf(&((*future_api_id)[0]), future_api_id->capacity(), "%s0x%016llx", + kApiIdentifier, + static_cast( // NOLINT + reinterpret_cast(user_internal))); +} - std::string email() const override { - return GetEmail(auth_data_, user_info_); +UserInternal::UserInternal(AuthData *auth_data, jobject user) + : auth_data_(auth_data), user_(nullptr), future_data_(kUserFnCount) { + assert(auth_data_); + JNIEnv *env = Env(auth_data_); + if (user != nullptr) { + user_ = env->NewGlobalRef(user); + assert(env->ExceptionCheck() == false); } + assign_future_id(this, &future_api_id_); +} - std::string display_name() const override { - return GetDisplayName(auth_data_, user_info_); +UserInternal::UserInternal(const UserInternal &user_internal) + : auth_data_(user_internal.auth_data_), + user_(nullptr), + future_data_(kUserFnCount) { + assert(auth_data_); + JNIEnv *env = Env(auth_data_); + if (user_internal.user_ != nullptr) { + user_ = env->NewGlobalRef(user_internal.user_); + assert(env->ExceptionCheck() == false); } + assign_future_id(this, &future_api_id_); +} - std::string phone_number() const override { - return GetPhoneNumber(auth_data_, user_info_); - } +UserInternal::~UserInternal() { + MutexLock user_lock(user_mutex_); + JNIEnv *env = Env(auth_data_); + util::CancelCallbacks(env, future_api_id_.c_str()); - std::string photo_url() const override { - return GetPhotoUrl(auth_data_, user_info_); + // Make sure we don't have any pending futures in flight before we disappear. + while (!future_data_.future_impl.IsSafeToDelete()) { + internal::Sleep(100); } - std::string provider_id() const override { - return GetProviderId(auth_data_, user_info_); - } + env->DeleteGlobalRef(user_); + user_ = nullptr; + auth_data_ = nullptr; - private: - /// Pointer to the main class. - /// Needed for context in implementation of virtuals. - AuthData* auth_data_; + clear_user_infos(); +} - /// Pointer to the main class. - /// Needed for context in implementation of virtuals. - jobject user_info_; -}; +void UserInternal::set_native_user_object_deprecated(jobject user) { + MutexLock user_lock(user_mutex_); + user_ = user; +} -void ReadTokenResult(jobject result, FutureCallbackData* d, - bool success, void* void_data) { - auto data = static_cast(void_data); - JNIEnv* env = Env(d->auth_data); +bool UserInternal::is_valid() const { return user_ != nullptr; } + +void UserInternal::clear_user_infos() { + for (size_t i = 0; i < user_infos_.size(); ++i) { + delete user_infos_[i]; + user_infos_[i] = nullptr; + } + user_infos_.clear(); +} + +void ReadTokenResult(jobject result, FutureCallbackData *d, + bool success, void *void_data) { + auto data = static_cast(void_data); + JNIEnv *env = Env(d->auth_data); // `result` is of type GetTokenResult when `success` is true. if (success) { @@ -295,45 +565,82 @@ void ReadTokenResult(jobject result, FutureCallbackData* d, } } -Future User::GetToken(bool force_refresh) { - if (!ValidUser(auth_data_)) { - return Future(); +Future UserInternal::GetToken(bool force_refresh) { + MutexLock user_info_lock(user_mutex_); + if (!is_valid()) { + return CreateAndCompleteFuture(kUserFn_GetToken, kAuthErrorInvalidUser, + kUserNotInitializedErrorMessage, + &future_data_, ""); } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc(kUserFn_GetToken); - JNIEnv* env = Env(auth_data_); + + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc(kUserFn_GetToken); + Future future = + MakeFuture(&future_data_.future_impl, future_handle); auth_data_->SetExpectIdTokenListenerCallback(force_refresh); + + JNIEnv *env = Env(auth_data_); jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kToken), force_refresh); + user_, user::GetMethodId(user::kToken), force_refresh); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, ReadTokenResult); + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, future_api_id_, + &future_data_.future_impl, ReadTokenResult); env->DeleteLocalRef(pending_result); } else { // If the method failed for some reason, clear the expected callback. auth_data_->SetExpectIdTokenListenerCallback(false); } - return MakeFuture(&futures, handle); + return future; } -const std::vector& User::provider_data() const { - ClearUserInfos(auth_data_); +std::vector UserInternal::provider_data() const { + JNIEnv *env = Env(auth_data_); + std::vector local_user_infos; + if (is_valid()) { + const jobject list = + env->CallObjectMethod(user_, user::GetMethodId(user::kProviderData)); + assert(env->ExceptionCheck() == false); - if (ValidUser(auth_data_)) { - JNIEnv* env = Env(auth_data_); + if (list != nullptr) { + const int num_providers = + env->CallIntMethod(list, util::list::GetMethodId(util::list::kSize)); + assert(env->ExceptionCheck() == false); + local_user_infos.resize(num_providers); + for (int i = 0; i < num_providers; ++i) { + // user_info is converted to a global reference in + // AndroidWrappedUserInfo() and the local reference is released. + jobject user_info = env->CallObjectMethod( + list, util::list::GetMethodId(util::list::kGet), i); + assert(env->ExceptionCheck() == false); + local_user_infos[i] = AndroidWrappedUserInfo(auth_data_, user_info); + } + env->DeleteLocalRef(list); + } + } + return local_user_infos; +} + +const std::vector + &UserInternal::provider_data_DEPRECATED() { + MutexLock user_info_lock(user_mutex_); + clear_user_infos(); + if (is_valid()) { + JNIEnv *env = Env(auth_data_); // getProviderData returns `List` - const jobject list = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kProviderData)); + const jobject list = + env->CallObjectMethod(user_, user::GetMethodId(user::kProviderData)); assert(env->ExceptionCheck() == false); - // Copy the list into auth_data_->user_infos. + // Copy the list into user_infos_. if (list != nullptr) { const int num_providers = env->CallIntMethod(list, util::list::GetMethodId(util::list::kSize)); assert(env->ExceptionCheck() == false); - auth_data_->user_infos.resize(num_providers); + user_infos_.resize(num_providers); for (int i = 0; i < num_providers; ++i) { // user_info is converted to a global reference in @@ -341,73 +648,97 @@ const std::vector& User::provider_data() const { jobject user_info = env->CallObjectMethod( list, util::list::GetMethodId(util::list::kGet), i); assert(env->ExceptionCheck() == false); - auth_data_->user_infos[i] = - new AndroidWrappedUserInfo(auth_data_, user_info); + user_infos_[i] = new AndroidWrappedUserInfo(auth_data_, user_info); } env->DeleteLocalRef(list); } } - // Return a reference to our internally-backed values. - return auth_data_->user_infos; + return user_infos_; } -Future User::UpdateEmail(const char* email) { - if (!ValidUser(auth_data_)) { - return Future(); +Future UserInternal::UpdateEmail(const char *email) { + MutexLock user_info_lock(user_mutex_); + if (!is_valid()) { + return CreateAndCompleteFuture(kUserFn_UpdateEmail, kAuthErrorInvalidUser, + kUserNotInitializedErrorMessage, + &future_data_); } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc(kUserFn_UpdateEmail); - JNIEnv* env = Env(auth_data_); + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc(kUserFn_UpdateEmail); + Future future = MakeFuture(&future_data_.future_impl, future_handle); + JNIEnv *env = Env(auth_data_); jstring j_email = env->NewStringUTF(email); jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kUpdateEmail), j_email); + user_, user::GetMethodId(user::kUpdateEmail), j_email); env->DeleteLocalRef(j_email); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, nullptr); + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, future_api_id_, + &future_data_.future_impl, nullptr); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return future; +} + +Future UserInternal::UpdateEmailLastResult() const { + return static_cast &>( + future_data_.future_impl.LastResult(kUserFn_UpdateEmail)); } -Future User::UpdatePassword(const char* password) { - if (!ValidUser(auth_data_)) { - return Future(); +Future UserInternal::UpdatePassword(const char *password) { + MutexLock user_info_lock(user_mutex_); + if (!is_valid()) { + return CreateAndCompleteFuture( + kUserFn_UpdatePassword, kAuthErrorInvalidUser, + kUserNotInitializedErrorMessage, &future_data_); } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc(kUserFn_UpdatePassword); - JNIEnv* env = Env(auth_data_); + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc(kUserFn_UpdatePassword); + Future future = MakeFuture(&future_data_.future_impl, future_handle); + JNIEnv *env = Env(auth_data_); jstring j_password = env->NewStringUTF(password); jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kUpdatePassword), - j_password); + user_, user::GetMethodId(user::kUpdatePassword), j_password); env->DeleteLocalRef(j_password); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, nullptr); + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, future_api_id_, + &future_data_.future_impl, nullptr); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return future; +} + +Future UserInternal::UpdatePasswordLastResult() const { + return static_cast &>( + future_data_.future_impl.LastResult(kUserFn_UpdatePassword)); } -Future User::UpdateUserProfile(const UserProfile& profile) { - if (!ValidUser(auth_data_)) { - return Future(); +Future UserInternal::UpdateUserProfile(const User::UserProfile &profile) { + MutexLock user_info_lock(user_mutex_); + if (!is_valid()) { + return CreateAndCompleteFuture( + kUserFn_UpdateUserProfile, kAuthErrorInvalidUser, + kUserNotInitializedErrorMessage, &future_data_); } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc(kUserFn_UpdateUserProfile); - JNIEnv* env = Env(auth_data_); + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc(kUserFn_UpdateUserProfile); + Future future = MakeFuture(&future_data_.future_impl, future_handle); + + JNIEnv *env = Env(auth_data_); AuthError error = kAuthErrorNone; std::string exception_error_message; jobject j_user_profile_builder = env->NewObject( userprofilebuilder::GetClass(), userprofilebuilder::GetMethodId(userprofilebuilder::kConstructor)); - // Painfully call UserProfileChangeRequest.Builder.setDisplayName. + // Call UserProfileChangeRequest.Builder.setDisplayName. if (profile.display_name != nullptr) { jstring j_display_name = env->NewStringUTF(profile.display_name); jobject j_builder_discard = env->CallObjectMethod( @@ -422,7 +753,7 @@ Future User::UpdateUserProfile(const UserProfile& profile) { env->DeleteLocalRef(j_display_name); } - // Extra painfully call UserProfileChangeRequest.Builder.setPhotoUri. + // Call UserProfileChangeRequest.Builder.setPhotoUri. if (error == kAuthErrorNone && profile.photo_url != nullptr) { jobject j_uri = CharsToJniUri(env, profile.photo_url); jobject j_builder_discard = env->CallObjectMethod( @@ -448,272 +779,489 @@ Future User::UpdateUserProfile(const UserProfile& profile) { if (error == kAuthErrorNone) { // Call FirebaseUser.updateProfile. jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kUpdateUserProfile), + user_, user::GetMethodId(user::kUpdateUserProfile), j_user_profile_request); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, nullptr); + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, + future_api_id_, &future_data_.future_impl, nullptr); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return future; } else { - futures.Complete(handle, error, exception_error_message.c_str()); + future_data_.future_impl.Complete(future_handle, error, + exception_error_message.c_str()); } if (j_user_profile_request) { env->DeleteLocalRef(j_user_profile_request); } env->DeleteLocalRef(j_user_profile_builder); - return MakeFuture(&futures, handle); + return future; } -Future User::LinkWithCredential_DEPRECATED( - const Credential& credential) { - if (!ValidUser(auth_data_)) { - return Future(); +Future UserInternal::UpdateUserProfileLastResult() const { + return static_cast &>( + future_data_.future_impl.LastResult(kUserFn_UpdateUserProfile)); +} + +Future UserInternal::SendEmailVerification() { + MutexLock user_info_lock(user_mutex_); + if (!is_valid()) { + return CreateAndCompleteFuture( + kUserFn_SendEmailVerification, kAuthErrorInvalidUser, + kUserNotInitializedErrorMessage, &future_data_); } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = - futures.SafeAlloc(kUserFn_LinkWithCredential_DEPRECATED); - JNIEnv* env = Env(auth_data_); + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc(kUserFn_SendEmailVerification); + Future future = MakeFuture(&future_data_.future_impl, future_handle); + + JNIEnv *env = Env(auth_data_); jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kLinkWithCredential), - CredentialFromImpl(credential.impl_)); + user_, user::GetMethodId(user::kSendEmailVerification)); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, - ReadUserFromSignInResult); + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, future_api_id_, + &future_data_.future_impl, nullptr); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return future; } -Future User::LinkAndRetrieveDataWithCredential( - const Credential& credential) { - if (!ValidUser(auth_data_)) { - return Future(); +Future UserInternal::SendEmailVerificationLastResult() const { + return static_cast &>( + future_data_.future_impl.LastResult(kUserFn_SendEmailVerification)); +} + +Future UserInternal::LinkWithCredential_DEPRECATED( + const Credential &credential) { + MutexLock user_info_lock(user_mutex_); + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc( + kUserFn_LinkWithCredential_DEPRECATED); + Future future = MakeFuture(&future_data_.future_impl, future_handle); + + if (!is_valid()) { + CompleteFuture(kAuthErrorInvalidUser, kUserNotInitializedErrorMessage, + future_handle, &future_data_, (User *)nullptr); + return future; } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc( - kUserFn_LinkAndRetrieveDataWithCredential); - JNIEnv* env = Env(auth_data_); - jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kLinkWithCredential), - CredentialFromImpl(credential.impl_)); + JNIEnv *env = Env(auth_data_); + jobject pending_result = + env->CallObjectMethod(user_, user::GetMethodId(user::kLinkWithCredential), + CredentialFromImpl(credential.impl_)); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, ReadSignInResult); + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, future_api_id_, + &future_data_.future_impl, ReadUserFromSignInResult); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return future; } -Future User::LinkWithProvider_DEPRECATED( - FederatedAuthProvider* provider) const { - FIREBASE_ASSERT_RETURN(Future(), provider); - return provider->Link(auth_data_); +Future UserInternal::LinkWithCredentialLastResult_DEPRECATED() const { + return static_cast &>( + future_data_.future_impl.LastResult( + kUserFn_LinkWithCredential_DEPRECATED)); } -Future User::Unlink_DEPRECATED(const char* provider) { - if (!ValidUser(auth_data_)) { - return Future(); +Future UserInternal::LinkAndRetrieveDataWithCredential_DEPRECATED( + const Credential &credential) { + MutexLock user_info_lock(user_mutex_); + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc( + kUserFn_LinkAndRetrieveDataWithCredential_DEPRECATED); + Future future = + MakeFuture(&future_data_.future_impl, future_handle); + if (!is_valid()) { + CompleteFuture(kAuthErrorInvalidUser, kUserNotInitializedErrorMessage, + future_handle, &future_data_, SignInResult()); + return future; } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc(kUserFn_Unlink_DEPRECATED); - JNIEnv* env = Env(auth_data_); + JNIEnv *env = Env(auth_data_); + jobject pending_result = + env->CallObjectMethod(user_, user::GetMethodId(user::kLinkWithCredential), + CredentialFromImpl(credential.impl_)); + + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, future_api_id_, + &future_data_.future_impl, ReadSignInResult); + env->DeleteLocalRef(pending_result); + } + return future; +} + +Future +UserInternal::LinkAndRetrieveDataWithCredentialLastResult_DEPRECATED() const { + return static_cast &>( + future_data_.future_impl.LastResult( + kUserFn_LinkAndRetrieveDataWithCredential_DEPRECATED)); +} + +Future UserInternal::LinkWithProvider_DEPRECATED( + FederatedAuthProvider *provider) { + MutexLock user_info_lock(user_mutex_); + if (!is_valid() || provider == nullptr) { + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc( + kUserFn_LinkWithProvider_DEPRECATED); + Future future = + MakeFuture(&future_data_.future_impl, future_handle); + if (!is_valid()) { + CompleteFuture(kAuthErrorInvalidUser, kUserNotInitializedErrorMessage, + future_handle, &future_data_, SignInResult()); + } else { + CompleteFuture(kAuthErrorInvalidParameter, + kUserNotInitializedErrorMessage, future_handle, + &future_data_, SignInResult()); + } + return future; + } + return provider->Link(auth_data_, this); +} + +Future UserInternal::Unlink_DEPRECATED(const char *provider) { + MutexLock user_info_lock(user_mutex_); + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc(kUserFn_Unlink_DEPRECATED); + Future future = MakeFuture(&future_data_.future_impl, future_handle); + + if (!is_valid()) { + CompleteFuture(kAuthErrorInvalidUser, kUserNotInitializedErrorMessage, + future_handle, &future_data_, (User *)nullptr); + return future; + } + + JNIEnv *env = Env(auth_data_); jstring j_provider = env->NewStringUTF(provider); jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kUnlink), j_provider); + user_, user::GetMethodId(user::kUnlink), j_provider); env->DeleteLocalRef(j_provider); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, - ReadUserFromSignInResult); + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, future_api_id_, + &future_data_.future_impl, ReadUserFromSignInResult); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return future; +} + +Future UserInternal::UnlinkLastResult_DEPRECATED() const { + return static_cast &>( + future_data_.future_impl.LastResult(kUserFn_Unlink_DEPRECATED)); } -Future User::UpdatePhoneNumberCredential_DEPRECATED( - const Credential& credential) { - if (!ValidUser(auth_data_)) { - return Future(); +Future UserInternal::UpdatePhoneNumberCredential_DEPRECATED( + const Credential &credential) { + MutexLock user_info_lock(user_mutex_); + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc( + kUserFn_UpdatePhoneNumberCredential_DEPRECATED); + Future future = MakeFuture(&future_data_.future_impl, future_handle); + + if (!is_valid()) { + CompleteFuture(kAuthErrorInvalidUser, kUserNotInitializedErrorMessage, + future_handle, &future_data_, (User *)nullptr); + return future; } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = - futures.SafeAlloc(kUserFn_UpdatePhoneNumberCredential_DEPRECATED); - JNIEnv* env = Env(auth_data_); + JNIEnv *env = Env(auth_data_); jobject j_credential = CredentialFromImpl(credential.impl_); if (env->IsInstanceOf(j_credential, phonecredential::GetClass())) { jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), - user::GetMethodId(user::kUpdatePhoneNumberCredential), j_credential); + user_, user::GetMethodId(user::kUpdatePhoneNumberCredential), + j_credential); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, + future_api_id_, &future_data_.future_impl, ReadUserFromSignInResult); env->DeleteLocalRef(pending_result); } } else { - futures.Complete(handle, kAuthErrorInvalidCredential, - "Credential is not a phone credential."); + CompleteFuture(kAuthErrorInvalidCredential, kInvalidCredentialErrorMessage, + future_handle, &future_data_, (User *)nullptr); } - return MakeFuture(&futures, handle); + return future; } -Future User::Reload() { - if (!ValidUser(auth_data_)) { - return Future(); +Future UserInternal::UpdatePhoneNumberCredentialLastResult_DEPRECATED() + const { + return static_cast &>( + future_data_.future_impl.LastResult( + kUserFn_UpdatePhoneNumberCredential_DEPRECATED)); +} + +Future UserInternal::Reload() { + MutexLock user_info_lock(user_mutex_); + if (!is_valid()) { + return CreateAndCompleteFuture(kUserFn_Reload, kAuthErrorInvalidUser, + kUserNotInitializedErrorMessage, + &future_data_); } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc(kUserFn_Reload); - JNIEnv* env = Env(auth_data_); - jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kReload)); + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc(kUserFn_Reload); + Future future = MakeFuture(&future_data_.future_impl, future_handle); + + JNIEnv *env = Env(auth_data_); + jobject pending_result = + env->CallObjectMethod(user_, user::GetMethodId(user::kReload)); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, nullptr); + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, future_api_id_, + &future_data_.future_impl, nullptr); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return future; } -Future User::Reauthenticate(const Credential& credential) { - if (!ValidUser(auth_data_)) { - return Future(); +Future UserInternal::ReloadLastResult() const { + return static_cast &>( + future_data_.future_impl.LastResult(kUserFn_Reload)); +} + +Future UserInternal::Reauthenticate(const Credential &credential) { + MutexLock user_info_lock(user_mutex_); + if (!is_valid()) { + return CreateAndCompleteFuture( + kUserFn_Reauthenticate, kAuthErrorInvalidUser, + kUserNotInitializedErrorMessage, &future_data_); } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc(kUserFn_Reauthenticate); - JNIEnv* env = Env(auth_data_); - jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kReauthenticate), - CredentialFromImpl(credential.impl_)); + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc(kUserFn_Reload); + Future future = MakeFuture(&future_data_.future_impl, future_handle); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, nullptr); + JNIEnv *env = Env(auth_data_); + jobject pending_result = + env->CallObjectMethod(user_, user::GetMethodId(user::kReauthenticate), + CredentialFromImpl(credential.impl_)); + + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, future_api_id_, + &future_data_.future_impl, nullptr); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return future; } -Future User::ReauthenticateAndRetrieveData_DEPRECATED( - const Credential& credential) { - if (!ValidUser(auth_data_)) { - return Future(); +Future UserInternal::ReauthenticateLastResult() const { + return static_cast &>( + future_data_.future_impl.LastResult(kUserFn_Reauthenticate)); +} + +Future UserInternal::ReauthenticateAndRetrieveData_DEPRECATED( + const Credential &credential) { + MutexLock user_info_lock(user_mutex_); + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc( + kUserFn_ReauthenticateAndRetrieveData_DEPRECATED); + Future future = + MakeFuture(&future_data_.future_impl, future_handle); + if (!is_valid()) { + CompleteFuture(kAuthErrorInvalidUser, kUserNotInitializedErrorMessage, + future_handle, &future_data_, SignInResult()); + return future; } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc( - kUserFn_ReauthenticateAndRetrieveData_DEPRECATED); - JNIEnv* env = Env(auth_data_); + JNIEnv *env = Env(auth_data_); jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), - user::GetMethodId(user::kReauthenticateAndRetrieveData), + user_, user::GetMethodId(user::kReauthenticateAndRetrieveData), CredentialFromImpl(credential.impl_)); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, ReadSignInResult); + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, future_api_id_, + &future_data_.future_impl, ReadSignInResult); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return future; } -Future User::ReauthenticateWithProvider_DEPRECATED( - FederatedAuthProvider* provider) const { - FIREBASE_ASSERT_RETURN(Future(), provider); - return provider->Reauthenticate(auth_data_); +Future +UserInternal::ReauthenticateAndRetrieveDataLastResult_DEPRECATED() const { + return static_cast &>( + future_data_.future_impl.LastResult( + kUserFn_ReauthenticateAndRetrieveData_DEPRECATED)); } -Future User::SendEmailVerification() { - if (!ValidUser(auth_data_)) { - return Future(); - } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc(kUserFn_SendEmailVerification); - JNIEnv* env = Env(auth_data_); - - jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kSendEmailVerification)); - - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, nullptr); - env->DeleteLocalRef(pending_result); +Future UserInternal::ReauthenticateWithProvider_DEPRECATED( + FederatedAuthProvider *provider) { + MutexLock user_info_lock(user_mutex_); + if (!is_valid() || provider == nullptr) { + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc( + kUserFn_ReauthenticateWithProvider_DEPRECATED); + Future future = + MakeFuture(&future_data_.future_impl, future_handle); + if (!is_valid()) { + CompleteFuture(kAuthErrorInvalidUser, kUserNotInitializedErrorMessage, + future_handle, &future_data_, SignInResult()); + } else { + CompleteFuture(kAuthErrorInvalidParameter, + kUserNotInitializedErrorMessage, future_handle, + &future_data_, SignInResult()); + } + return future; } - return MakeFuture(&futures, handle); + return provider->Reauthenticate(auth_data_, this); } -Future User::Delete() { - if (!ValidUser(auth_data_)) { - return Future(); +Future UserInternal::Delete() { + MutexLock user_info_lock(user_mutex_); + if (!is_valid()) { + return CreateAndCompleteFuture(kUserFn_Delete, kAuthErrorInvalidUser, + kUserNotInitializedErrorMessage, + &future_data_); } - ReferenceCountedFutureImpl& futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc(kUserFn_Delete); - JNIEnv* env = Env(auth_data_); - jobject pending_result = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kDelete)); + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc(kUserFn_Reload); + Future future = MakeFuture(&future_data_.future_impl, future_handle); + + JNIEnv *env = Env(auth_data_); + jobject pending_result = + env->CallObjectMethod(user_, user::GetMethodId(user::kDelete)); - if (!CheckAndCompleteFutureOnError(env, &futures, handle)) { - RegisterCallback(pending_result, handle, auth_data_, - [](jobject result, FutureCallbackData* d, - bool success, void* void_data) { + if (!CheckAndCompleteFutureOnError(env, &future_data_.future_impl, + future_handle)) { + RegisterCallback(auth_data_, pending_result, future_handle, future_api_id_, + &future_data_.future_impl, + [](jobject result, FutureCallbackData *d, + bool success, void *void_data) { if (success) { - UpdateCurrentUser(d->auth_data); + UpdateCurrentUser(Env(d->auth_data), d->auth_data); } }); env->DeleteLocalRef(pending_result); } - return MakeFuture(&futures, handle); + return future; } -UserMetadata User::metadata() const { - if (!ValidUser(auth_data_)) return UserMetadata(); +Future UserInternal::DeleteLastResult() const { + return static_cast &>( + future_data_.future_impl.LastResult(kUserFn_Delete)); +} - JNIEnv* env = Env(auth_data_); +UserMetadata UserInternal::metadata() const { + UserMetadata user_metadata; + if (!is_valid()) return user_metadata; - jobject userMetadata = env->CallObjectMethod( - UserImpl(auth_data_), user::GetMethodId(user::kGetMetadata)); + JNIEnv *env = Env(auth_data_); + jobject jUserMetadata = + env->CallObjectMethod(user_, user::GetMethodId(user::kGetMetadata)); util::CheckAndClearJniExceptions(env); - if (!userMetadata) return UserMetadata(); + if (!jUserMetadata) return user_metadata; - UserMetadata data; - data.last_sign_in_timestamp = env->CallLongMethod( - userMetadata, metadata::GetMethodId(metadata::kGetLastSignInTimestamp)); + user_metadata.last_sign_in_timestamp = env->CallLongMethod( + jUserMetadata, metadata::GetMethodId(metadata::kGetLastSignInTimestamp)); assert(env->ExceptionCheck() == false); - data.creation_timestamp = env->CallLongMethod( - userMetadata, metadata::GetMethodId(metadata::kGetCreationTimestamp)); + user_metadata.creation_timestamp = env->CallLongMethod( + jUserMetadata, metadata::GetMethodId(metadata::kGetCreationTimestamp)); assert(env->ExceptionCheck() == false); - env->DeleteLocalRef(userMetadata); + env->DeleteLocalRef(jUserMetadata); - return data; + return user_metadata; } -bool User::is_email_verified() const { - if (!ValidUser(auth_data_)) return false; +bool UserInternal::is_email_verified() const { + if (!is_valid()) return false; - JNIEnv* env = Env(auth_data_); + JNIEnv *env = Env(auth_data_); bool result = env->CallBooleanMethod( - UserImpl(auth_data_), userinfo::GetMethodId(userinfo::kIsEmailVerified)); + user_, userinfo::GetMethodId(userinfo::kIsEmailVerified)); util::CheckAndClearJniExceptions(env); return result; } -bool User::is_anonymous() const { - if (!ValidUser(auth_data_)) return false; +bool UserInternal::is_anonymous() const { + if (!is_valid()) return false; - JNIEnv* env = Env(auth_data_); - bool result = env->CallBooleanMethod(UserImpl(auth_data_), - user::GetMethodId(user::kIsAnonymous)); + JNIEnv *env = Env(auth_data_); + bool result = + env->CallBooleanMethod(user_, user::GetMethodId(user::kIsAnonymous)); util::CheckAndClearJniExceptions(env); return result; } +std::string UserInternal::uid() const { + if (!is_valid()) return ""; + JNIEnv *env = Env(auth_data_); + jobject property = + env->CallObjectMethod(user_, userinfo::GetMethodId(userinfo::kGetUid)); + if (firebase::util::CheckAndClearJniExceptions(env) || !property) { + return std::string(); + } + return JniStringToString(env, property); +} + +std::string UserInternal::email() const { + if (!is_valid()) return ""; + JNIEnv *env = Env(auth_data_); + jobject property = + env->CallObjectMethod(user_, userinfo::GetMethodId(userinfo::kGetEmail)); + if (firebase::util::CheckAndClearJniExceptions(env) || !property) { + return std::string(); + } + return JniStringToString(env, property); +} + +std::string UserInternal::display_name() const { + if (!is_valid()) return ""; + JNIEnv *env = Env(auth_data_); + jobject property = env->CallObjectMethod( + user_, userinfo::GetMethodId(userinfo::kGetDisplayName)); + if (firebase::util::CheckAndClearJniExceptions(env) || !property) { + return std::string(); + } + return JniStringToString(env, property); +} + +std::string UserInternal::photo_url() const { + if (!is_valid()) return ""; + JNIEnv *env = Env(auth_data_); + jobject property = env->CallObjectMethod( + user_, userinfo::GetMethodId(userinfo::kGetPhotoUrl)); + if (firebase::util::CheckAndClearJniExceptions(env) || !property) { + return std::string(); + } + return JniUriToString(env, property); +} + +std::string UserInternal::provider_id() const { + if (!is_valid()) return ""; + JNIEnv *env = Env(auth_data_); + jobject property = env->CallObjectMethod( + user_, userinfo::GetMethodId(userinfo::kGetProviderId)); + if (firebase::util::CheckAndClearJniExceptions(env) || !property) { + return std::string(); + } + return JniStringToString(env, property); +} + +std::string UserInternal::phone_number() const { + if (!is_valid()) return ""; + JNIEnv *env = Env(auth_data_); + jobject property = env->CallObjectMethod( + user_, userinfo::GetMethodId(userinfo::kGetPhoneNumber)); + if (firebase::util::CheckAndClearJniExceptions(env) || !property) { + return std::string(); + } + return JniStringToString(env, property); +} + } // namespace auth } // namespace firebase diff --git a/auth/src/auth.cc b/auth/src/auth.cc index a7bd564e53..40680df32c 100644 --- a/auth/src/auth.cc +++ b/auth/src/auth.cc @@ -134,48 +134,55 @@ Auth::Auth(App* app, void* auth_impl) : auth_data_(new AuthData) { } void Auth::DeleteInternal() { - MutexLock lock(*g_auths_mutex); + // Mutex is contained within the auth_data_ object. Retain a pointer to it so + // that we can clean up its contents and then delete it after we leave the + // mutex. + AuthData* retained_auth_impl = auth_data_; + { + MutexLock(auth_data_->auth_mutex); + MutexLock lock(*g_auths_mutex); - if (!auth_data_) return; + if (!auth_data_) return; - { - MutexLock destructing_lock(auth_data_->desctruting_mutex); - auth_data_->destructing = true; - } + { + MutexLock destructing_lock(auth_data_->destructing_mutex); + auth_data_->destructing = true; + } - CleanupNotifier* notifier = CleanupNotifier::FindByOwner(auth_data_->app); - assert(notifier); - notifier->UnregisterObject(this); + CleanupNotifier* notifier = CleanupNotifier::FindByOwner(auth_data_->app); + assert(notifier); + notifier->UnregisterObject(this); - int num_auths_remaining = 0; - { - // Remove `this` from the g_auths map. - // The mapping is 1:1, so we should only ever delete one. - for (auto it = g_auths.begin(); it != g_auths.end(); ++it) { - if (it->second == this) { - LogDebug("Deleting Auth %p for App %p", this, it->first); - g_auths.erase(it); - break; + int num_auths_remaining = 0; + { + // Remove `this` from the g_auths map. + // The mapping is 1:1, so we should only ever delete one. + for (auto it = g_auths.begin(); it != g_auths.end(); ++it) { + if (it->second == this) { + LogDebug("Deleting Auth %p for App %p", this, it->first); + g_auths.erase(it); + break; + } } - } - num_auths_remaining = g_auths.size(); - } + num_auths_remaining = g_auths.size(); + } - auth_data_->ClearListeners(); + auth_data_->ClearListeners(); - // If this is the last Auth instance to be cleaned up, also clean up data for - // Credentials. - if (num_auths_remaining == 0) { - CleanupCredentialFutureImpl(); - } + // If this is the last Auth instance to be cleaned up, also clean up data + // for Credentials. + if (num_auths_remaining == 0) { + CleanupCredentialFutureImpl(); + } - // Destroy the platform-specific object. - DestroyPlatformAuth(auth_data_); + // Destroy the platform-specific object. + DestroyPlatformAuth(auth_data_); + auth_data_ = nullptr; + } // Delete the pimpl data. - delete auth_data_; - auth_data_ = nullptr; + delete retained_auth_impl; } Auth::~Auth() { DeleteInternal(); } @@ -183,6 +190,7 @@ Auth::~Auth() { DeleteInternal(); } // Always non-nullptr since set in constructor. App& Auth::app() { FIREBASE_ASSERT(auth_data_ != nullptr); + MutexLock(auth_data_->auth_mutex); return *auth_data_->app; } @@ -217,6 +225,7 @@ static bool AddListener(T listener, std::vector* listener_vector, Auth* auth, void Auth::AddAuthStateListener(AuthStateListener* listener) { if (!auth_data_) return; + MutexLock(auth_data_->auth_mutex); // Would have to lock mutex till the method ends to protect on race // conditions. MutexLock lock(auth_data_->listeners_mutex); @@ -235,6 +244,7 @@ void Auth::AddAuthStateListener(AuthStateListener* listener) { void Auth::AddIdTokenListener(IdTokenListener* listener) { if (!auth_data_) return; + MutexLock(auth_data_->auth_mutex); // Would have to lock mutex till the method ends to protect on race // conditions. MutexLock lock(auth_data_->listeners_mutex); @@ -289,12 +299,14 @@ static void RemoveListener(T listener, std::vector* listener_vector, void Auth::RemoveAuthStateListener(AuthStateListener* listener) { if (!auth_data_) return; + MutexLock(auth_data_->auth_mutex); RemoveListener(listener, &auth_data_->listeners, this, &listener->auths_, &auth_data_->listeners_mutex); } void Auth::RemoveIdTokenListener(IdTokenListener* listener) { if (!auth_data_) return; + MutexLock(auth_data_->auth_mutex); int listener_count = auth_data_->id_token_listeners.size(); RemoveListener(listener, &auth_data_->id_token_listeners, this, &listener->auths_, &auth_data_->listeners_mutex); diff --git a/auth/src/common.cc b/auth/src/common.cc index 510d1f82c0..165c8c6cfd 100644 --- a/auth/src/common.cc +++ b/auth/src/common.cc @@ -22,9 +22,15 @@ namespace firebase { namespace auth { const char* kUserNotInitializedErrorMessage = - "Operation attmpted on an invalid User object."; + "Operation attempted on an invalid User object."; const char* kPhoneAuthNotSupportedErrorMessage = "Phone Auth is not supported on this platform."; +const char* kAuthInvalidParameterErrorMessage = + "A parameter pass to the auth method is null or invalid."; +extern const char* kInvalidCredentialErrorMessage = + "The provided credential does not match the required type."; +extern const char* kErrorEmptyEmailPasswordErrorMessage = + "Empty email or password are not allowed."; // static member variables const uint32_t PhoneAuthProvider::kMaxTimeoutMs = 3000; diff --git a/auth/src/common.h b/auth/src/common.h index 80674ab2bd..0079c9eb61 100644 --- a/auth/src/common.h +++ b/auth/src/common.h @@ -28,6 +28,9 @@ namespace auth { // the AdErrorCode enumeration in the C++ API. extern const char* kUserNotInitializedErrorMessage; extern const char* kPhoneAuthNotSupportedErrorMessage; +extern const char* kAuthInvalidParameterErrorMessage; +extern const char* kInvalidCredentialErrorMessage; +extern const char* kErrorEmptyEmailPasswordErrorMessage; // Enumeration for Credential API functions that return a Future. // This allows us to hold a Future for the most recent call to that API. @@ -46,12 +49,6 @@ struct FutureData { ReferenceCountedFutureImpl future_impl; }; -template -struct FutureCallbackData { - FutureData* future_data; - SafeFutureHandle future_handle; -}; - // Create a future and update the corresponding last result. template SafeFutureHandle CreateFuture(int fn_idx, FutureData* future_data) { @@ -131,9 +128,6 @@ void NotifyAuthStateListeners(AuthData* auth_data); // Notify all the listeners of the ID token change. void NotifyIdTokenListeners(AuthData* auth_data); -// Synchronize the current user. -void UpdateCurrentUser(AuthData* auth_data); - // Get a FutureImpl to use for Credential methods that return Futures. ReferenceCountedFutureImpl* GetCredentialFutureImpl(); diff --git a/auth/src/data.h b/auth/src/data.h index 54a09264c7..529355f951 100644 --- a/auth/src/data.h +++ b/auth/src/data.h @@ -39,6 +39,9 @@ struct AuthDataDeprecatedFields { // pointer the platform specific user object, which is updated on User // operations. UserInternal* user_internal_deprecated; + + // JNI reference to the user object in the Firebase Android SDK. + void* android_user_impl; }; // Enumeration for API functions that return a Future. @@ -195,7 +198,10 @@ struct AuthData { bool destructing; // Mutex protecting destructing - Mutex desctruting_mutex; + Mutex destructing_mutex; + + // Mutex guarding the auth object for standard API operations. + Mutex auth_mutex; // Sets if the Id Token Listener is expecting a callback. // Used to workaround an issue where the Id Token is not reset with a new one, diff --git a/auth/src/desktop/user_desktop.cc b/auth/src/desktop/user_desktop.cc index 742875103b..25d15127e2 100644 --- a/auth/src/desktop/user_desktop.cc +++ b/auth/src/desktop/user_desktop.cc @@ -511,7 +511,7 @@ void AssignLoadedData(const Future& future, AuthData* auth_data) { void HandleLoadedData(const Future& future, void* auth_data) { auto cast_auth_data = static_cast(auth_data); - MutexLock destructing_lock(cast_auth_data->desctruting_mutex); + MutexLock destructing_lock(cast_auth_data->destructing_mutex); if (cast_auth_data->destructing) { // If auth is destructing, abort. return; diff --git a/auth/src/include/firebase/auth/types.h b/auth/src/include/firebase/auth/types.h index 1627e37f8a..4c0fe9c0be 100644 --- a/auth/src/include/firebase/auth/types.h +++ b/auth/src/include/firebase/auth/types.h @@ -429,8 +429,11 @@ enum AuthError { #endif // INTERNAL_EXEPERIMENTAL - /// An operation was attempted on an invalid User. + /// Indicates that an operation was attempted on an invalid User. kAuthErrorInvalidUser, + + /// Indicates that an invalid parameter was passed to an auth method. + kAuthErrorInvalidParameter, }; /// @brief Contains information required to authenticate with a third party diff --git a/auth/src/include/firebase/auth/user.h b/auth/src/include/firebase/auth/user.h index 719446266b..5278c7d593 100644 --- a/auth/src/include/firebase/auth/user.h +++ b/auth/src/include/firebase/auth/user.h @@ -386,7 +386,7 @@ class User : public UserInfoInterface { /// platforms. On other platforms this method will return a Future with a /// preset error code: kAuthErrorUnimplemented. FIREBASE_DEPRECATED Future LinkWithProvider_DEPRECATED( - FederatedAuthProvider* provider) const; + FederatedAuthProvider* provider); /// @deprecated This is a deprecated method. Please use @ref Unlink(const /// char*) instead. @@ -523,11 +523,6 @@ class User : public UserInfoInterface { virtual std::string phone_number() const; private: - // @deprecated User references to auth_data should only be needed during - // the Google I/O 23 breaking changes deprecation window. - // - // Internal only. - // // Constructor of an internal opaque type. Memory ownership of UserInternal // passes to to this User object. User(AuthData* auth_data, UserInternal* user_internal); diff --git a/auth/src/ios/auth_ios.mm b/auth/src/ios/auth_ios.mm index 428f3ab80b..af60ff8ac0 100644 --- a/auth/src/ios/auth_ios.mm +++ b/auth/src/ios/auth_ios.mm @@ -149,7 +149,7 @@ explicit ListenerHandleHolder(T handle) : handle(handle) {} // Grab the user value from the iOS API and remember it locally. void UpdateCurrentUser(AuthData *auth_data) { - MutexLock lock(auth_data->future_impl.mutex()); + MutexLock(auth_data->auth_mutex); FIRUser *user = [AuthImpl(auth_data) currentUser]; SetUserImpl(auth_data, user); } @@ -202,6 +202,7 @@ void UpdateCurrentUser(AuthData *auth_data) { // Platform-specific method to destroy the wrapped Auth class. void Auth::DestroyPlatformAuth(AuthData *auth_data) { + // Note: auth_data->auth_mutex is already locked by Auth::DeleteInternal(). // Remove references from listener blocks. AuthDataIos *auth_data_ios = reinterpret_cast(auth_data->auth_impl); FIRCPPAuthListenerHandle *listener_cpp_handle = auth_data_ios->listener_handle.get(); @@ -243,18 +244,19 @@ void LogHeartbeat(Auth *auth) { } Future Auth::FetchProvidersForEmail(const char *email) { + MutexLock(auth_data_->auth_mutex); // Create data structure to hold asynchronous results. FetchProvidersResult initial_data; - ReferenceCountedFutureImpl &futures = auth_data_->future_impl; - const auto handle = - futures.SafeAlloc(kAuthFn_FetchProvidersForEmail, initial_data); - + ReferenceCountedFutureImpl &future_impl = auth_data_->future_impl; + const auto future_handle = + future_impl.SafeAlloc(kAuthFn_FetchProvidersForEmail, initial_data); + Future future = MakeFuture(&future_impl, future_handle); [AuthImpl(auth_data_) fetchSignInMethodsForEmail:@(email) completion:^(NSArray *_Nullable providers, NSError *_Nullable error) { - futures.Complete( - handle, AuthErrorFromNSError(error), + future_impl.Complete( + future_handle, AuthErrorFromNSError(error), [error.localizedDescription UTF8String], [providers](FetchProvidersResult *data) { // Copy data to our result format. @@ -265,8 +267,7 @@ void LogHeartbeat(Auth *auth) { } }); }]; - - return MakeFuture(&futures, handle); + return future; } // Support the deprecated current_user method by returning a pointer to the @@ -275,7 +276,7 @@ void LogHeartbeat(Auth *auth) { // window. User *Auth::current_user_DEPRECATED() { if (!auth_data_) return nullptr; - MutexLock lock(auth_data_->future_impl.mutex()); + MutexLock(auth_data_->auth_mutex); if (auth_data_->deprecated_fields.user_deprecated == nullptr || !auth_data_->deprecated_fields.user_deprecated->is_valid()) { return nullptr; @@ -286,7 +287,7 @@ void LogHeartbeat(Auth *auth) { static User *AssignUser(FIRUser *_Nullable user, AuthData *auth_data) { // Update our pointer to the iOS user that we're wrapping. - MutexLock lock(auth_data->future_impl.mutex()); + MutexLock(auth_data->auth_mutex); if (user) { SetUserImpl(auth_data, user); } @@ -296,6 +297,7 @@ void LogHeartbeat(Auth *auth) { std::string Auth::language_code() const { if (!auth_data_) return ""; + MutexLock(auth_data_->auth_mutex); NSString *language_code = [AuthImpl(auth_data_) languageCode]; if (language_code == nil) { return std::string(); @@ -306,6 +308,7 @@ void LogHeartbeat(Auth *auth) { void Auth::set_language_code(const char *language_code) { if (!auth_data_) return; + MutexLock(auth_data_->auth_mutex); NSString *code; if (language_code != nullptr) { code = [NSString stringWithUTF8String:language_code]; @@ -315,6 +318,7 @@ void LogHeartbeat(Auth *auth) { void Auth::UseAppLanguage() { if (!auth_data_) return; + MutexLock(auth_data_->auth_mutex); [AuthImpl(auth_data_) useAppLanguage]; } @@ -329,13 +333,13 @@ AuthError AuthErrorFromNSError(NSError *_Nullable error) { } void SignInCallback(FIRUser *_Nullable user, NSError *_Nullable error, - SafeFutureHandle handle, ReferenceCountedFutureImpl &future_impl, + SafeFutureHandle future_handle, ReferenceCountedFutureImpl &future_impl, AuthData *auth_data) { User *result = AssignUser(user, auth_data); - if (future_impl.ValidFuture(handle)) { + if (future_impl.ValidFuture(future_handle)) { // Finish off the asynchronous call so that the caller can read it. - future_impl.CompleteWithResult(handle, AuthErrorFromNSError(error), + future_impl.CompleteWithResult(future_handle, AuthErrorFromNSError(error), util::NSStringToString(error.localizedDescription).c_str(), result); } @@ -343,16 +347,16 @@ void SignInCallback(FIRUser *_Nullable user, NSError *_Nullable error, void SignInResultWithProviderCallback( FIRAuthDataResult *_Nullable auth_result, NSError *_Nullable error, - SafeFutureHandle handle, ReferenceCountedFutureImpl &future_impl, + SafeFutureHandle future_handle, ReferenceCountedFutureImpl &future_impl, AuthData *_Nonnull auth_data, const FIROAuthProvider *_Nonnull ios_auth_provider /*unused */) { // ios_auth_provider exists as a parameter to hold a reference to the FIROAuthProvider preventing // its release by the Objective-C runtime during the asynchronous SignIn operation. error = RemapBadProviderIDErrors(error); - SignInResultCallback(auth_result, error, handle, future_impl, auth_data); + SignInResultCallback(auth_result, error, future_handle, future_impl, auth_data); } void SignInResultCallback(FIRAuthDataResult *_Nullable auth_result, NSError *_Nullable error, - SafeFutureHandle handle, + SafeFutureHandle future_handle, ReferenceCountedFutureImpl &future_impl, AuthData *auth_data) { User *user = AssignUser(auth_result.user, auth_data); @@ -373,52 +377,56 @@ void SignInResultCallback(FIRAuthDataResult *_Nullable auth_result, NSError *_Nu } } - if (future_impl.ValidFuture(handle)) { - future_impl.CompleteWithResult(handle, AuthErrorFromNSError(error), + if (future_impl.ValidFuture(future_handle)) { + future_impl.CompleteWithResult(future_handle, AuthErrorFromNSError(error), util::NSStringToString(error.localizedDescription).c_str(), result); } } Future Auth::SignInWithCustomToken_DEPRECATED(const char *token) { - ReferenceCountedFutureImpl &futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc(kAuthFn_SignInWithCustomToken_DEPRECATED, nullptr); - + MutexLock(auth_data_->auth_mutex); + ReferenceCountedFutureImpl &future_impl = auth_data_->future_impl; + const auto future_handle = + future_impl.SafeAlloc(kAuthFn_SignInWithCustomToken_DEPRECATED, nullptr); + Future future = MakeFuture(&future_impl, future_handle); [AuthImpl(auth_data_) signInWithCustomToken:@(token) completion:^(FIRAuthDataResult *_Nullable auth_result, NSError *_Nullable error) { - SignInCallback(auth_result.user, error, handle, futures, auth_data_); + SignInCallback(auth_result.user, error, future_handle, future_impl, auth_data_); }]; - return MakeFuture(&futures, handle); + return future; } Future Auth::SignInWithCredential_DEPRECATED(const Credential &credential) { - ReferenceCountedFutureImpl &futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc(kAuthFn_SignInWithCredential_DEPRECATED, nullptr); - + MutexLock(auth_data_->auth_mutex); + ReferenceCountedFutureImpl &future_impl = auth_data_->future_impl; + const auto future_handle = + future_impl.SafeAlloc(kAuthFn_SignInWithCredential_DEPRECATED, nullptr); + Future future = MakeFuture(&future_impl, future_handle); [AuthImpl(auth_data_) signInWithCredential:CredentialFromImpl(credential.impl_) completion:^(FIRAuthDataResult *_Nullable auth_result, NSError *_Nullable error) { - SignInCallback(auth_result.user, error, handle, futures, auth_data_); + SignInCallback(auth_result.user, error, future_handle, future_impl, auth_data_); }]; - return MakeFuture(&futures, handle); + return future; } Future Auth::SignInAndRetrieveDataWithCredential_DEPRECATED( const Credential &credential) { - ReferenceCountedFutureImpl &futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc( + MutexLock(auth_data_->auth_mutex); + ReferenceCountedFutureImpl &future_impl = auth_data_->future_impl; + const auto future_handle = future_impl.SafeAlloc( kAuthFn_SignInAndRetrieveDataWithCredential_DEPRECATED, SignInResult()); - + Future future = MakeFuture(&future_impl, future_handle); [AuthImpl(auth_data_) signInWithCredential:CredentialFromImpl(credential.impl_) completion:^(FIRAuthDataResult *_Nullable auth_result, NSError *_Nullable error) { - SignInResultCallback(auth_result, error, handle, futures, auth_data_); + SignInResultCallback(auth_result, error, future_handle, future_impl, auth_data_); }]; - - return MakeFuture(&futures, handle); + return future; } Future Auth::SignInWithProvider_DEPRECATED(FederatedAuthProvider *provider) { @@ -427,59 +435,67 @@ void SignInResultCallback(FIRAuthDataResult *_Nullable auth_result, NSError *_Nu } Future Auth::SignInAnonymously_DEPRECATED() { - ReferenceCountedFutureImpl &futures = auth_data_->future_impl; - const auto handle = + MutexLock(auth_data_->auth_mutex); + ReferenceCountedFutureImpl &future_impl = auth_data_->future_impl; + const auto future_handle = auth_data_->future_impl.SafeAlloc(kAuthFn_SignInAnonymously_DEPRECATED, nullptr); - + Future future = MakeFuture(&future_impl, future_handle); [AuthImpl(auth_data_) signInAnonymouslyWithCompletion:^(FIRAuthDataResult *_Nullable auth_result, NSError *_Nullable error) { - SignInCallback(auth_result.user, error, handle, futures, auth_data_); + SignInCallback(auth_result.user, error, future_handle, future_impl, auth_data_); }]; - - return MakeFuture(&futures, handle); + return future; } Future Auth::SignInWithEmailAndPassword_DEPRECATED(const char *email, const char *password) { - ReferenceCountedFutureImpl &futures = auth_data_->future_impl; - const auto handle = - futures.SafeAlloc(kAuthFn_SignInWithEmailAndPassword_DEPRECATED, nullptr); + MutexLock(auth_data_->auth_mutex); + ReferenceCountedFutureImpl &future_impl = auth_data_->future_impl; + const auto future_handle = + future_impl.SafeAlloc(kAuthFn_SignInWithEmailAndPassword_DEPRECATED, nullptr); + Future future = MakeFuture(&future_impl, future_handle); if (!email || strlen(email) == 0) { - futures.Complete(handle, kAuthErrorMissingEmail, "Empty email is not allowed."); + future_impl.Complete(future_handle, kAuthErrorMissingEmail, "Empty email is not allowed."); } else if (!password || strlen(password) == 0) { - futures.Complete(handle, kAuthErrorMissingPassword, "Empty password is not allowed."); + future_impl.Complete(future_handle, kAuthErrorMissingPassword, + "Empty password is not allowed."); } else { [AuthImpl(auth_data_) signInWithEmail:@(email) password:@(password) completion:^(FIRAuthDataResult *_Nullable auth_result, NSError *_Nullable error) { - SignInCallback(auth_result.user, error, handle, futures, auth_data_); + SignInCallback(auth_result.user, error, future_handle, future_impl, auth_data_); }]; } - return MakeFuture(&futures, handle); + return future; } Future Auth::CreateUserWithEmailAndPassword_DEPRECATED(const char *email, const char *password) { - ReferenceCountedFutureImpl &futures = auth_data_->future_impl; - const auto handle = - futures.SafeAlloc(kAuthFn_CreateUserWithEmailAndPassword_DEPRECATED, nullptr); + MutexLock(auth_data_->auth_mutex); + ReferenceCountedFutureImpl &future_impl = auth_data_->future_impl; + const auto future_handle = + future_impl.SafeAlloc(kAuthFn_CreateUserWithEmailAndPassword_DEPRECATED, nullptr); + Future future = MakeFuture(&future_impl, future_handle); if (!email || strlen(email) == 0) { - futures.Complete(handle, kAuthErrorMissingEmail, "Empty email is not allowed."); + future_impl.Complete(future_handle, kAuthErrorMissingEmail, + kErrorEmptyEmailPasswordErrorMessage); } else if (!password || strlen(password) == 0) { - futures.Complete(handle, kAuthErrorMissingPassword, "Empty password is not allowed."); + future_impl.Complete(future_handle, kAuthErrorMissingPassword, + kErrorEmptyEmailPasswordErrorMessage); } else { [AuthImpl(auth_data_) createUserWithEmail:@(email) password:@(password) completion:^(FIRAuthDataResult *_Nullable auth_result, NSError *_Nullable error) { - SignInCallback(auth_result.user, error, handle, futures, auth_data_); + SignInCallback(auth_result.user, error, future_handle, future_impl, auth_data_); }]; } - return MakeFuture(&futures, handle); + return future; } void Auth::SignOut() { + MutexLock(auth_data_->auth_mutex); // TODO(jsanmiya): Verify with iOS team why this returns an error. NSError *_Nullable error; [AuthImpl(auth_data_) signOut:&error]; @@ -487,16 +503,17 @@ void SignInResultCallback(FIRAuthDataResult *_Nullable auth_result, NSError *_Nu } Future Auth::SendPasswordResetEmail(const char *email) { - ReferenceCountedFutureImpl &futures = auth_data_->future_impl; - const auto handle = futures.SafeAlloc(kAuthFn_SendPasswordResetEmail); - - [AuthImpl(auth_data_) sendPasswordResetWithEmail:@(email) - completion:^(NSError *_Nullable error) { - futures.Complete(handle, AuthErrorFromNSError(error), - [error.localizedDescription UTF8String]); - }]; - - return MakeFuture(&futures, handle); + MutexLock(auth_data_->auth_mutex); + ReferenceCountedFutureImpl &future_impl = auth_data_->future_impl; + const auto future_handle = future_impl.SafeAlloc(kAuthFn_SendPasswordResetEmail); + Future future = MakeFuture(&future_impl, future_handle); + [AuthImpl(auth_data_) + sendPasswordResetWithEmail:@(email) + completion:^(NSError *_Nullable error) { + future_impl.Complete(future_handle, AuthErrorFromNSError(error), + [error.localizedDescription UTF8String]); + }]; + return future; } // Remap iOS SDK errors reported by the UIDelegate. While these errors seem like diff --git a/auth/src/ios/common_ios.h b/auth/src/ios/common_ios.h index f5d8809752..4f9f655218 100644 --- a/auth/src/ios/common_ios.h +++ b/auth/src/ios/common_ios.h @@ -48,12 +48,23 @@ OBJ_C_PTR_WRAPPER(FIRAuthCredential); OBJ_C_PTR_WRAPPER(FIRUser); OBJ_C_PTR_WRAPPER(FIRCPPAuthListenerHandle); +// Synchronize the current user. +void UpdateCurrentUser(AuthData *auth_data); + // Auth implementation on iOS. struct AuthDataIos { FIRAuthPointer fir_auth; FIRCPPAuthListenerHandlePointer listener_handle; }; +// Struct to contain the data required to complete +// futures asynchronously on iOS. +template +struct FutureCallbackData { + FutureData *future_data; + SafeFutureHandle future_handle; +}; + // Contains the interface between the public API and the underlying // Obj-C SDK FirebaseUser implemention. class UserInternal { @@ -152,14 +163,12 @@ class UserInternal { // Used to support older method invocation of provider_data_DEPRECATED(). std::vector user_infos_; - // Guard the creation and deletion of the vector of UserInfoInterface* - // allocations in provider_data_DEPRECATED(). - Mutex user_info_mutex_deprecated_; - // Guard against changes to the user_ object. Mutex user_mutex_; }; +/// @deprecated +/// /// Replace the platform-dependent FIRUser object. /// Note: this function is only used to support DEPRECATED methods which return User*. This /// functionality should be removed when those deprecated methods are removed. diff --git a/auth/src/ios/credential_ios.mm b/auth/src/ios/credential_ios.mm index 8a6daaaba7..5fbade9c73 100644 --- a/auth/src/ios/credential_ios.mm +++ b/auth/src/ios/credential_ios.mm @@ -170,10 +170,12 @@ @implementation PhoneListenerDataObjC // static Future GameCenterAuthProvider::GetCredential() { - auto future_api = GetCredentialFutureImpl(); - FIREBASE_ASSERT(future_api != nullptr); + ReferenceCountedFutureImpl* future_impl = GetCredentialFutureImpl(); + FIREBASE_ASSERT(future_impl != nullptr); + const auto future_handle = + future_impl->SafeAlloc(kCredentialFn_GameCenterGetCredential); + Future future = MakeFuture(future_impl, future_handle); - const auto handle = future_api->SafeAlloc(kCredentialFn_GameCenterGetCredential); /** Linking GameKit.framework without using it on macOS results in App Store rejection. Thus we don't link GameKit.framework to our SDK directly. `optionalLocalPlayer` is used for @@ -184,20 +186,19 @@ @implementation PhoneListenerDataObjC // Early-out if GameKit is not linked if (!optionalLocalPlayer) { - future_api->Complete(handle, kAuthErrorInvalidCredential, - "GameCenter authentication is unavailable - missing GameKit capability."); - return MakeFuture(future_api, handle); + future_impl->Complete(future_handle, kAuthErrorInvalidCredential, + "GameCenter authentication is unavailable - missing GameKit capability."); + } else { + [FIRGameCenterAuthProvider + getCredentialWithCompletion:^(FIRAuthCredential* _Nullable credential, + NSError* _Nullable error) { + Credential result(new FIRAuthCredentialPointer(credential)); + future_impl->CompleteWithResult( + future_handle, AuthErrorFromNSError(error), + util::NSStringToString(error.localizedDescription).c_str(), result); + }]; } - - [FIRGameCenterAuthProvider getCredentialWithCompletion:^(FIRAuthCredential* _Nullable credential, - NSError* _Nullable error) { - Credential result(new FIRAuthCredentialPointer(credential)); - future_api->CompleteWithResult(handle, AuthErrorFromNSError(error), - util::NSStringToString(error.localizedDescription).c_str(), - result); - }]; - - return MakeFuture(future_api, handle); + return future; } // static @@ -407,19 +408,19 @@ explicit PhoneAuthProviderData(FIRPhoneAuthProvider* objc_provider) // current API. void LinkWithProviderGetCredentialCallback(FIRAuthCredential* _Nullable credential, NSError* _Nullable error, - SafeFutureHandle handle, + SafeFutureHandle future_handle, ReferenceCountedFutureImpl& future_impl, AuthData* auth_data, FIRUser* user, const FIROAuthProvider* ios_auth_provider) { if (error && error.code != 0) { error = RemapBadProviderIDErrors(error); - future_impl.CompleteWithResult(handle, AuthErrorFromNSError(error), + future_impl.CompleteWithResult(future_handle, AuthErrorFromNSError(error), util::NSStringToString(error.localizedDescription).c_str(), SignInResult()); } else { [user linkWithCredential:credential completion:^(FIRAuthDataResult* _Nullable auth_result, NSError* _Nullable error) { - SignInResultWithProviderCallback(auth_result, error, handle, future_impl, + SignInResultWithProviderCallback(auth_result, error, future_handle, future_impl, auth_data, ios_auth_provider); }]; } @@ -430,20 +431,20 @@ void LinkWithProviderGetCredentialCallback(FIRAuthCredential* _Nullable credenti // accessible via their current API. void ReauthenticateWithProviderGetCredentialCallback(FIRAuthCredential* _Nullable credential, NSError* _Nullable error, - SafeFutureHandle handle, + SafeFutureHandle future_handle, ReferenceCountedFutureImpl& future_impl, AuthData* auth_data, FIRUser* user, const FIROAuthProvider* ios_auth_provider) { if (error && error.code != 0) { error = RemapBadProviderIDErrors(error); - future_impl.CompleteWithResult(handle, AuthErrorFromNSError(error), + future_impl.CompleteWithResult(future_handle, AuthErrorFromNSError(error), util::NSStringToString(error.localizedDescription).c_str(), SignInResult()); } else { [user reauthenticateWithCredential:credential completion:^(FIRAuthDataResult* _Nullable auth_result, NSError* _Nullable error) { - SignInResultWithProviderCallback(auth_result, error, handle, + SignInResultWithProviderCallback(auth_result, error, future_handle, future_impl, auth_data, ios_auth_provider); }]; @@ -452,9 +453,11 @@ void ReauthenticateWithProviderGetCredentialCallback(FIRAuthCredential* _Nullabl Future FederatedOAuthProvider::SignIn(AuthData* auth_data) { assert(auth_data); - ReferenceCountedFutureImpl& futures = auth_data->future_impl; - const auto handle = - futures.SafeAlloc(kAuthFn_SignInWithProvider_DEPRECATED, SignInResult()); + ReferenceCountedFutureImpl& future_impl = auth_data->future_impl; + const auto future_handle = + future_impl.SafeAlloc(kAuthFn_SignInWithProvider_DEPRECATED, SignInResult()); + Future future = MakeFuture(&future_impl, future_handle); + FIROAuthProvider* ios_provider = (FIROAuthProvider*)[FIROAuthProvider providerWithProviderID:@(provider_data_.provider_id.c_str()) auth:AuthImpl(auth_data)]; @@ -465,24 +468,25 @@ void ReauthenticateWithProviderGetCredentialCallback(FIRAuthCredential* _Nullabl signInWithProvider:ios_provider UIDelegate:nullptr completion:^(FIRAuthDataResult* _Nullable auth_result, NSError* _Nullable error) { - SignInResultWithProviderCallback(auth_result, error, handle, futures, auth_data, - ios_provider); + SignInResultWithProviderCallback(auth_result, error, future_handle, future_impl, + auth_data, ios_provider); }]; - return MakeFuture(&futures, handle); } else { - Future future = MakeFuture(&futures, handle); - futures.CompleteWithResult(handle, kAuthErrorFailure, "Internal error constructing provider.", - SignInResult()); - return future; + future_impl.CompleteWithResult(future_handle, kAuthErrorFailure, + "Internal error constructing provider.", SignInResult()); } + return future; } Future FederatedOAuthProvider::Link(AuthData* auth_data, UserInternal* user_internal) { assert(auth_data); assert(user_internal); - auto handle = user_internal->future_data_.future_impl.SafeAlloc( - kUserFn_LinkWithProvider_DEPRECATED, SignInResult()); + ReferenceCountedFutureImpl& future_impl = user_internal->future_data_.future_impl; + auto future_handle = + future_impl.SafeAlloc(kUserFn_LinkWithProvider_DEPRECATED, SignInResult()); + Future future = MakeFuture(&future_impl, future_handle); + #if FIREBASE_PLATFORM_IOS FIROAuthProvider* ios_provider = (FIROAuthProvider*)[FIROAuthProvider providerWithProviderID:@(provider_data_.provider_id.c_str()) @@ -493,36 +497,36 @@ void ReauthenticateWithProviderGetCredentialCallback(FIRAuthCredential* _Nullabl FIRUser* user = user_internal->user_; // TODO(b/138788092) invoke FIRUser linkWithProvider instead, once that method is added to the // iOS SDK. - [ios_provider - getCredentialWithUIDelegate:nullptr - completion:^(FIRAuthCredential* _Nullable credential, - NSError* _Nullable error) { - LinkWithProviderGetCredentialCallback( - credential, error, handle, user_internal->future_data_.future_impl, - auth_data, user, ios_provider); - }]; - return MakeFuture(&user_internal->future_data_.future_impl, handle); + [ios_provider getCredentialWithUIDelegate:nullptr + completion:^(FIRAuthCredential* _Nullable credential, + NSError* _Nullable error) { + LinkWithProviderGetCredentialCallback( + credential, error, future_handle, + user_internal->future_data_.future_impl, auth_data, user, + ios_provider); + }]; } else { - Future future = MakeFuture(&user_internal->future_data_.future_impl, handle); - user_internal->future_data_.future_impl.CompleteWithResult( - handle, kAuthErrorFailure, "Internal error constructing provider.", SignInResult()); - return future; + future_impl.CompleteWithResult(future_handle, kAuthErrorFailure, + "Internal error constructing provider.", SignInResult()); } #else // non-iOS Apple platforms (eg: tvOS) - Future future = MakeFuture(&user_internal->future_data_.future_impl, handle); - user_internal->future_data_.future_impl.Complete( - handle, kAuthErrorApiNotAvailable, - "OAuth provider linking is not supported on non-iOS Apple platforms."); + future_impl.CompleteWithResult( + future_handle, kAuthErrorApiNotAvailable, + "OAuth provider linking is not supported on non-iOS Apple platforms.", SignInResult()); #endif // FIREBASE_PLATFORM_IOS + return future; } Future FederatedOAuthProvider::Reauthenticate(AuthData* auth_data, UserInternal* user_internal) { assert(auth_data); assert(user_internal); - auto handle = user_internal->future_data_.future_impl.SafeAlloc( - kUserFn_LinkWithProvider_DEPRECATED, SignInResult()); + ReferenceCountedFutureImpl& future_impl = user_internal->future_data_.future_impl; + auto future_handle = future_impl.SafeAlloc( + kUserFn_ReauthenticateWithProvider_DEPRECATED, SignInResult()); + Future future = MakeFuture(&future_impl, future_handle); + #if FIREBASE_PLATFORM_IOS FIROAuthProvider* ios_provider = (FIROAuthProvider*)[FIROAuthProvider providerWithProviderID:@(provider_data_.provider_id.c_str()) @@ -533,28 +537,23 @@ void ReauthenticateWithProviderGetCredentialCallback(FIRAuthCredential* _Nullabl FIRUser* user = user_internal->user_; // TODO(b/138788092) invoke FIRUser:reuthenticateWithProvider instead, once that method is added // to the iOS SDK. - [ios_provider - getCredentialWithUIDelegate:nullptr - completion:^(FIRAuthCredential* _Nullable credential, - NSError* _Nullable error) { - ReauthenticateWithProviderGetCredentialCallback( - credential, error, handle, user_internal->future_data_.future_impl, - auth_data, user, ios_provider); - }]; - return MakeFuture(&user_internal->future_data_.future_impl, handle); + [ios_provider getCredentialWithUIDelegate:nullptr + completion:^(FIRAuthCredential* _Nullable credential, + NSError* _Nullable error) { + ReauthenticateWithProviderGetCredentialCallback( + credential, error, future_handle, future_impl, auth_data, + user, ios_provider); + }]; } else { - Future future = MakeFuture(&user_internal->future_data_.future_impl, handle); - user_internal->future_data_.future_impl.CompleteWithResult( - handle, kAuthErrorFailure, "Internal error constructing provider.", SignInResult()); - return future; + future_impl.CompleteWithResult(future_handle, kAuthErrorFailure, + "Internal error constructing provider.", SignInResult()); } - #else // non-iOS Apple platforms (eg: tvOS) - Future future = MakeFuture(&user_internal->future_data_.future_impl, handle); - user_internal->future_data_.future_impl.Complete( - handle, kAuthErrorApiNotAvailable, - "OAuth reauthentication is not supported on non-iOS Apple platforms."); + future_impl.CompleteWithResult( + future_handle, kAuthErrorApiNotAvailable, + "OAuth reauthentication is not supported on non-iOS Apple platforms.", SignInResult()); #endif // FIREBASE_PLATFORM_IOS + return future; } } // namespace auth diff --git a/auth/src/ios/user_ios.mm b/auth/src/ios/user_ios.mm index 80d48c20c4..6882b3e6f8 100644 --- a/auth/src/ios/user_ios.mm +++ b/auth/src/ios/user_ios.mm @@ -36,6 +36,7 @@ /// User::User(AuthData *auth_data, UserInternal *user_internal) { assert(auth_data); + auth_data_ = auth_data; if (user_internal == nullptr) { // Create an invalid user internal. // This will return is_valid() false, and operations will fail. @@ -43,7 +44,18 @@ } else { user_internal_ = user_internal; } - auth_data_ = auth_data; +} + +User::User(const User &user) { + assert(user.auth_data_); + auth_data_ = user.auth_data_; + if (user.user_internal_ == nullptr) { + // Create an invalid user internal. + // This will return is_valid() false, and operations will fail. + user_internal_ = new UserInternal(nullptr); + } else { + user_internal_ = new UserInternal(user.user_internal_->user_); + } } User::~User() { @@ -171,7 +183,7 @@ return user_internal_->LinkAndRetrieveDataWithCredentialLastResult_DEPRECATED(); } -Future User::LinkWithProvider_DEPRECATED(FederatedAuthProvider *provider) const { +Future User::LinkWithProvider_DEPRECATED(FederatedAuthProvider *provider) { assert(user_internal_); return user_internal_->LinkWithProvider_DEPRECATED(auth_data_, provider); } @@ -290,15 +302,13 @@ explicit IOSWrappedUserInfo(id user_info) : user_info_(user_info) { : user_(user_internal.user_), future_data_(kUserFnCount) {} UserInternal::~UserInternal() { - user_ = nil; - { - MutexLock user_info_lock(user_info_mutex_deprecated_); - clear_user_infos(); - } + MutexLock user_info_lock(user_mutex_); // Make sure we don't have any pending futures in flight before we disappear. while (!future_data_.future_impl.IsSafeToDelete()) { internal::Sleep(100); } + user_ = nil; + clear_user_infos(); } void UserInternal::set_native_user_object_deprecated(FIRUser *user) { @@ -352,7 +362,7 @@ explicit IOSWrappedUserInfo(id user_info) : user_info_(user_info) { } const std::vector &UserInternal::provider_data_DEPRECATED() { - MutexLock user_info_lock(user_info_mutex_deprecated_); + MutexLock user_info_lock(user_mutex_); clear_user_infos(); if (is_valid()) { NSArray> *provider_data = user_.providerData; @@ -448,7 +458,6 @@ explicit IOSWrappedUserInfo(id user_info) : user_info_(user_info) { [error.localizedDescription UTF8String]); delete callback_data; }]; - return future; } @@ -474,7 +483,6 @@ explicit IOSWrappedUserInfo(id user_info) : user_info_(user_info) { [error.localizedDescription UTF8String]); delete callback_data; }]; - return future; } @@ -516,9 +524,8 @@ explicit IOSWrappedUserInfo(id user_info) : user_info_(user_info) { Future UserInternal::LinkAndRetrieveDataWithCredential_DEPRECATED( AuthData *auth_data, const Credential &credential) { - // MutexLock user_info_lock(user_mutex_); + MutexLock user_info_lock(user_mutex_); if (!is_valid()) { - printf("DEDB LinkAndRetrieveDataWithCredential_DEPRECATED user is not valid\n"); SafeFutureHandle future_handle = future_data_.future_impl.SafeAlloc( kUserFn_LinkAndRetrieveDataWithCredential_DEPRECATED); Future future = MakeFuture(&future_data_.future_impl, future_handle); @@ -534,11 +541,9 @@ explicit IOSWrappedUserInfo(id user_info) : user_info_(user_info) { [user_ linkWithCredential:CredentialFromImpl(credential.impl_) completion:^(FIRAuthDataResult *_Nullable auth_result, NSError *_Nullable error) { - NSLog(@"DEDB Completion auth_result: %p error: %p\n", auth_result, error); SignInResultCallback(auth_result, error, callback_data->future_handle, callback_data->future_data->future_impl, auth_data); - NSLog(@"DEDB Completion SignInResultCallback returned\n"); - // delete callback_data; + delete callback_data; }]; return future; } @@ -550,8 +555,23 @@ explicit IOSWrappedUserInfo(id user_info) : user_info_(user_info) { Future UserInternal::LinkWithProvider_DEPRECATED(AuthData *auth_data, FederatedAuthProvider *provider) { - FIREBASE_ASSERT_RETURN(Future(), provider); - return provider->Link(auth_data, this); + MutexLock user_info_lock(user_mutex_); + if (!is_valid() || provider == nullptr) { + SafeFutureHandle future_handle = + future_data_.future_impl.SafeAlloc(kUserFn_LinkWithProvider_DEPRECATED); + Future future = MakeFuture(&future_data_.future_impl, future_handle); + + if (!is_valid()) { + CompleteFuture(kAuthErrorInvalidUser, kUserNotInitializedErrorMessage, future_handle, + &future_data_, SignInResult()); + } else { + CompleteFuture(kAuthErrorInvalidParameter, kUserNotInitializedErrorMessage, future_handle, + &future_data_, SignInResult()); + } + return future; + } else { + return provider->Link(auth_data, this); + } } Future UserInternal::Unlink_DEPRECATED(AuthData *auth_data, const char *provider) { @@ -610,19 +630,17 @@ explicit IOSWrappedUserInfo(id user_info) : user_info_(user_info) { delete callback_data; }]; } else { - CompleteFuture(kAuthErrorInvalidCredential, kInvalidCredentialMessage, + CompleteFuture(kAuthErrorInvalidCredential, kInvalidCredentialErrorMessage, callback_data->future_handle, &future_data_, (User *)nullptr); } - return future; - #else // non iOS Apple platforms (eg: tvOS). SafeFutureHandle future_handle = future_data_.future_impl.SafeAlloc(kUserFn_UpdatePhoneNumberCredential_DEPRECATED); Future future = MakeFuture(&future_data_.future_impl, future_handle); CompleteFuture(kAuthErrorApiNotAvailable, kPhoneAuthNotSupportedErrorMessage, future_handle, &future_data_, (User *)nullptr); - return future; #endif // FIREBASE_PLATFORM_IOS + return future; } Future UserInternal::Reload() { @@ -711,7 +729,20 @@ explicit IOSWrappedUserInfo(id user_info) : user_info_(user_info) { Future UserInternal::ReauthenticateWithProvider_DEPRECATED( AuthData *auth_data, FederatedAuthProvider *provider) { - FIREBASE_ASSERT_RETURN(Future(), provider); + MutexLock user_info_lock(user_mutex_); + if (!is_valid() || provider == nullptr) { + SafeFutureHandle future_handle = future_data_.future_impl.SafeAlloc( + kUserFn_ReauthenticateWithProvider_DEPRECATED); + Future future = MakeFuture(&future_data_.future_impl, future_handle); + if (!is_valid()) { + CompleteFuture(kAuthErrorInvalidUser, kUserNotInitializedErrorMessage, future_handle, + &future_data_, SignInResult()); + } else { + CompleteFuture(kAuthErrorInvalidParameter, kUserNotInitializedErrorMessage, future_handle, + &future_data_, SignInResult()); + } + return future; + } return provider->Reauthenticate(auth_data, this); }