chromium/chrome/browser/ash/printing/oauth2/authorization_zone_unittest.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/printing/oauth2/authorization_zone.h"

#include <memory>
#include <string>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/values.h"
#include "chrome/browser/ash/printing/oauth2/constants.h"
#include "chrome/browser/ash/printing/oauth2/mock_client_ids_database.h"
#include "chrome/browser/ash/printing/oauth2/test_authorization_server.h"
#include "chromeos/printing/uri.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace ash {
namespace printing {
namespace oauth2 {
namespace {

class PrintingOAuth2AuthorizationZoneTest : public testing::Test {
 public:
  // Initializes Authorization Zone.
  void CreateAuthorizationZone(const std::string& client_id) {
    GURL auth_server_uri(authorization_server_uri_);
    CHECK(auth_server_uri.is_valid());

    EXPECT_CALL(client_ids_database_, FetchId)
        .WillOnce([client_id](const GURL& url, StatusCallback callback) {
          std::move(callback).Run(StatusCode::kOK, client_id);
        });
    authorization_zone_ = printing::oauth2::AuthorizationZone::Create(
        server_.GetURLLoaderFactory(), auth_server_uri, &client_ids_database_);
  }

  // Simulates the authorization process in the internet browser. Returns the
  // URL that the browser is redirected to at the end of the authorization
  // process.
  std::string SimulateAuthorization(const std::string& authorization_url,
                                    const std::string& auth_code,
                                    const std::string& scope) {
    auto question_mark = authorization_url.find('?');
    EXPECT_LT(question_mark, authorization_url.size());
    const std::string auth_host_path =
        authorization_url.substr(0, question_mark);
    base::flat_map<std::string, std::string> params;
    EXPECT_TRUE(ParseURLParameters(authorization_url.substr(question_mark + 1),
                                   params));
    EXPECT_EQ(params["scope"], scope);
    return base::StrCat(
        {printing::oauth2::kRedirectURI,
         "?code=", base::EscapeUrlEncodedData(auth_code, true),
         "&state=", base::EscapeUrlEncodedData(params["state"], true)});
  }

  std::string SimulateAuthorizationError(const std::string& authorization_url,
                                         const std::string& error) {
    auto question_mark = authorization_url.find('?');
    EXPECT_LT(question_mark, authorization_url.size());
    const std::string auth_host_path =
        authorization_url.substr(0, question_mark);
    base::flat_map<std::string, std::string> params;
    EXPECT_TRUE(ParseURLParameters(authorization_url.substr(question_mark + 1),
                                   params));
    return base::StrCat(
        {printing::oauth2::kRedirectURI,
         "?error=", base::EscapeUrlEncodedData(error, true),
         "&state=", base::EscapeUrlEncodedData(params["state"], true)});
  }

  // Simulates Metadata Request described in rfc8414, section 3.
  void ProcessMetadataRequest() {
    EXPECT_EQ("", server_.ReceiveGET(metadata_uri_));
    auto fields = BuildMetadata(authorization_server_uri_, authorization_uri_,
                                token_uri_, registration_uri_);
    server_.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, fields);
  }

  // Simulates Registration Request described in rfc7591, section 3.
  void ProcessRegistrationRequest(const std::string& client_id) {
    base::Value::Dict fields;
    EXPECT_EQ("", server_.ReceivePOSTWithJSON(registration_uri_, fields));
    fields.Set("client_id", client_id);
    server_.ResponseWithJSON(net::HttpStatusCode::HTTP_CREATED, fields);
  }

  // Simulates First Token Request described in rfc6749, sections 4.1.3-4 and 5.
  void ProcessFirstTokenRequest(const std::string& auth_code,
                                const std::string& access_token,
                                const std::string& refresh_token) {
    base::flat_map<std::string, std::string> params;
    EXPECT_EQ("", server_.ReceivePOSTWithURLParams(token_uri_, params));
    EXPECT_EQ(params["code"], auth_code);
    base::Value::Dict fields;
    fields.Set("access_token", access_token);
    fields.Set("token_type", "bearer");
    if (!refresh_token.empty()) {
      fields.Set("refresh_token", refresh_token);
    }
    server_.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, fields);
  }

  // The same as ProcessFirstTokenRequest(...) but with an error response.
  void ProcessFirstTokenRequestError(const std::string& auth_code,
                                     const std::string& error) {
    base::flat_map<std::string, std::string> params;
    EXPECT_EQ("", server_.ReceivePOSTWithURLParams(token_uri_, params));
    EXPECT_EQ(params["code"], auth_code);
    base::Value::Dict fields;
    fields.Set("error", error);
    server_.ResponseWithJSON(net::HttpStatusCode::HTTP_BAD_REQUEST, fields);
  }

