// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/services/device_sync/cryptauth_client_impl.h"
#include <memory>
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_devicesync.pb.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_enrollment.pb.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h"
#include "chromeos/ash/services/device_sync/switches.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/access_token_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace ash {
namespace device_sync {
namespace {
// -------------------- CryptAuth v1 Endpoints --------------------
// Default URL of Google APIs endpoint hosting CryptAuth v1.
const char kDefaultCryptAuthV1HTTPHost[] = "https://www.googleapis.com";
// URL subpath hosting the CryptAuth v1 service.
const char kCryptAuthV1Path[] = "cryptauth/v1/";
// URL subpaths for each CryptAuth v1 API.
const char kGetMyDevicesPath[] = "deviceSync/getmydevices";
const char kFindEligibleUnlockDevicesPath[] =
"deviceSync/findeligibleunlockdevices";
const char kFindEligibleForPromotionPath[] =
"deviceSync/findeligibleforpromotion";
const char kSendDeviceSyncTicklePath[] = "deviceSync/senddevicesynctickle";
const char kToggleEasyUnlockPath[] = "deviceSync/toggleeasyunlock";
const char kSetupEnrollmentPath[] = "enrollment/setup";
const char kFinishEnrollmentPath[] = "enrollment/finish";
// -------------------- CryptAuth v2 Endpoints --------------------
// Default URL of Google APIs endpoint hosting CryptAuth v2 Enrollment.
const char kDefaultCryptAuthV2EnrollmentHTTPHost[] =
"https://cryptauthenrollment.googleapis.com";
// Default URL of Google APIs endpoint hosting CryptAuth v2 DeviceSync.
const char kDefaultCryptAuthV2DeviceSyncHTTPHost[] =
"https://cryptauthdevicesync.googleapis.com";
// URL subpaths for each CryptAuth v2 API endpoint.
// Note: Although "v1" is part of the path names, these are in fact v2 API
// endpoints. Also, the "/" is necessary for GURL::Resolve() to parse the
// paths correctly; otherwise, ":" is interpreted as a scheme delimiter.
const char kSyncKeysPath[] = "/v1:syncKeys";
const char kEnrollKeysPath[] = "/v1:enrollKeys";
const char kSyncMetadataPath[] = "/v1:syncMetadata";
const char kShareGroupPrivateKeyPath[] = "/v1:shareGroupPrivateKey";
const char kBatchNotifyGroupDevicesPath[] = "/v1:batchNotifyGroupDevices";
const char kBatchGetFeatureStatusesPath[] = "/v1:batchGetFeatureStatuses";
const char kBatchSetFeatureStatusesPath[] = "/v1:batchSetFeatureStatuses";
const char kGetDevicesActivityStatusPath[] = "/v1:getDevicesActivityStatus";
const char kCryptAuthOAuth2Scope[] =
"https://www.googleapis.com/auth/cryptauth";
// Creates the full CryptAuth v1 URL for endpoint to the API with
// |request_path|.
GURL CreateV1RequestUrl(const std::string& request_path) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
GURL google_apis_url = command_line->HasSwitch(switches::kCryptAuthHTTPHost)
? GURL(command_line->GetSwitchValueASCII(
switches::kCryptAuthHTTPHost))
: GURL(kDefaultCryptAuthV1HTTPHost);
return google_apis_url.Resolve(kCryptAuthV1Path + request_path);
}
// Creates the full URL for endpoint to the CryptAuth v2 Enrollment API with
// |request_path|.
GURL CreateV2EnrollmentRequestUrl(const std::string& request_path) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
GURL google_apis_url =
command_line->HasSwitch(switches::kCryptAuthV2EnrollmentHTTPHost)
? GURL(command_line->GetSwitchValueASCII(
switches::kCryptAuthV2EnrollmentHTTPHost))
: GURL(kDefaultCryptAuthV2EnrollmentHTTPHost);
return google_apis_url.Resolve(request_path);
}
// Creates the full URL for endpoint to the CryptAuth v2 DeviceSync API with
// |request_path|.
GURL CreateV2DeviceSyncRequestUrl(const std::string& request_path) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
GURL google_apis_url =
command_line->HasSwitch(switches::kCryptAuthV2DeviceSyncHTTPHost)
? GURL(command_line->GetSwitchValueASCII(
switches::kCryptAuthV2DeviceSyncHTTPHost))
: GURL(kDefaultCryptAuthV2DeviceSyncHTTPHost);
return google_apis_url.Resolve(request_path);
}
} // namespace
CryptAuthClientImpl::CryptAuthClientImpl(
std::unique_ptr<CryptAuthApiCallFlow> api_call_flow,
signin::IdentityManager* identity_manager,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const cryptauth::DeviceClassifier& device_classifier)
: api_call_flow_(std::move(api_call_flow)),
identity_manager_(identity_manager),
url_loader_factory_(std::move(url_loader_factory)),
device_classifier_(device_classifier),
has_call_started_(false) {}
CryptAuthClientImpl::~CryptAuthClientImpl() {}
void CryptAuthClientImpl::GetMyDevices(
const cryptauth::GetMyDevicesRequest& request,
GetMyDevicesCallback callback,
ErrorCallback error_callback,
const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
MakeApiCall(CreateV1RequestUrl(kGetMyDevicesPath), RequestType::kPost,
RequestWithDeviceClassifierSet(request).SerializeAsString(),
std::nullopt /* request_as_query_parameters */,
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
void CryptAuthClientImpl::FindEligibleUnlockDevices(
const cryptauth::FindEligibleUnlockDevicesRequest& request,
FindEligibleUnlockDevicesCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"cryptauth_find_eligible_unlock_devices", "oauth2_api_call_flow",
R"(
semantics {
sender: "CryptAuth Device Manager"
description:
"Gets the list of mobile devices that can be used by Smart Lock to "
"unlock the current device."
trigger:
"This request is sent when the user starts the Smart Lock setup flow."
data: "OAuth 2.0 token and the device's public key."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled in settings, but the request will "
"only be sent if the user explicitly tries to enable Smart Lock "
"(EasyUnlock), i.e. starts the setup flow."
chrome_policy {
EasyUnlockAllowed {
EasyUnlockAllowed: false
}
}
})");
MakeApiCall(
CreateV1RequestUrl(kFindEligibleUnlockDevicesPath), RequestType::kPost,
RequestWithDeviceClassifierSet(request).SerializeAsString(),
std::nullopt /* request_as_query_parameters */, std::move(callback),
std::move(error_callback), partial_traffic_annotation);
}
void CryptAuthClientImpl::FindEligibleForPromotion(
const cryptauth::FindEligibleForPromotionRequest& request,
FindEligibleForPromotionCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"cryptauth_find_eligible_for_promotion", "oauth2_api_call_flow",
R"(
semantics {
sender: "Promotion Manager"
description:
"Return whether the current device is eligible for a Smart Lock promotion."
trigger:
"This request is sent when the user starts the Smart Lock setup flow."
data: "OAuth 2.0 token and the device's public key."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled in settings"
chrome_policy {
EasyUnlockAllowed {
EasyUnlockAllowed: false
}
}
})");
MakeApiCall(
CreateV1RequestUrl(kFindEligibleForPromotionPath), RequestType::kPost,
RequestWithDeviceClassifierSet(request).SerializeAsString(),
std::nullopt /* request_as_query_parameters */, std::move(callback),
std::move(error_callback), partial_traffic_annotation);
}
void CryptAuthClientImpl::SendDeviceSyncTickle(
const cryptauth::SendDeviceSyncTickleRequest& request,
SendDeviceSyncTickleCallback callback,
ErrorCallback error_callback,
const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
MakeApiCall(CreateV1RequestUrl(kSendDeviceSyncTicklePath), RequestType::kPost,
RequestWithDeviceClassifierSet(request).SerializeAsString(),
std::nullopt /* request_as_query_parameters */,
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
void CryptAuthClientImpl::ToggleEasyUnlock(
const cryptauth::ToggleEasyUnlockRequest& request,
ToggleEasyUnlockCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation("cryptauth_toggle_easyunlock",
"oauth2_api_call_flow", R"(
semantics {
sender: "CryptAuth Device Manager"
description: "Enables Smart Lock (EasyUnlock) for the current device."
trigger:
"This request is send after the user goes through the EasyUnlock "
"setup flow."
data: "OAuth 2.0 token and the device public key."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled in settings, but the request will "
"only be send if the user explicitly enables Smart Lock "
"(EasyUnlock), i.e. uccessfully complete the setup flow."
chrome_policy {
EasyUnlockAllowed {
EasyUnlockAllowed: false
}
}
})");
MakeApiCall(CreateV1RequestUrl(kToggleEasyUnlockPath), RequestType::kPost,
RequestWithDeviceClassifierSet(request).SerializeAsString(),
std::nullopt /* request_as_query_parameters */,
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
void CryptAuthClientImpl::SetupEnrollment(
const cryptauth::SetupEnrollmentRequest& request,
SetupEnrollmentCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"cryptauth_enrollment_flow_setup", "oauth2_api_call_flow", R"(
semantics {
sender: "CryptAuth Device Manager"
description: "Starts the CryptAuth registration flow."
trigger:
"Occurs periodically, at least once a month, because if the device "
"does not re-enroll for more than a specific number of days "
"(currently 45) it will be removed from the server."
data:
"Various device information (public key, bluetooth MAC address, "
"model, OS version, screen size, manufacturer, has screen lock "
"enabled), and OAuth 2.0 token."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled by settings. However, this request "
"is made only for signed-in users."
chrome_policy {
SigninAllowed {
SigninAllowed: false
}
}
})");
MakeApiCall(CreateV1RequestUrl(kSetupEnrollmentPath), RequestType::kPost,
RequestWithDeviceClassifierSet(request).SerializeAsString(),
std::nullopt /* request_as_query_parameters */,
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
void CryptAuthClientImpl::FinishEnrollment(
const cryptauth::FinishEnrollmentRequest& request,
FinishEnrollmentCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"cryptauth_enrollment_flow_finish", "oauth2_api_call_flow", R"(
semantics {
sender: "CryptAuth Device Manager"
description: "Finishes the CryptAuth registration flow."
trigger:
"Occurs periodically, at least once a month, because if the device "
"does not re-enroll for more than a specific number of days "
"(currently 45) it will be removed from the server."
data: "OAuth 2.0 token."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled by settings. However, this request "
"is made only for signed-in users."
chrome_policy {
SigninAllowed {
SigninAllowed: false
}
}
})");
MakeApiCall(CreateV1RequestUrl(kFinishEnrollmentPath), RequestType::kPost,
RequestWithDeviceClassifierSet(request).SerializeAsString(),
std::nullopt /* request_as_query_parameters */,
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
void CryptAuthClientImpl::SyncKeys(const cryptauthv2::SyncKeysRequest& request,
SyncKeysCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"cryptauth_v2_enrollment_flow_sync_keys", "oauth2_api_call_flow", R"(
semantics {
sender: "CryptAuth V2 Enroller"
description: "Starts the CryptAuth v2 Enrollment flow."
trigger:
"Occurs periodically at a period provided by CryptAuth in the "
"previous SyncKeysResponse's ClientDirective. The client can also "
"bypass the periodic schedule and immediately trigger a "
"SyncKeysRequest."
data:
"A list of all keys used by the client; metadata about the "
"local device's feature support, hardware, etc.; and an OAuth 2.0 "
"token."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled by settings. However, this request "
"is made only for signed-in users."
chrome_policy {
SigninAllowed {
SigninAllowed: false
}
}
})");
MakeApiCall(CreateV2EnrollmentRequestUrl(kSyncKeysPath), RequestType::kPost,
request.SerializeAsString(),
std::nullopt /* request_as_query_parameters */,
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
void CryptAuthClientImpl::EnrollKeys(
const cryptauthv2::EnrollKeysRequest& request,
EnrollKeysCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"cryptauth_v2_enrollment_flow_enroll_keys", "oauth2_api_call_flow",
R"(
semantics {
sender: "CryptAuth V2 Enroller"
description: "Finishes the CryptAuth v2 Enrollment flow."
trigger:
"The second part of the v2 Enrollment flow. Sent after the client "
"receives a SyncKeysResponse from CryptAuth, requesting the client "
"create new keys."
data:
"A list of newly created key material and necessary proofs for "
"verifying the keys."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled by settings. However, this request "
"is made only for signed-in users."
chrome_policy {
SigninAllowed {
SigninAllowed: false
}
}
})");
MakeApiCall(CreateV2EnrollmentRequestUrl(kEnrollKeysPath), RequestType::kPost,
request.SerializeAsString(),
std::nullopt /* request_as_query_parameters */,
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
void CryptAuthClientImpl::SyncMetadata(
const cryptauthv2::SyncMetadataRequest& request,
SyncMetadataCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"cryptauth_v2_devicesync_sync_metadata", "oauth2_api_call_flow",
R"(
semantics {
sender: "CryptAuth Device Syncer"
description:
"Sends device metadata to CryptAuth and receives metadata data for "
"the user's other devices."
trigger:
"CryptAuth will potentially instruct the client to invoke "
"SyncMetadata at the end of enrollment flows, which occur "
"periodically, or via GCM messages. There is no dedicated periodic "
"scheduling. The client can also force a SyncMetadataRequest."
data:
"Sends the device's encrypted metadata. Receives encrypted metadata "
"from other user devices. Can potentially receive the group public "
"key and/or the encrypted group private key, used for the encryption "
"and decryption of all device metadata."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled by settings. However, this request "
"is made only for signed-in users."
chrome_policy {
SigninAllowed {
SigninAllowed: false
}
}
})");
MakeApiCall(CreateV2DeviceSyncRequestUrl(kSyncMetadataPath),
RequestType::kPost, request.SerializeAsString(),
std::nullopt /* request_as_query_parameters */,
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
void CryptAuthClientImpl::ShareGroupPrivateKey(
const cryptauthv2::ShareGroupPrivateKeyRequest& request,
ShareGroupPrivateKeyCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"cryptauth_v2_devicesync_share_group_private_key",
"oauth2_api_call_flow",
R"(
semantics {
sender: "CryptAuth Device Syncer"
description:
"The device shares the group private key by encrypting it with the "
"public key of the user's other devices."
trigger:
"If the SyncMetadataResponse indicates that other user devices need "
"the group private key, then the client immediately invokes "
"ShareGroupPrivateKey."
data:
"The group private key encrypted with the public key of other user "
"devices."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled by settings. However, this request "
"is made only for signed-in users."
chrome_policy {
SigninAllowed {
SigninAllowed: false
}
}
})");
MakeApiCall(CreateV2DeviceSyncRequestUrl(kShareGroupPrivateKeyPath),
RequestType::kPost, request.SerializeAsString(),
std::nullopt /* request_as_query_parameters */,
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
void CryptAuthClientImpl::BatchNotifyGroupDevices(
const cryptauthv2::BatchNotifyGroupDevicesRequest& request,
BatchNotifyGroupDevicesCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"cryptauth_v2_devicesync_batch_notify_group_devices",
"oauth2_api_call_flow",
R"(
semantics {
sender: "CryptAuth Device Notifier"
description:
"The client sends a list of the user's devices that it wants to "
"tickle via a GCM message."
trigger:
"The DeviceSync service has a NotifyDevices() method that triggers "
"this API call. Currently, that method is only used by the "
"multi-device host verifier to alert the current multi-device host "
"that it needs to enable its individual multi-device features."
data:
"The list of device IDs to notify as well as a specification of the "
"the CryptAuth service (Enrollment or DeviceSync) and feature "
"relevant to the tickle."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled by settings. However, this request "
"is made only for signed-in users."
chrome_policy {
SigninAllowed {
SigninAllowed: false
}
}
})");
MakeApiCall(
CreateV2DeviceSyncRequestUrl(kBatchNotifyGroupDevicesPath),
RequestType::kGet, std::nullopt /* serialized_request */,
cryptauthv2::BatchNotifyGroupDevicesRequestToQueryParameters(request),
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
void CryptAuthClientImpl::BatchGetFeatureStatuses(
const cryptauthv2::BatchGetFeatureStatusesRequest& request,
BatchGetFeatureStatusesCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"cryptauth_v2_devicesync_batch_get_feature_statuses",
"oauth2_api_call_flow",
R"(
semantics {
sender: "CryptAuth Device Syncer"
description:
"The client queries CryptAuth for the state of features on the "
"user's devices, for example, whether or not Magic Tether is enabled "
"on any of the user's phones."
trigger:
"Called after SyncMetadata as part of the v2 DeviceSync flow."
data: "The user device IDs and feature types to query."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled by settings. However, this request "
"is made only for signed-in users."
chrome_policy {
SigninAllowed {
SigninAllowed: false
}
}
})");
MakeApiCall(
CreateV2DeviceSyncRequestUrl(kBatchGetFeatureStatusesPath),
RequestType::kGet, std::nullopt /* serialized_request */,
cryptauthv2::BatchGetFeatureStatusesRequestToQueryParameters(request),
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
void CryptAuthClientImpl::BatchSetFeatureStatuses(
const cryptauthv2::BatchSetFeatureStatusesRequest& request,
BatchSetFeatureStatusesCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"cryptauth_v2_devicesync_batch_set_feature_statuses",
"oauth2_api_call_flow",
R"(
semantics {
sender: "CryptAuth Feature Status Setter"
description:
"The client requests CryptAuth to set the state of various features "
"for the user's devices."
trigger:
"The DeviceSync service has a SetFeatureStatus() method that "
"triggers this API call. Currently, that method is only used by the "
"multi-device host backend delegate to enable (disable) the "
"kBetterTogetherHost bit for the desired (current) multi-device host."
data: "User device IDs and feature state specifications."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled by settings. However, this request "
"is made only for signed-in users."
chrome_policy {
SigninAllowed {
SigninAllowed: false
}
}
})");
MakeApiCall(CreateV2DeviceSyncRequestUrl(kBatchSetFeatureStatusesPath),
RequestType::kPost, request.SerializeAsString(),
std::nullopt /* request_as_query_parameters */,
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
void CryptAuthClientImpl::GetDevicesActivityStatus(
const cryptauthv2::GetDevicesActivityStatusRequest& request,
GetDevicesActivityStatusCallback callback,
ErrorCallback error_callback) {
net::PartialNetworkTrafficAnnotationTag partial_traffic_annotation =
net::DefinePartialNetworkTrafficAnnotation(
"cryptauth_v2_devicesync_get_devices_activity_status",
"oauth2_api_call_flow",
R"(
semantics {
sender: "CryptAuth Device Activity Getter"
description:
"The client queries CryptAuth for the activity of status of the "
"user's devices."
trigger:
"The DeviceSync service has a GetDevicesActivityStatus() method that "
"triggers this API call. Currently, that method is only used by the "
"eligible-host-devices provider in order to better organize the "
"drop-down list used for multi-device setup."
data: "User device ID."
destination: GOOGLE_OWNED_SERVICE
}
policy {
setting:
"This feature cannot be disabled by settings. However, this request "
"is made only for signed-in users."
chrome_policy {
SigninAllowed {
SigninAllowed: false
}
}
})");
MakeApiCall(
CreateV2DeviceSyncRequestUrl(kGetDevicesActivityStatusPath),
RequestType::kGet, std::nullopt /* serialized_request */,
cryptauthv2::GetDevicesActivityStatusRequestToQueryParameters(request),
std::move(callback), std::move(error_callback),
partial_traffic_annotation);
}
std::string CryptAuthClientImpl::GetAccessTokenUsed() {
return access_token_used_;
}
template <class ResponseProto>
void CryptAuthClientImpl::MakeApiCall(
const GURL& request_url,
RequestType request_type,
const std::optional<std::string>& serialized_request,
const std::optional<std::vector<std::pair<std::string, std::string>>>&
request_as_query_parameters,
base::OnceCallback<void(const ResponseProto&)> response_callback,
ErrorCallback error_callback,
const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
if (has_call_started_) {
PA_LOG(ERROR) << "CryptAuthClientImpl::MakeApiCall(): Tried to make an API "
<< "call, but the client had already been used.";
NOTREACHED_IN_MIGRATION();
return;
}
has_call_started_ = true;
api_call_flow_->SetPartialNetworkTrafficAnnotation(
partial_traffic_annotation);
request_url_ = request_url;
error_callback_ = std::move(error_callback);
OAuth2AccessTokenManager::ScopeSet scopes;
scopes.insert(kCryptAuthOAuth2Scope);
access_token_fetcher_ =
std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
"cryptauth_client", identity_manager_, scopes,
base::BindOnce(
&CryptAuthClientImpl::OnAccessTokenFetched<ResponseProto>,
weak_ptr_factory_.GetWeakPtr(), request_type, serialized_request,
request_as_query_parameters, std::move(response_callback)),
signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable,
signin::ConsentLevel::kSignin);
}
template <class ResponseProto>
void CryptAuthClientImpl::OnAccessTokenFetched(
RequestType request_type,
const std::optional<std::string>& serialized_request,
const std::optional<std::vector<std::pair<std::string, std::string>>>&
request_as_query_parameters,
base::OnceCallback<void(const ResponseProto&)> response_callback,
GoogleServiceAuthError error,
signin::AccessTokenInfo access_token_info) {
access_token_fetcher_.reset();
if (error.state() != GoogleServiceAuthError::NONE) {
OnApiCallFailed(NetworkRequestError::kAuthenticationError);
return;
}
access_token_used_ = access_token_info.token;
switch (request_type) {
case RequestType::kGet:
DCHECK(request_as_query_parameters && !serialized_request);
api_call_flow_->StartGetRequest(
request_url_, *request_as_query_parameters, url_loader_factory_,
access_token_used_,
base::BindOnce(&CryptAuthClientImpl::OnFlowSuccess<ResponseProto>,
weak_ptr_factory_.GetWeakPtr(),
std::move(response_callback)),
base::BindOnce(&CryptAuthClientImpl::OnApiCallFailed,
weak_ptr_factory_.GetWeakPtr()));
break;
case RequestType::kPost:
DCHECK(serialized_request && !request_as_query_parameters);
api_call_flow_->StartPostRequest(
request_url_, *serialized_request, url_loader_factory_,
access_token_used_,
base::BindOnce(&CryptAuthClientImpl::OnFlowSuccess<ResponseProto>,
weak_ptr_factory_.GetWeakPtr(),
std::move(response_callback)),
base::BindOnce(&CryptAuthClientImpl::OnApiCallFailed,
weak_ptr_factory_.GetWeakPtr()));
break;
}
}
template <class ResponseProto>
void CryptAuthClientImpl::OnFlowSuccess(
base::OnceCallback<void(const ResponseProto&)> result_callback,
const std::string& serialized_response) {
ResponseProto response;
if (!response.ParseFromString(serialized_response)) {
OnApiCallFailed(NetworkRequestError::kResponseMalformed);
return;
}
std::move(result_callback).Run(response);
}
void CryptAuthClientImpl::OnApiCallFailed(NetworkRequestError error) {
std::move(error_callback_).Run(error);
}
template <class RequestProto>
RequestProto CryptAuthClientImpl::RequestWithDeviceClassifierSet(
const RequestProto& request) {
RequestProto request_copy(request);
request_copy.mutable_device_classifier()->CopyFrom(device_classifier_);
return request_copy;
}
// CryptAuthClientFactoryImpl
CryptAuthClientFactoryImpl::CryptAuthClientFactoryImpl(
signin::IdentityManager* identity_manager,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const cryptauth::DeviceClassifier& device_classifier)
: identity_manager_(identity_manager),
url_loader_factory_(std::move(url_loader_factory)),
device_classifier_(device_classifier) {}
CryptAuthClientFactoryImpl::~CryptAuthClientFactoryImpl() {}
std::unique_ptr<CryptAuthClient> CryptAuthClientFactoryImpl::CreateInstance() {
return std::make_unique<CryptAuthClientImpl>(
base::WrapUnique(new CryptAuthApiCallFlow()), identity_manager_,
url_loader_factory_, device_classifier_);
}
} // namespace device_sync
} // namespace ash