buffet: Refetch access token when expired.

BUG=chromium:420580
TEST=cros_workon_make buffet --test&&manual

Change-Id: I17c5eb2ef86991baed1a700007a11537bb07104d
Reviewed-on: https://chromium-review.googlesource.com/224940
Tested-by: Anton Muhin <antonm@chromium.org>
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Anton Muhin <antonm@chromium.org>
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
index 21638e8..8dd78a3 100644
--- a/buffet/device_registration_info.cc
+++ b/buffet/device_registration_info.cc
@@ -13,6 +13,7 @@
 #include <base/values.h>
 #include <chromeos/bind_lambda.h>
 #include <chromeos/data_encoding.h>
+#include <chromeos/errors/error_codes.h>
 #include <chromeos/http/http_utils.h>
 #include <chromeos/mime_utils.h>
 #include <chromeos/strings/string_utils.h>
@@ -545,6 +546,9 @@
     // TODO(antonm): Support Retry-After header.
     on_retriable_failure();
   } else {
+    chromeos::Error::AddTo(&error, chromeos::errors::http::kDomain,
+                           std::to_string(status_code),
+                           response->GetStatusText());
     PostToCallback(errorback, std::move(error));
   }
 }
@@ -552,7 +556,7 @@
 }  // namespace
 
 void DeviceRegistrationInfo::DoCloudRequest(
-    const char* method,
+    const std::string& method,
     const std::string& url,
     const base::DictionaryValue* body,
     CloudRequestCallback callback,
@@ -584,12 +588,35 @@
     PostToCallback(callback, std::move(json_resp));
   };
 
+  auto transport = transport_;
+  auto errorback_with_reauthorization = base::Bind(
+      [method, url, data, mime_type, transport, request_cb, errorback]
+      (DeviceRegistrationInfo* self, const chromeos::Error& error) {
+    if (error.HasError(chromeos::errors::http::kDomain,
+                       std::to_string(chromeos::http::status_code::Denied))) {
+      chromeos::ErrorPtr reauthorization_error;
+      if (!self->ValidateAndRefreshAccessToken(&reauthorization_error)) {
+        // TODO(antonm): Check if the device has been actually removed.
+        errorback.Run(*reauthorization_error.get());
+        return;
+      }
+      SendRequestAsync(method, url,
+                       data, mime_type,
+                       {self->GetAuthorizationHeader()},
+                       transport,
+                       7,
+                       base::Bind(request_cb), errorback);
+    } else {
+      errorback.Run(error);
+    }
+  }, base::Unretained(this));
+
   SendRequestAsync(method, url,
                    data, mime_type,
                    {GetAuthorizationHeader()},
-                   transport_,
+                   transport,
                    7,
-                   base::Bind(request_cb), errorback);
+                   base::Bind(request_cb), errorback_with_reauthorization);
 }
 
 void DeviceRegistrationInfo::StartDevice(chromeos::ErrorPtr* error) {
diff --git a/buffet/device_registration_info.h b/buffet/device_registration_info.h
index 2ed39ed..80c57d8 100644
--- a/buffet/device_registration_info.h
+++ b/buffet/device_registration_info.h
@@ -130,7 +130,7 @@
   // requests.
   // TODO(antonm): Consider moving into some other class.
   void DoCloudRequest(
-      const char* method,
+      const std::string& method,
       const std::string& url,
       const base::DictionaryValue* body,
       CloudRequestCallback callback,