buffet: Forcibly refresh access token when handling HTTP error 401.

It's not enough to just check that the access token hasn't expired so
always fetch a new access token when the server specifically tells us
that the access token is wrong (e.g. HTTP error code 401).

BUG=brillo:275
TEST=Unit tests pass.

Change-Id: Ib619883a9629e313c9f76679232bcd245bf83bb9
Reviewed-on: https://chromium-review.googlesource.com/255590
Reviewed-by: Nathan Bullock <nathanbullock@google.com>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Tested-by: David Zeuthen <zeuthen@chromium.org>
Commit-Queue: David Zeuthen <zeuthen@chromium.org>
(cherry picked from commit 155a413327fdc20a99e195f2753d98cb19cb9db4)
Reviewed-on: https://chromium-review.googlesource.com/258860
Reviewed-by: Patrick Sosinski <sosinski@chromium.org>
Commit-Queue: Patrick Sosinski <sosinski@chromium.org>
Tested-by: Patrick Sosinski <sosinski@chromium.org>
Reviewed-by: Aaron Kemp <kemp@google.com>
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
index 16f576a..3dffe12 100644
--- a/buffet/device_registration_info.cc
+++ b/buffet/device_registration_info.cc
@@ -292,7 +292,7 @@
 
 bool DeviceRegistrationInfo::CheckRegistration(chromeos::ErrorPtr* error) {
   return HaveRegistrationCredentials(error) &&
-         ValidateAndRefreshAccessToken(error);
+         MaybeRefreshAccessToken(error);
 }
 
 bool DeviceRegistrationInfo::HaveRegistrationCredentials(
@@ -310,7 +310,7 @@
   return have_credentials;
 }
 
-bool DeviceRegistrationInfo::ValidateAndRefreshAccessToken(
+bool DeviceRegistrationInfo::MaybeRefreshAccessToken(
     chromeos::ErrorPtr* error) {
   LOG(INFO) << "Checking access token expiration.";
   if (!access_token_.empty() &&
@@ -319,7 +319,12 @@
     LOG(INFO) << "Access token is still valid.";
     return true;
   }
+  return RefreshAccessToken(error);
+}
 
+bool DeviceRegistrationInfo::RefreshAccessToken(
+    chromeos::ErrorPtr* error) {
+  LOG(INFO) << "Refreshing access token.";
   auto response = chromeos::http::PostFormDataAndBlock(GetOAuthURL("token"), {
     {"refresh_token", refresh_token_},
     {"client_id", client_id_},
@@ -686,7 +691,8 @@
     if (error->HasError(chromeos::errors::http::kDomain,
                         std::to_string(chromeos::http::status_code::Denied))) {
       chromeos::ErrorPtr reauthorization_error;
-      if (!self->ValidateAndRefreshAccessToken(&reauthorization_error)) {
+      // Forcibly refresh the access token.
+      if (!self->RefreshAccessToken(&reauthorization_error)) {
         // TODO(antonm): Check if the device has been actually removed.
         error_cb(request_id, reauthorization_error.get());
         return;
diff --git a/buffet/device_registration_info.h b/buffet/device_registration_info.h
index 95c8b0d..1f74af9 100644
--- a/buffet/device_registration_info.h
+++ b/buffet/device_registration_info.h
@@ -147,8 +147,13 @@
   // Checks whether we have credentials generated during registration.
   bool HaveRegistrationCredentials(chromeos::ErrorPtr* error);
 
-  // Makes sure the access token is available and up-to-date.
-  bool ValidateAndRefreshAccessToken(chromeos::ErrorPtr* error);
+  // If we currently have an access token and it doesn't like like it
+  // has expired yet, returns true immediately. Otherwise calls
+  // RefreshAccessToken().
+  bool MaybeRefreshAccessToken(chromeos::ErrorPtr* error);
+
+  // Forcibly refreshes the access token.
+  bool RefreshAccessToken(chromeos::ErrorPtr* error);
 
   // This attempts to open the XMPP channel. The XMPP channel needs to be
   // restarted anytime the access_token is refreshed.