19
19
#include " app/instance_id/iid_data_generated.h"
20
20
#include " app/rest/util.h"
21
21
#include " app/rest/www_form_url_encoded.h"
22
+ #include " app/src/app_common.h"
22
23
#include " app/src/app_identifier.h"
23
24
#include " app/src/base64.h"
24
25
#include " app/src/cleanup_notifier.h"
26
+ #include " app/src/locale.h"
25
27
#include " app/src/time.h"
26
28
#include " app/src/uuid.h"
27
29
#include " flatbuffers/flexbuffers.h"
@@ -44,27 +46,32 @@ static const int kCheckinProtocolVersion = 2;
44
46
// Instance ID backend.
45
47
static const char kInstanceIdUrl [] = " https://fcmtoken.googleapis.com/register" ;
46
48
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
+
47
56
std::map<App*, InstanceIdDesktopImpl*>
48
57
InstanceIdDesktopImpl::instance_id_by_app_; // NOLINT
49
58
Mutex InstanceIdDesktopImpl::instance_id_by_app_mutex_; // NOLINT
50
59
51
60
InstanceIdDesktopImpl::InstanceIdDesktopImpl (App* app)
52
61
: storage_semaphore_(0 ),
53
62
app_ (app),
54
- locale_(" en_US" /* TODO(b/132732303) */ ),
55
- timezone_(" America/Los_Angeles" /* TODO(b/132733022) */ ),
63
+ locale_(firebase::internal::GetLocale()),
56
64
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 */ ),
59
67
app_version_(" 1.2.3" /* TODO */ ),
60
- os_version_(" freedos-10.0.0 " /* TODO */ ),
68
+ os_version_(app_common:: kOperatingSystem /* TODO add: version */ ),
61
69
platform_(0 ),
62
70
network_operation_complete_(0 ),
63
71
terminating_(false ) {
64
72
rest::InitTransportCurl ();
65
73
rest::util::Initialize ();
66
74
transport_.reset (new rest::TransportCurl ());
67
- (void )kInstanceIdUrl ; // TODO(smiles): Remove this when registration is in.
68
75
69
76
future_manager ().AllocFutureApi (this , kInstanceIdFnCount );
70
77
@@ -160,21 +167,11 @@ Future<void> InstanceIdDesktopImpl::DeleteId() {
160
167
SafeFutureHandle<void > handle =
161
168
ref_future ()->SafeAlloc <void >(kInstanceIdFnRemoveId );
162
169
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" );
172
174
}
173
- instance_id_ = " " ;
174
- DeleteFromStorage ();
175
- checkin_data_.Clear ();
176
- ref_future ()->Complete (handle, 0 , " " );
177
-
178
175
return MakeFuture (ref_future (), handle);
179
176
}
180
177
@@ -191,7 +188,7 @@ Future<std::string> InstanceIdDesktopImpl::GetToken(const char* scope) {
191
188
ref_future ()->SafeAlloc <std::string>(kInstanceIdFnGetToken );
192
189
193
190
std::string scope_str (scope);
194
- if (FetchToken (scope_str.c_str ())) {
191
+ if (FetchServerToken (scope_str.c_str ())) {
195
192
ref_future ()->CompleteWithResult (handle, 0 , " " ,
196
193
FindCachedToken (scope_str.c_str ()));
197
194
} else {
@@ -213,9 +210,7 @@ Future<void> InstanceIdDesktopImpl::DeleteToken(const char* scope) {
213
210
ref_future ()->SafeAlloc <void >(kInstanceIdFnRemoveToken );
214
211
215
212
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 )) {
219
214
ref_future ()->Complete (handle, 0 , " " );
220
215
} else {
221
216
ref_future ()->Complete (handle, kErrorUnknownError , " DeleteToken failed" );
@@ -392,7 +387,9 @@ bool InstanceIdDesktopImpl::InitialOrRefreshCheckin() {
392
387
int logging_id;
393
388
const char * ios_device_model;
394
389
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(),
396
393
logging_id_, ios_device_model_.c_str(),
397
394
ios_device_version_.c_str());
398
395
fbb.Map(
@@ -520,18 +517,13 @@ std::string InstanceIdDesktopImpl::FindCachedToken(const char* scope) {
520
517
521
518
void InstanceIdDesktopImpl::DeleteCachedToken (const char * scope) {
522
519
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);
526
521
}
527
522
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) {
535
527
const AppOptions& app_options = app_->options ();
536
528
request_buffer_.clear ();
537
529
rest::WwwFormUrlEncoded form (&request_buffer_);
@@ -564,10 +556,20 @@ bool InstanceIdDesktopImpl::FetchToken(const char* scope) {
564
556
(std::string (" AidLogin " ) + checkin_data_.device_id +
565
557
std::string (" :" ) + checkin_data_.security_token )
566
558
.c_str ());
559
+ if (request_callback) request_callback (request, state);
567
560
network_operation_->Perform (transport_.get ());
568
561
}
569
562
network_operation_complete_.Wait ();
563
+ }
564
+
565
+ bool InstanceIdDesktopImpl::FetchServerToken (const char * scope) {
566
+ if (terminating_ || !InitialOrRefreshCheckin ()) return false ;
570
567
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 );
571
573
{
572
574
MutexLock lock (network_operation_mutex_);
573
575
assert (network_operation_.get ());
@@ -581,9 +583,7 @@ bool InstanceIdDesktopImpl::FetchToken(const char* scope) {
581
583
}
582
584
// Parse the response.
583
585
auto form_data = rest::WwwFormUrlEncoded::Parse (response.GetBody ());
584
-
585
586
std::string error;
586
-
587
587
// Search the response for a token or an error.
588
588
for (size_t i = 0 ; i < form_data.size (); ++i) {
589
589
const auto & item = form_data[i];
@@ -594,20 +594,122 @@ bool InstanceIdDesktopImpl::FetchToken(const char* scope) {
594
594
}
595
595
}
596
596
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
+
597
619
if (token.empty ()) {
598
620
LogError (
599
621
" No token returned in instance ID token fetch. "
600
622
" Responded with '%s'" ,
601
623
response.GetBody ());
624
+ network_operation_.reset (nullptr );
602
625
return false ;
603
626
}
604
627
605
628
// Cache the token.
606
629
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 ;
607
635
}
608
636
return true ;
609
637
}
610
638
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
+
611
713
} // namespace internal
612
714
} // namespace instance_id
613
715
} // namespace firebase
0 commit comments