  // Simulates Next Token Request described in rfc6749, section 6.
  void ProcessNextTokenRequest(const std::string& current_refresh_token,
                               const std::string& access_token,
                               const std::string& new_refresh_token) {
    base::flat_map<std::string, std::string> params;
    EXPECT_EQ("", server_.ReceivePOSTWithURLParams(token_uri_, params));
    EXPECT_EQ(params["refresh_token"], current_refresh_token);
    base::Value::Dict fields;
    fields.Set("access_token", access_token);
    fields.Set("token_type", "bearer");
    if (!new_refresh_token.empty()) {
      fields.Set("refresh_token", new_refresh_token);
    }
    server_.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, fields);
  }

  // Simulates Token Exchange Request described in rfc8693, section 2.
  void ProcessTokenExchangeRequest(const chromeos::Uri& ipp_endpoint,
                                   const std::string& access_token,
                                   const std::string& endpoint_access_token) {
    base::flat_map<std::string, std::string> params;
    EXPECT_EQ("", server_.ReceivePOSTWithURLParams(token_uri_, params));
    EXPECT_EQ(params["resource"], ipp_endpoint.GetNormalized());
    EXPECT_EQ(params["subject_token"], access_token);
    base::Value::Dict fields;
    fields.Set("access_token", endpoint_access_token);
    fields.Set("issued_token_type",
               "urn:ietf:params:oauth:token-type:access_token");
    fields.Set("token_type", "bearer");
    server_.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, fields);
  }

  // The same as ProcessTokenExchangeRequest(...) but with an error response.
  void ProcessTokenExchangeRequestError(const chromeos::Uri& ipp_endpoint,
                                        const std::string& access_token,
                                        const std::string& error) {
    base::flat_map<std::string, std::string> params;
    EXPECT_EQ("", server_.ReceivePOSTWithURLParams(token_uri_, params));
    EXPECT_EQ(params["resource"], ipp_endpoint.GetNormalized());
    EXPECT_EQ(params["subject_token"], access_token);
    base::Value::Dict fields;
    fields.Set("error", error);
    server_.ResponseWithJSON(net::HttpStatusCode::HTTP_BAD_REQUEST, fields);
  }

 protected:
  const std::string authorization_server_uri_ = "https://example.com/path";
  const std::string metadata_uri_ =
      "https://example.com/.well-known/oauth-authorization-server/path";
  const std::string authorization_uri_ = "https://example.com/authorization";
  const std::string token_uri_ = "https://example.com/token";
  const std::string registration_uri_ = "https://example.com/registration";

  testing::NiceMock<MockClientIdsDatabase> client_ids_database_;
  std::unique_ptr<printing::oauth2::AuthorizationZone> authorization_zone_;
  FakeAuthorizationServer server_;
};

TEST_F(PrintingOAuth2AuthorizationZoneTest, InitializationOfRegisteredClient) {
  CallbackResult cr;
  CreateAuthorizationZone("clientID_abcd1234");

  authorization_zone_->InitAuthorization("scope1 scope2 scope3",
                                         BindResult(cr));
  ProcessMetadataRequest();
  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
  const std::string authorization_url = cr.data;

  // Parse and verify the returned URL.
  auto question_mark = authorization_url.find('?');
  ASSERT_LT(question_mark, authorization_url.size());
  const std::string auth_host_path = authorization_url.substr(0, question_mark);
  base::flat_map<std::string, std::string> params;
  ASSERT_TRUE(
      ParseURLParameters(authorization_url.substr(question_mark + 1), params));
  EXPECT_EQ(auth_host_path, authorization_uri_);
  EXPECT_EQ(params["response_type"], "code");
  EXPECT_EQ(params["response_mode"], "query");
  EXPECT_EQ(params["client_id"], "clientID_abcd1234");
  EXPECT_EQ(params["redirect_uri"], printing::oauth2::kRedirectURI);
  EXPECT_EQ(params["scope"], "scope1 scope2 scope3");
  EXPECT_FALSE(params["code_challenge"].empty());
  EXPECT_EQ(params["code_challenge_method"], "S256");
  EXPECT_FALSE(params["state"].empty());
}

