Skip to content

Commit 1cc5b5c

Browse files
smilesa-maurice
authored andcommitted
Added support for deletion of IID tokens and IIDs.
This also will delete all tokens if an ID has expired. PiperOrigin-RevId: 248654381
1 parent 2d581bd commit 1cc5b5c

File tree

2 files changed

+154
-40
lines changed

2 files changed

+154
-40
lines changed

app/instance_id/instance_id_desktop_impl.cc

Lines changed: 139 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
#include "app/instance_id/iid_data_generated.h"
2020
#include "app/rest/util.h"
2121
#include "app/rest/www_form_url_encoded.h"
22+
#include "app/src/app_common.h"
2223
#include "app/src/app_identifier.h"
2324
#include "app/src/base64.h"
2425
#include "app/src/cleanup_notifier.h"
26+
#include "app/src/locale.h"
2527
#include "app/src/time.h"
2628
#include "app/src/uuid.h"
2729
#include "flatbuffers/flexbuffers.h"
@@ -44,27 +46,32 @@ static const int kCheckinProtocolVersion = 2;
4446
// Instance ID backend.
4547
static const char kInstanceIdUrl[] = "https://fcmtoken.googleapis.com/register";
4648

49+
// Backend had a temporary failure, the client should retry. http://b/27043795
50+
static const char kPhoneRegistrationError[] = "PHONE_REGISTRATION_ERROR";
51+
// Server detected the token is no longer valid.
52+
static const char kTokenResetError[] = "RST";
53+
// Wildcard scope used to delete all tokens and the ID in a server registration.
54+
static const char kWildcardTokenScope[] = "*";
55+
4756
std::map<App*, InstanceIdDesktopImpl*>
4857
InstanceIdDesktopImpl::instance_id_by_app_; // NOLINT
4958
Mutex InstanceIdDesktopImpl::instance_id_by_app_mutex_; // NOLINT
5059

