Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Firestore/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Unreleased
- [fixed] Fix crash while fetching Auth and App Check tokens. (#15281)

# 12.4.0
- [fixed] Implemented an internal workaround to fix
[CVE-2025-0838](https://nvd.nist.gov/vuln/detail/CVE-2025-0838). (#15300)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ namespace firestore {
namespace credentials {

class FirebaseAppCheckCredentialsProvider
: public CredentialsProvider<std::string, std::string> {
: public CredentialsProvider<std::string, std::string>,
public std::enable_shared_from_this<FirebaseAppCheckCredentialsProvider> {
public:
FirebaseAppCheckCredentialsProvider(FIRApp* app,
id<FIRAppCheckInterop> app_check);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,11 @@

void FirebaseAppCheckCredentialsProvider::GetToken(
TokenListener<std::string> completion) {
std::weak_ptr<Contents> weak_contents = contents_;
if (contents_->app_check) {
// Capture self via a shared_ptr to ensure that the credentials provider
// is not deallocated while the getToken request is outstanding.
std::shared_ptr<FirebaseAppCheckCredentialsProvider> self =
shared_from_this();
if (self->contents_->app_check) {
void (^get_token_callback)(id<FIRAppCheckTokenResultInterop>) =
^(id<FIRAppCheckTokenResultInterop> result) {
if (result.error != nil) {
Expand All @@ -90,13 +93,13 @@

// Retrieve a cached or generate a new FAC Token. If forcingRefresh == YES
// always generates a new token and updates the cache.
[contents_->app_check getTokenForcingRefresh:force_refresh_
completion:get_token_callback];
[self->contents_->app_check getTokenForcingRefresh:self->force_refresh_
completion:get_token_callback];
} else {
// If there's no AppCheck provider, call back immediately with a nil token.
completion(std::string{""});
}
force_refresh_ = false;
self->force_refresh_ = false;
}

void FirebaseAppCheckCredentialsProvider::SetCredentialChangeListener(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ namespace credentials {
* For non-Apple desktop build, this is right now just a stub.
*/
class FirebaseAuthCredentialsProvider
: public CredentialsProvider<AuthToken, User> {
: public CredentialsProvider<AuthToken, User>,
public std::enable_shared_from_this<FirebaseAuthCredentialsProvider> {
public:
// TODO(zxu123): Provide a ctor to accept the C++ Firebase Games App, which
// deals all platforms. Right now, only works for FIRApp*.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,26 +81,23 @@
// fail if there is a token change while the request is outstanding.
int initial_token_counter = contents_->token_counter;

std::weak_ptr<Contents> weak_contents = contents_;
// Capture self via a shared_ptr to ensure that the credentials provider
// is not deallocated while the getToken request is outstanding.
std::shared_ptr<FirebaseAuthCredentialsProvider> self = shared_from_this();
void (^get_token_callback)(NSString*, NSError*) =
^(NSString* _Nullable token, NSError* _Nullable error) {
std::shared_ptr<Contents> contents = weak_contents.lock();
if (!contents) {
return;
}

std::unique_lock<std::mutex> lock(contents->mutex);
if (initial_token_counter != contents->token_counter) {
std::unique_lock<std::mutex> lock(self->contents_->mutex);
if (initial_token_counter != self->contents_->token_counter) {
// Cancel the request since the user changed while the request was
// outstanding so the response is likely for a previous user (which
// user, we can't be sure).
LOG_DEBUG("GetToken aborted due to token change.");
return GetToken(completion);
return self->GetToken(completion);
} else {
if (error == nil) {
if (token != nil) {
completion(
AuthToken{util::MakeString(token), contents->current_user});
completion(AuthToken{util::MakeString(token),
self->contents_->current_user});
} else {
completion(AuthToken::Unauthenticated());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ - (nonnull NSString*)tokenDidChangeNotificationName {
auto token_promise = std::make_shared<std::promise<std::string>>();

FIRApp* app = testutil::AppForUnitTesting();
FirebaseAppCheckCredentialsProvider credentials_provider(app, nil);
credentials_provider.GetToken(
auto credentials_provider =
std::make_shared<FirebaseAppCheckCredentialsProvider>(app, nil);
credentials_provider->GetToken(
[token_promise](util::StatusOr<std::string> result) {
EXPECT_TRUE(result.ok());
const std::string& token = result.ValueOrDie();
Expand All @@ -115,8 +116,9 @@ - (nonnull NSString*)tokenDidChangeNotificationName {
FIRApp* app = testutil::AppForUnitTesting();
FSTAppCheckFake* app_check =
[[FSTAppCheckFake alloc] initWithToken:@"fake token"];
FirebaseAppCheckCredentialsProvider credentials_provider(app, app_check);
credentials_provider.GetToken([](util::StatusOr<std::string> result) {
auto credentials_provider =
std::make_shared<FirebaseAppCheckCredentialsProvider>(app, app_check);
credentials_provider->GetToken([](util::StatusOr<std::string> result) {
EXPECT_TRUE(result.ok());
const std::string& token = result.ValueOrDie();
EXPECT_EQ("fake token", token);
Expand All @@ -127,20 +129,22 @@ - (nonnull NSString*)tokenDidChangeNotificationName {
FIRApp* app = testutil::AppForUnitTesting();
FSTAppCheckFake* app_check =
[[FSTAppCheckFake alloc] initWithToken:@"fake token"];
FirebaseAppCheckCredentialsProvider credentials_provider(app, app_check);
credentials_provider.SetCredentialChangeListener(
auto credentials_provider =
std::make_shared<FirebaseAppCheckCredentialsProvider>(app, app_check);
credentials_provider->SetCredentialChangeListener(
[](std::string token) { EXPECT_EQ("", token); });

credentials_provider.SetCredentialChangeListener(nullptr);
credentials_provider->SetCredentialChangeListener(nullptr);
}

TEST(FirebaseAppCheckCredentialsProviderTest, InvalidateToken) {
FIRApp* app = testutil::AppForUnitTesting();
FSTAppCheckFake* app_check =
[[FSTAppCheckFake alloc] initWithToken:@"fake token"];
FirebaseAppCheckCredentialsProvider credentials_provider(app, app_check);
credentials_provider.InvalidateToken();
credentials_provider.GetToken(
auto credentials_provider =
std::make_shared<FirebaseAppCheckCredentialsProvider>(app, app_check);
credentials_provider->InvalidateToken();
credentials_provider->GetToken(
[&app_check](util::StatusOr<std::string> result) {
EXPECT_TRUE(result.ok());
EXPECT_TRUE(app_check.forceRefreshTriggered);
Expand All @@ -154,9 +158,10 @@ - (nonnull NSString*)tokenDidChangeNotificationName {

FIRApp* app = testutil::AppForUnitTesting();
FSTAppCheckFake* app_check = [[FSTAppCheckFake alloc] initWithToken:@""];
FirebaseAppCheckCredentialsProvider credentials_provider(app, app_check);
auto credentials_provider =
std::make_shared<FirebaseAppCheckCredentialsProvider>(app, app_check);

credentials_provider.SetCredentialChangeListener(
credentials_provider->SetCredentialChangeListener(
[token_promise](const std::string& result) {
if (result != "") {
token_promise->set_value(result);
Expand Down Expand Up @@ -186,9 +191,10 @@ - (nonnull NSString*)tokenDidChangeNotificationName {

FIRApp* app = testutil::AppForUnitTesting();
FSTAppCheckFake* app_check = [[FSTAppCheckFake alloc] initWithToken:@""];
FirebaseAppCheckCredentialsProvider credentials_provider(app, app_check);
auto credentials_provider =
std::make_shared<FirebaseAppCheckCredentialsProvider>(app, app_check);

credentials_provider.SetCredentialChangeListener(
credentials_provider->SetCredentialChangeListener(
[token_promise](const std::string& result) {
if (result != "") {
token_promise->set_value(result);
Expand Down Expand Up @@ -227,9 +233,10 @@ - (nonnull NSString*)tokenDidChangeNotificationName {

FIRApp* app = testutil::AppForUnitTesting();
FSTAppCheckFake* app_check = [[FSTAppCheckFake alloc] initWithToken:@""];
FirebaseAppCheckCredentialsProvider credentials_provider(app, app_check);
auto credentials_provider =
std::make_shared<FirebaseAppCheckCredentialsProvider>(app, app_check);

credentials_provider.SetCredentialChangeListener(
credentials_provider->SetCredentialChangeListener(
[token_promise](const std::string& result) {
if (result != "") {
token_promise->set_value(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
Expand Down Expand Up @@ -75,8 +76,9 @@ - (void)getTokenForcingRefresh:(BOOL)forceRefresh
auto token_promise = std::make_shared<std::promise<AuthToken>>();

FIRApp* app = testutil::AppForUnitTesting();
FirebaseAuthCredentialsProvider credentials_provider(app, nil);
credentials_provider.GetToken(
auto credentials_provider =
std::make_shared<FirebaseAuthCredentialsProvider>(app, nil);
credentials_provider->GetToken(
[token_promise](util::StatusOr<AuthToken> result) {
EXPECT_TRUE(result.ok());
const AuthToken& token = result.ValueOrDie();
Expand All @@ -99,8 +101,9 @@ - (void)getTokenForcingRefresh:(BOOL)forceRefresh
TEST(FirebaseAuthCredentialsProviderTest, GetTokenUnauthenticated) {
FIRApp* app = testutil::AppForUnitTesting();
FSTAuthFake* auth = [[FSTAuthFake alloc] initWithToken:nil uid:nil];
FirebaseAuthCredentialsProvider credentials_provider(app, auth);
credentials_provider.GetToken([](util::StatusOr<AuthToken> result) {
auto credentials_provider =
std::make_shared<FirebaseAuthCredentialsProvider>(app, auth);
credentials_provider->GetToken([](util::StatusOr<AuthToken> result) {
EXPECT_TRUE(result.ok());
const AuthToken& token = result.ValueOrDie();
EXPECT_ANY_THROW(token.token());
Expand All @@ -114,8 +117,9 @@ - (void)getTokenForcingRefresh:(BOOL)forceRefresh
FIRApp* app = testutil::AppForUnitTesting();
FSTAuthFake* auth = [[FSTAuthFake alloc] initWithToken:@"token for fake uid"
uid:@"fake uid"];
FirebaseAuthCredentialsProvider credentials_provider(app, auth);
credentials_provider.GetToken([](util::StatusOr<AuthToken> result) {
auto credentials_provider =
std::make_shared<FirebaseAuthCredentialsProvider>(app, auth);
credentials_provider->GetToken([](util::StatusOr<AuthToken> result) {
EXPECT_TRUE(result.ok());
const AuthToken& token = result.ValueOrDie();
EXPECT_EQ("token for fake uid", token.token());
Expand All @@ -129,22 +133,24 @@ - (void)getTokenForcingRefresh:(BOOL)forceRefresh
FIRApp* app = testutil::AppForUnitTesting();
FSTAuthFake* auth = [[FSTAuthFake alloc] initWithToken:@"default token"
uid:@"fake uid"];
FirebaseAuthCredentialsProvider credentials_provider(app, auth);
credentials_provider.SetCredentialChangeListener([](User user) {
auto credentials_provider =
std::make_shared<FirebaseAuthCredentialsProvider>(app, auth);
credentials_provider->SetCredentialChangeListener([](User user) {
EXPECT_EQ("fake uid", user.uid());
EXPECT_TRUE(user.is_authenticated());
});

credentials_provider.SetCredentialChangeListener(nullptr);
credentials_provider->SetCredentialChangeListener(nullptr);
}

TEST(FirebaseAuthCredentialsProviderTest, InvalidateToken) {
FIRApp* app = testutil::AppForUnitTesting();
FSTAuthFake* auth = [[FSTAuthFake alloc] initWithToken:@"token for fake uid"
uid:@"fake uid"];
FirebaseAuthCredentialsProvider credentials_provider(app, auth);
credentials_provider.InvalidateToken();
credentials_provider.GetToken([&auth](util::StatusOr<AuthToken> result) {
auto credentials_provider =
std::make_shared<FirebaseAuthCredentialsProvider>(app, auth);
credentials_provider->InvalidateToken();
credentials_provider->GetToken([&auth](util::StatusOr<AuthToken> result) {
EXPECT_TRUE(result.ok());
EXPECT_TRUE(auth.forceRefreshTriggered);
const AuthToken& token = result.ValueOrDie();
Expand All @@ -160,14 +166,15 @@ - (void)getTokenForcingRefresh:(BOOL)forceRefresh
FIRApp* app = testutil::AppForUnitTesting();
FSTAuthFake* auth = [[FSTAuthFake alloc] initWithToken:@"token for fake uid"
uid:@"fake uid"];
FirebaseAuthCredentialsProvider credentials_provider(app, auth);
auto credentials_provider =
std::make_shared<FirebaseAuthCredentialsProvider>(app, auth);

std::thread thread1([&credentials_provider] {
credentials_provider.GetToken(
std::thread thread1([credentials_provider] {
credentials_provider->GetToken(
[](util::StatusOr<AuthToken> result) { EXPECT_TRUE(result.ok()); });
});
std::thread thread2([&credentials_provider] {
credentials_provider.GetToken(
std::thread thread2([credentials_provider] {
credentials_provider->GetToken(
[](util::StatusOr<AuthToken> result) { EXPECT_TRUE(result.ok()); });
});

Expand Down
Loading