TEST_F(PrintingOAuth2AuthorizationZoneTest,
       InitializationOfUnregisteredClient) {
  CallbackResult cr;
  CreateAuthorizationZone("");

  authorization_zone_->InitAuthorization("", BindResult(cr));
  ProcessMetadataRequest();
  ProcessRegistrationRequest("clientID_!@#$");
  EXPECT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
  const std::string authorization_url = cr.data;

  // Parse and verify the returned URL.
  auto question_mark = authorization_url.find('?');
  ASSERT_LT(question_mark, authorization_url.size());
  const std::string auth_host_path = authorization_url.substr(0, question_mark);
  base::flat_map<std::string, std::string> params;
  ASSERT_TRUE(
      ParseURLParameters(authorization_url.substr(question_mark + 1), params));
  EXPECT_EQ(params["client_id"], "clientID_!@#$");
  EXPECT_EQ(params.count("scope"), 0u);
}

TEST_F(PrintingOAuth2AuthorizationZoneTest, FirstAccessToken) {
  CallbackResult cr;
  chromeos::Uri ipp_endpoint("ipp://my.printer:1234/path");
  CreateAuthorizationZone("clientID_!@#$");
  authorization_zone_->InitAuthorization("", BindResult(cr));
  ProcessMetadataRequest();
  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
  auto resultant_url = SimulateAuthorization(cr.data, "auth_code_123", "");
  authorization_zone_->FinishAuthorization(GURL(resultant_url), BindResult(cr));
  ProcessFirstTokenRequest("auth_code_123", "access_TOKEN", "refresh_TOKEN");
  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
  EXPECT_TRUE(cr.data.empty());
}

TEST_F(PrintingOAuth2AuthorizationZoneTest, AuthorizationFail) {
  CallbackResult cr;
  chromeos::Uri ipp_endpoint("ipp://my.printer:1234/path");
  CreateAuthorizationZone("clientID_!@#$");
  authorization_zone_->InitAuthorization("", BindResult(cr));
  ProcessMetadataRequest();
  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
  auto resultant_url = SimulateAuthorizationError(cr.data, "weird_problem");
  authorization_zone_->FinishAuthorization(GURL(resultant_url), BindResult(cr));
  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kAccessDenied);
  // The error message contains "weird_problem".
  EXPECT_NE(cr.data.find("weird_problem"), std::string::npos);
}

TEST_F(PrintingOAuth2AuthorizationZoneTest,
       AuthorizationInvalidResponseEmptyCode) {
  CallbackResult cr;
  chromeos::Uri ipp_endpoint("ipp://my.printer:1234/path");
  CreateAuthorizationZone("clientID_!@#$");
  authorization_zone_->InitAuthorization("", BindResult(cr));
  ProcessMetadataRequest();
  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
  // The URL the browser was redirected to has empty "code" param.
  auto resultant_url = SimulateAuthorization(cr.data, "", "");
  authorization_zone_->FinishAuthorization(GURL(resultant_url), BindResult(cr));
  EXPECT_EQ(cr.status, printing::oauth2::StatusCode::kInvalidResponse);
  // The error message contains "code".
  EXPECT_NE(cr.data.find("code"), std::string::npos);
}

TEST_F(PrintingOAuth2AuthorizationZoneTest,
       AuthorizationInvalidResponseNoState) {
  CallbackResult cr;
  chromeos::Uri ipp_endpoint("ipp://my.printer:1234/path");
  CreateAuthorizationZone("clientID_!@#$");
  authorization_zone_->InitAuthorization("", BindResult(cr));
  ProcessMetadataRequest();
  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
  // The URL the browser was redirected to has missing "state" param.
  std::string resultant_url = printing::oauth2::kRedirectURI;
  resultant_url += "?code=authCode123";
  authorization_zone_->FinishAuthorization(GURL(resultant_url), BindResult(cr));
  EXPECT_EQ(cr.status, printing::oauth2::StatusCode::kInvalidResponse);
  // The error message contains "state".
  EXPECT_NE(cr.data.find("state"), std::string::npos);
}

TEST_F(PrintingOAuth2AuthorizationZoneTest, FirstAccessTokenFail) {
  CallbackResult cr;
  chromeos::Uri ipp_endpoint("ipp://my.printer:1234/path");
  CreateAuthorizationZone("clientID_!@#$");
  authorization_zone_->InitAuthorization("", BindResult(cr));
  ProcessMetadataRequest();
  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
  auto resultant_url = SimulateAuthorization(cr.data, "auth_code_123", "");
  authorization_zone_->FinishAuthorization(GURL(resultant_url), BindResult(cr));
  ProcessFirstTokenRequestError("auth_code_123", "my unknown error");
  EXPECT_EQ(cr.status, printing::oauth2::StatusCode::kAccessDenied);
  // The error message contains "my unknown error".
  EXPECT_NE(cr.data.find("my unknown error"), std::string::npos);
}