5160
InstanceIdDesktopImpl::InstanceIdDesktopImpl(App* app)
5261
: storage_semaphore_(0),
5362
app_(app),
54-
locale_("en_US" /* TODO(b/132732303) */),
55-
timezone_("America/Los_Angeles" /* TODO(b/132733022) */),
63+
locale_(firebase::internal::GetLocale()),
5664
logging_id_(rand()), // NOLINT
57-
ios_device_model_("iPhone 8" /* TODO */),
58-
ios_device_version_("8.0" /* TODO */),
65+
ios_device_model_(app_common::kOperatingSystem),
66+
ios_device_version_("0.0.0" /* TODO */),
5967
app_version_("1.2.3" /* TODO */),
60-
os_version_("freedos-10.0.0" /* TODO */),
68+
os_version_(app_common::kOperatingSystem /* TODO add: version */),
6169
platform_(0),
6270
network_operation_complete_(0),
6371
terminating_(false) {
6472
rest::InitTransportCurl();
6573
rest::util::Initialize();
6674
transport_.reset(new rest::TransportCurl());
67-
(void)kInstanceIdUrl; // TODO(smiles): Remove this when registration is in.
6875

6976
future_manager().AllocFutureApi(this, kInstanceIdFnCount);
7077

@@ -160,21 +167,11 @@ Future<void> InstanceIdDesktopImpl::DeleteId() {
160167
SafeFutureHandle<void> handle =
161168
ref_future()->SafeAlloc<void>(kInstanceIdFnRemoveId);
162169

163-
std::vector<std::string> token_scopes;
164-
for (auto i = tokens_.begin(); i != tokens_.end(); ++i) {
165-
token_scopes.push_back(i->first);
166-
}
167-
// Delete all tokens.
168-
while (!token_scopes.empty()) {
169-
/* TODO DeleteTokenRequest(token_scopes.back()); */
170-
DeleteCachedToken(token_scopes.back().c_str());
171-
token_scopes.pop_back();
170+
if (DeleteServerToken(nullptr, true)) {
171+
ref_future()->Complete(handle, 0, "");
172+
} else {
173+
ref_future()->Complete(handle, kErrorUnknownError, "DeleteId failed");
172174
}
173-
instance_id_ = "";
174-
DeleteFromStorage();
175-
checkin_data_.Clear();
176-
ref_future()->Complete(handle, 0, "");
177-
178175
return MakeFuture(ref_future(), handle);
179176
}
180177

@@ -191,7 +188,7 @@ Future<std::string> InstanceIdDesktopImpl::GetToken(const char* scope) {
191188
ref_future()->SafeAlloc<std::string>(kInstanceIdFnGetToken);
192189

193190
std::string scope_str(scope);
194-
if (FetchToken(scope_str.c_str())) {
191+
if (FetchServerToken(scope_str.c_str())) {
195192
ref_future()->CompleteWithResult(handle, 0, "",
196193
FindCachedToken(scope_str.c_str()));
197194
} else {
@@ -213,9 +210,7 @@ Future<void> InstanceIdDesktopImpl::DeleteToken(const char* scope) {
213210
ref_future()->SafeAlloc<void>(kInstanceIdFnRemoveToken);
214211

215212
std::string scope_str(scope);
216-
DeleteCachedToken(scope_str.c_str());
217-
if (/*ServerDeleteToken(scope_str.c_str() && */
218-
tokens_.find(scope_str) == tokens_.end()) {
213+
if (DeleteServerToken(scope_str.c_str(), false)) {
219214
ref_future()->Complete(handle, 0, "");
220215
} else {
221216
ref_future()->Complete(handle, kErrorUnknownError, "DeleteToken failed");
@@ -392,7 +387,9 @@ bool InstanceIdDesktopImpl::InitialOrRefreshCheckin() {
392387
int logging_id;
393388
const char* ios_device_model;
394389
const char* ios_device_version;
395-
} builder_scope(&fbb, &checkin_data_, locale_.c_str(), timezone_.c_str(),
390+
} builder_scope(&fbb, &checkin_data_, locale_.c_str(),
391+
timezone_.empty() ? firebase::internal::GetTimezone().c_str()
392+
: timezone_.c_str(),
396393
logging_id_, ios_device_model_.c_str(),
397394
ios_device_version_.c_str());
398395
fbb.Map(
@@ -520,18 +517,13 @@ std::string InstanceIdDesktopImpl::FindCachedToken(const char* scope) {
520517

521518
void InstanceIdDesktopImpl::DeleteCachedToken(const char* scope) {
522519
auto cached_token = tokens_.find(scope);
523-
if (cached_token != tokens_.end()) {
524-
tokens_.erase(cached_token);
525-
}
520+
if (cached_token != tokens_.end()) tokens_.erase(cached_token);
526521
}
527522

528-
bool InstanceIdDesktopImpl::FetchToken(const char* scope) {
529-
if (terminating_ || !InitialOrRefreshCheckin()) return false;
530-
531-
// If we already have a token, don't refresh.
532-
std::string token = FindCachedToken(scope);
533-
if (!token.empty()) return true;
534-
523+
void InstanceIdDesktopImpl::ServerTokenOperation(
524+
const char* scope,
525+
void (*request_callback)(rest::Request* request, void* state),
526+
void* state) {
535527
const AppOptions& app_options = app_->options();
536528
request_buffer_.clear();
537529
rest::WwwFormUrlEncoded form(&request_buffer_);
@@ -564,10 +556,20 @@ bool InstanceIdDesktopImpl::FetchToken(const char* scope) {
564556
(std::string("AidLogin ") + checkin_data_.device_id +
565557
std::string(":") + checkin_data_.security_token)
566558
.c_str());
559+
if (request_callback) request_callback(request, state);
567560
network_operation_->Perform(transport_.get());
568561
}
569562
network_operation_complete_.Wait();
563+
}
564+
565+
bool InstanceIdDesktopImpl::FetchServerToken(const char* scope) {
566+
if (terminating_ || !InitialOrRefreshCheckin()) return false;
570567

568+
// If we already have a token, don't refresh.
569+
std::string token = FindCachedToken(scope);
570+
if (!token.empty()) return true;
571+
572+
ServerTokenOperation(scope, nullptr, nullptr);
571573
{
572574
MutexLock lock(network_operation_mutex_);
573575
assert(network_operation_.get());
@@ -581,9 +583,7 @@ bool InstanceIdDesktopImpl::FetchToken(const char* scope) {
581583
}
582584
// Parse the response.
583585
auto form_data = rest::WwwFormUrlEncoded::Parse(response.GetBody());
584-
585586
std::string error;
586-
587587
// Search the response for a token or an error.
588588
for (size_t i = 0; i < form_data.size(); ++i) {
589589
const auto& item = form_data[i];
@@ -594,20 +594,122 @@ bool InstanceIdDesktopImpl::FetchToken(const char* scope) {
594594
}
595595
}
596596

597+
// Parse any returned errors.
598+
if (!error.empty()) {
599+
size_t component_start = 0;
600+
size_t component_end;
601+
do {
602+
component_end = error.find(":", component_start);
603+
std::string error_component =
604+
error.substr(component_start, component_end - component_start);
605+
if (error_component == kPhoneRegistrationError) {
606+
// TODO(smiles): Retry with expodential backoff.
607+
network_operation_.reset(nullptr);
608+
return true;
609+
} else if (error_component == kTokenResetError) {
610+
// Server requests that the token is reset.
611+
DeleteServerToken(nullptr, true);
612+
network_operation_.reset(nullptr);
613+
return false;
614+
}
615+
component_start = component_end + 1;
616+
} while (component_end != std::string::npos);
617+
}
618+
597619
if (token.empty()) {
598620
LogError(
599621
"No token returned in instance ID token fetch. "
600622
"Responded with '%s'",
601623
response.GetBody());
624+
network_operation_.reset(nullptr);
602625
return false;
603626
}
604627

605628
// Cache the token.
606629
tokens_[scope] = token;
630+
network_operation_.reset(nullptr);
631+
}
632+
if (!SaveToStorage()) {
633+
LogError("Failed to save token for scope %s to storage", scope);
634+
return false;
607635
}
608636
return true;
609637
}
610638

639+
bool InstanceIdDesktopImpl::DeleteServerToken(const char* scope,
640+
bool delete_id) {
641+
if (terminating_) return false;
642+
643+
// Load credentials from storage as we'll need them to delete the ID.
644+
LoadFromStorage();
645+
646+
if (delete_id) {
647+
if (tokens_.empty() && instance_id_.empty()) return true;
648+
scope = kWildcardTokenScope;
649+
} else {
650+
// If we don't have a token, we have nothing to do.
651+
std::string token = FindCachedToken(scope);
652+
if (token.empty()) return true;
653+
}
654+
655+
ServerTokenOperation(
656+
scope,
657+
[](rest::Request* request, void* delete_id) {
658+
std::string body;
659+
request->ReadBodyIntoString(&body);
660+
rest::WwwFormUrlEncoded form(&body);
661+
form.Add("delete", "true");
662+
if (delete_id) form.Add("iid-operation", "delete");
663+
request->set_post_fields(body.c_str(), body.length());
664+
},
665+
reinterpret_cast<void*>(delete_id ? 1 : 0));
666+
667+
{
668+
MutexLock lock(network_operation_mutex_);
669+
assert(network_operation_.get());
670+
const rest::Response& response = network_operation_->response;
671+
// Check for errors
672+
if (response.status() != rest::util::HttpSuccess) {
673+
LogError("Instance ID token delete failed with response %d '%s'",
674+
response.status(), response.GetBody());
675+
network_operation_.reset(nullptr);
676+
return false;
677+
}
678+
// Parse the response.
679+
auto form_data = rest::WwwFormUrlEncoded::Parse(response.GetBody());
680+
bool found_valid_response = false;
681+
for (int i = 0; i < form_data.size(); ++i) {
682+
const auto& item = form_data[i];
683+
if (item.key == "deleted" || item.key == "token") {
684+
found_valid_response = true;
685+
break;
686+
}
687+
}
688+
if (found_valid_response) {
689+
if (delete_id) {
690+
checkin_data_.Clear();
691+
instance_id_.clear();
692+
tokens_.clear();
693+
DeleteFromStorage();
694+
} else {
695+
DeleteCachedToken(scope);
696+
if (!SaveToStorage()) {
697+
LogError("Failed to delete tokens for scope %s from storage", scope);
698+
network_operation_.reset(nullptr);
699+
return false;
700+
}
701+
}
702+
} else {
703+
LogError(
704+
"Instance ID token delete failed, server returned invalid "
705+
"response '%s'",
706+
response.GetBody());
707+
network_operation_.reset(nullptr);
708+
}
709+
return found_valid_response;
710+
}
711+
}
712+
611713
} // namespace internal
612714
} // namespace instance_id
613715
} // namespace firebase

app/instance_id/instance_id_desktop_impl.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,21 @@ class InstanceIdDesktopImpl {
213213
// Clear a token for a scope.
214214
void DeleteCachedToken(const char* scope);
215215

216-
// Fetch a new token. Scope can be either "FCM" or "*" for remote config
217-
// and other users.
218-
bool FetchToken(const char* scope);
216+
// Perform a token fetch or delete network operation with an optional
217+
// callback to modify the request before the network operation is scheduled.
218+
void ServerTokenOperation(const char* scope,
219+
void (*request_callback)(rest::Request* request,
220+
void* state),
221+
void* state);
222+
223+
// Fetch a token from the cache or retrieve a new token from the server.
224+
// The scope can be either "FCM" or "*" for remote config and other users.
225+
bool FetchServerToken(const char* scope);
226+
227+
// Delete a server-side token for a scope and remove it from the cache.
228+
// If delete_id is true all tokens are deleted along with the server
229+
// registration of instance ID.
230+
bool DeleteServerToken(const char* scope, bool delete_id);
219231

220232
// Used to wait for async storage functions to finish.
221233
Semaphore storage_semaphore_;

0 commit comments

Comments
 (0)