TEST_F(PrintingOAuth2AuthorizationZoneTest, TokenRefresh) {
  CallbackResult cr;
  chromeos::Uri ipp_endpoint("ipp://my.printer:1234/path");
  CreateAuthorizationZone("clientID_!@#$");
  authorization_zone_->InitAuthorization("", BindResult(cr));
  ProcessMetadataRequest();
  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
  auto resultant_url = SimulateAuthorization(cr.data, "auth_code_123", "");
  authorization_zone_->FinishAuthorization(GURL(resultant_url), BindResult(cr));
  ProcessFirstTokenRequest("auth_code_123", "access_TOKEN", "refresh_TOKEN");
  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
  authorization_zone_->GetEndpointAccessToken(ipp_endpoint, "", BindResult(cr));
  ProcessTokenExchangeRequest(ipp_endpoint, "access_TOKEN",
                              "endpoint_token_24$#D");
  ASSERT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
  authorization_zone_->MarkEndpointAccessTokenAsExpired(ipp_endpoint,
                                                        "endpoint_token_24$#D");
  authorization_zone_->GetEndpointAccessToken(ipp_endpoint, "", BindResult(cr));
  ProcessTokenExchangeRequestError(ipp_endpoint, "access_TOKEN",
                                   "invalid_grant");
  ProcessNextTokenRequest("refresh_TOKEN", "access_token2_w5%",
                          "refresh_token2_1dh");
  ProcessTokenExchangeRequest(ipp_endpoint, "access_token2_w5%",
                              "endpoint_token2_E46h");
  EXPECT_EQ(cr.status, printing::oauth2::StatusCode::kOK);
  EXPECT_EQ(cr.data, "endpoint_token2_E46h");
}

TEST_F(PrintingOAuth2AuthorizationZoneTest, ParallelRequestsWithScopes) {
  CallbackResult cr_ia1;
  CallbackResult cr_ia2;
  CallbackResult cr_ia3;
  chromeos::Uri ipp_endpoint_1("ipp://my.printer:1234/path");
  chromeos::Uri ipp_endpoint_2("ipp://my.other_printer:123/path");
  CreateAuthorizationZone("clientID");
  authorization_zone_->InitAuthorization("scope0", BindResult(cr_ia1));
  authorization_zone_->InitAuthorization("scope1 scope2", BindResult(cr_ia2));
  authorization_zone_->InitAuthorization("scope2", BindResult(cr_ia3));
  ProcessMetadataRequest();
  ASSERT_EQ(cr_ia1.status, printing::oauth2::StatusCode::kOK);
  ASSERT_EQ(cr_ia2.status, printing::oauth2::StatusCode::kOK);
  ASSERT_EQ(cr_ia3.status, printing::oauth2::StatusCode::kOK);
  auto auth_url_1 = SimulateAuthorization(cr_ia1.data, "auth_code_1", "scope0");
  auto auth_url_2 =
      SimulateAuthorization(cr_ia2.data, "auth_code_2", "scope1 scope2");
  auto auth_url_3 = SimulateAuthorization(cr_ia3.data, "auth_code_3", "scope2");
  authorization_zone_->FinishAuthorization(GURL(auth_url_3),
                                           BindResult(cr_ia3));
  authorization_zone_->FinishAuthorization(GURL(auth_url_1),
                                           BindResult(cr_ia1));
  authorization_zone_->FinishAuthorization(GURL(auth_url_2),
                                           BindResult(cr_ia2));
  ProcessFirstTokenRequest("auth_code_3", "acc_token_3", "ref_token_3");
  ProcessFirstTokenRequest("auth_code_1", "acc_token_1", "ref_token_1");
  ProcessFirstTokenRequest("auth_code_2", "acc_token_2", "ref_token_2");
  ASSERT_EQ(cr_ia1.status, printing::oauth2::StatusCode::kOK);
  ASSERT_EQ(cr_ia2.status, printing::oauth2::StatusCode::kOK);
  ASSERT_EQ(cr_ia3.status, printing::oauth2::StatusCode::kOK);
  authorization_zone_->GetEndpointAccessToken(ipp_endpoint_1, "scope0",
                                              BindResult(cr_ia1));
  authorization_zone_->GetEndpointAccessToken(ipp_endpoint_2, "scope1",
                                              BindResult(cr_ia2));
  authorization_zone_->GetEndpointAccessToken(ipp_endpoint_1, "scope2 scope1",
                                              BindResult(cr_ia3));
  ProcessTokenExchangeRequest(ipp_endpoint_1, "acc_token_1", "end_token_1");
  ProcessTokenExchangeRequest(ipp_endpoint_2, "acc_token_2", "end_token_2");
  // The third GetEndpointAccessToken(...) call used the first token as the
  // first one.
  EXPECT_EQ(cr_ia1.status, printing::oauth2::StatusCode::kOK);
  EXPECT_EQ(cr_ia2.status, printing::oauth2::StatusCode::kOK);
  EXPECT_EQ(cr_ia3.status, printing::oauth2::StatusCode::kOK);
  EXPECT_EQ(cr_ia1.data, "end_token_1");
  EXPECT_EQ(cr_ia2.data, "end_token_2");
  EXPECT_EQ(cr_ia3.data, "end_token_1");
}

TEST_F(PrintingOAuth2AuthorizationZoneTest, ParallelInitializations) {
  CreateAuthorizationZone("clientID");
  std::vector<CallbackResult> crs(kMaxNumberOfSessions + 1);

  // Too many initializations. The oldest one is going to be rejected.
  for (auto& cr : crs) {
    authorization_zone_->InitAuthorization("scope", BindResult(cr));
  }
  EXPECT_EQ(crs[0].status, printing::oauth2::StatusCode::kTooManySessions);
  ProcessMetadataRequest();
  for (size_t i = 1; i < crs.size(); ++i) {
    EXPECT_EQ(crs[i].status, printing::oauth2::StatusCode::kOK);
  }
}

TEST_F(PrintingOAuth2AuthorizationZoneTest, ParallelAuthorizations) {
  CreateAuthorizationZone("clientID");
  std::vector<CallbackResult> crs(kMaxNumberOfSessions + 1);

  // Call the first InitAuthorization(...) to go through initialization.
  authorization_zone_->InitAuthorization("", BindResult(crs[0]));
  ProcessMetadataRequest();
  ASSERT_EQ(crs[0].status, printing::oauth2::StatusCode::kOK);
  // Call InitAuthorization(...) kMaxNumberOfSessions times.
  for (size_t i = 1; i < crs.size(); ++i) {
    authorization_zone_->InitAuthorization("", BindResult(crs[i]));
    ASSERT_EQ(crs[i].status, printing::oauth2::StatusCode::kOK);
  }

  // Call all corresponding FinishAuthorization(...).
  for (size_t i = 0; i < crs.size(); ++i) {
    auto auth_url = SimulateAuthorization(
        crs[i].data, "auth_code_" + base::NumberToString(i), "");
    authorization_zone_->FinishAuthorization(GURL(auth_url),
                                             BindResult(crs[i]));
  }

  // The first call to FinishAuthorization(...) failed because the pending
  // authorization is missing (was removed as the oldest one). Other calls
  // succeeded.
  EXPECT_EQ(crs[0].status, printing::oauth2::StatusCode::kNoMatchingSession);
  for (size_t i = 1; i < crs.size(); ++i) {
    const std::string si = base::NumberToString(i);
    ProcessFirstTokenRequest("auth_code_" + si, "acc_token_" + si,
                             "ref_token_" + si);
    EXPECT_EQ(crs[i].status, printing::oauth2::StatusCode::kOK);
  }
}

TEST_F(PrintingOAuth2AuthorizationZoneTest, ParallelFirstTokenRequests) {
  CreateAuthorizationZone("clientID");
  std::vector<CallbackResult> crs(kMaxNumberOfSessions + 1);

  // Complete the first authorization and get an endpoint access token.
  authorization_zone_->InitAuthorization("scope0", BindResult(crs[0]));
  ProcessMetadataRequest();
  ASSERT_EQ(crs[0].status, printing::oauth2::StatusCode::kOK);
  auto auth_url_0 = SimulateAuthorization(crs[0].data, "auth_code_0", "scope0");
  authorization_zone_->FinishAuthorization(GURL(auth_url_0),
                                           BindResult(crs[0]));
  ProcessFirstTokenRequest("auth_code_0", "acc_token_0", "ref_token_0");
  ASSERT_EQ(crs[0].status, printing::oauth2::StatusCode::kOK);
  authorization_zone_->GetEndpointAccessToken(chromeos::Uri("ipp://whatever:1"),
                                              "scope0", BindResult(crs[0]));
  ProcessTokenExchangeRequest(chromeos::Uri("ipp://whatever:1"), "acc_token_0",
                              "xxx");
  EXPECT_EQ(crs[0].status, printing::oauth2::StatusCode::kOK);

  // Start kMaxNumberOfSessions other sessions (with different scope).
  for (size_t i = 1; i < crs.size(); ++i) {
    const std::string si = base::NumberToString(i);
    authorization_zone_->InitAuthorization("", BindResult(crs[i]));
    ASSERT_EQ(crs[i].status, printing::oauth2::StatusCode::kOK);
    auto auth_url = SimulateAuthorization(crs[i].data, "auth_code_" + si, "");
    authorization_zone_->FinishAuthorization(GURL(auth_url),
                                             BindResult(crs[i]));
    ProcessFirstTokenRequest("auth_code_" + si, "acc_token_" + si,
                             "ref_token_" + si);
    EXPECT_EQ(crs[i].status, printing::oauth2::StatusCode::kOK);
  }

  // The first session was closed because it was the oldest one and max allowed
  // number of sessions was reached.
  authorization_zone_->GetEndpointAccessToken(chromeos::Uri("ipp://whatever:2"),
                                              "scope0", BindResult(crs[0]));
  EXPECT_EQ(crs[0].status, printing::oauth2::StatusCode::kAuthorizationNeeded);
}

TEST_F(PrintingOAuth2AuthorizationZoneTest, CancellationDuringInitialization) {
  CreateAuthorizationZone("clientID");
  CallbackResult cr_0;
  CallbackResult cr_1;

  // Try to start two sessions and cancel before the first response from the
  // server returns.
  authorization_zone_->InitAuthorization("scope0", BindResult(cr_0));
  authorization_zone_->InitAuthorization("scope1", BindResult(cr_1));
  authorization_zone_->MarkAuthorizationZoneAsUntrusted();
  EXPECT_EQ(cr_0.status, StatusCode::kUntrustedAuthorizationServer);
  EXPECT_EQ(cr_1.status, StatusCode::kUntrustedAuthorizationServer);

  // Response from the server should not trigger anything.
  ProcessMetadataRequest();
  EXPECT_EQ(cr_0.status, StatusCode::kUntrustedAuthorizationServer);
  EXPECT_EQ(cr_1.status, StatusCode::kUntrustedAuthorizationServer);
}

TEST_F(PrintingOAuth2AuthorizationZoneTest, CancelExistingSessions) {
  CreateAuthorizationZone("clientID");
  CallbackResult cr_0;
  CallbackResult cr_1;

  // Start two sessions and send request for the first access token.
  authorization_zone_->InitAuthorization("scope0", BindResult(cr_0));
  authorization_zone_->InitAuthorization("scope1", BindResult(cr_1));
  ProcessMetadataRequest();
  ASSERT_EQ(cr_0.status, printing::oauth2::StatusCode::kOK);
  ASSERT_EQ(cr_1.status, printing::oauth2::StatusCode::kOK);
  auto auth_url_0 = SimulateAuthorization(cr_0.data, "auth_code_0", "scope0");
  auto auth_url_1 = SimulateAuthorization(cr_1.data, "auth_code_1", "scope1");
  authorization_zone_->FinishAuthorization(GURL(auth_url_0), BindResult(cr_0));
  authorization_zone_->FinishAuthorization(GURL(auth_url_1), BindResult(cr_1));

  // Get the access token for the first session and ask for an endpoint access
  // token.
  ProcessFirstTokenRequest("auth_code_0", "acc_token_0", "ref_token_0");
  ASSERT_EQ(cr_0.status, printing::oauth2::StatusCode::kOK);
  authorization_zone_->GetEndpointAccessToken(chromeos::Uri("ipp://printer0"),
                                              "scope0", BindResult(cr_0));

  // Cancel the zone. All pending callbacks should return.
  authorization_zone_->MarkAuthorizationZoneAsUntrusted();
  EXPECT_EQ(cr_0.status, StatusCode::kUntrustedAuthorizationServer);
  EXPECT_EQ(cr_1.status, StatusCode::kUntrustedAuthorizationServer);
}

}  // namespace
}  // namespace oauth2
}  // namespace printing
}  // namespace ash