1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/service/cloud_print/cloud_print_auth.h" 6 7 #include "base/bind.h" 8 #include "base/metrics/histogram.h" 9 #include "base/strings/string_util.h" 10 #include "chrome/common/cloud_print/cloud_print_constants.h" 11 #include "chrome/common/cloud_print/cloud_print_helpers.h" 12 #include "chrome/service/cloud_print/cloud_print_token_store.h" 13 #include "chrome/service/net/service_url_request_context_getter.h" 14 #include "chrome/service/service_process.h" 15 #include "google_apis/gaia/gaia_urls.h" 16 17 namespace cloud_print { 18 19 namespace { 20 21 enum CloudPrintAuthEvent { 22 AUTH_EVENT_ROBO_CREATE, 23 AUTH_EVENT_ROBO_SUCCEEDED, 24 AUTH_EVENT_ROBO_FAILED, 25 AUTH_EVENT_ROBO_JSON_ERROR, 26 AUTH_EVENT_ROBO_AUTH_ERROR, 27 AUTH_EVENT_AUTH_WITH_TOKEN, 28 AUTH_EVENT_AUTH_WITH_CODE, 29 AUTH_EVENT_TOKEN_RESPONSE, 30 AUTH_EVENT_REFRESH_REQUEST, 31 AUTH_EVENT_REFRESH_RESPONSE, 32 AUTH_EVENT_AUTH_ERROR, 33 AUTH_EVENT_NET_ERROR, 34 AUTH_EVENT_MAX 35 }; 36 37 } // namespace 38 39 CloudPrintAuth::CloudPrintAuth( 40 Client* client, 41 const GURL& cloud_print_server_url, 42 const gaia::OAuthClientInfo& oauth_client_info, 43 const std::string& proxy_id) 44 : client_(client), 45 oauth_client_info_(oauth_client_info), 46 cloud_print_server_url_(cloud_print_server_url), 47 proxy_id_(proxy_id) { 48 DCHECK(client); 49 } 50 51 void CloudPrintAuth::AuthenticateWithToken( 52 const std::string& cloud_print_token) { 53 VLOG(1) << "CP_AUTH: Authenticating with token"; 54 55 UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", AUTH_EVENT_ROBO_CREATE, 56 AUTH_EVENT_MAX); 57 58 client_login_token_ = cloud_print_token; 59 60 // We need to get the credentials of the robot here. 61 GURL get_authcode_url = GetUrlForGetAuthCode(cloud_print_server_url_, 62 oauth_client_info_.client_id, 63 proxy_id_); 64 request_ = CloudPrintURLFetcher::Create(); 65 request_->StartGetRequest(CloudPrintURLFetcher::REQUEST_AUTH_CODE, 66 get_authcode_url, this, 67 kCloudPrintAuthMaxRetryCount, std::string()); 68 } 69 70 void CloudPrintAuth::AuthenticateWithRobotToken( 71 const std::string& robot_oauth_refresh_token, 72 const std::string& robot_email) { 73 VLOG(1) << "CP_AUTH: Authenticating with robot token"; 74 75 UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", AUTH_EVENT_AUTH_WITH_TOKEN, 76 AUTH_EVENT_MAX); 77 78 robot_email_ = robot_email; 79 refresh_token_ = robot_oauth_refresh_token; 80 RefreshAccessToken(); 81 } 82 83 void CloudPrintAuth::AuthenticateWithRobotAuthCode( 84 const std::string& robot_oauth_auth_code, 85 const std::string& robot_email) { 86 VLOG(1) << "CP_AUTH: Authenticating with robot auth code"; 87 88 UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", AUTH_EVENT_AUTH_WITH_CODE, 89 AUTH_EVENT_MAX); 90 91 robot_email_ = robot_email; 92 // Now that we have an auth code we need to get the refresh and access tokens. 93 oauth_client_.reset(new gaia::GaiaOAuthClient( 94 g_service_process->GetServiceURLRequestContextGetter())); 95 oauth_client_->GetTokensFromAuthCode(oauth_client_info_, 96 robot_oauth_auth_code, 97 kCloudPrintAuthMaxRetryCount, 98 this); 99 } 100 101 void CloudPrintAuth::RefreshAccessToken() { 102 UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", AUTH_EVENT_REFRESH_REQUEST, 103 AUTH_EVENT_MAX); 104 oauth_client_.reset(new gaia::GaiaOAuthClient( 105 g_service_process->GetServiceURLRequestContextGetter())); 106 std::vector<std::string> empty_scope_list; // (Use scope from refresh token.) 107 oauth_client_->RefreshToken(oauth_client_info_, 108 refresh_token_, 109 empty_scope_list, 110 kCloudPrintAuthMaxRetryCount, 111 this); 112 } 113 114 void CloudPrintAuth::OnGetTokensResponse(const std::string& refresh_token, 115 const std::string& access_token, 116 int expires_in_seconds) { 117 UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", AUTH_EVENT_TOKEN_RESPONSE, 118 AUTH_EVENT_MAX); 119 refresh_token_ = refresh_token; 120 // After saving the refresh token, this is just like having just refreshed 121 // the access token. Just call OnRefreshTokenResponse. 122 OnRefreshTokenResponse(access_token, expires_in_seconds); 123 } 124 125 void CloudPrintAuth::OnRefreshTokenResponse(const std::string& access_token, 126 int expires_in_seconds) { 127 UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", AUTH_EVENT_REFRESH_RESPONSE, 128 AUTH_EVENT_MAX); 129 client_->OnAuthenticationComplete(access_token, refresh_token_, 130 robot_email_, user_email_); 131 132 // Schedule a task to refresh the access token again when it is about to 133 // expire. 134 DCHECK(expires_in_seconds > kTokenRefreshGracePeriodSecs); 135 base::TimeDelta refresh_delay = base::TimeDelta::FromSeconds( 136 expires_in_seconds - kTokenRefreshGracePeriodSecs); 137 base::MessageLoop::current()->PostDelayedTask( 138 FROM_HERE, 139 base::Bind(&CloudPrintAuth::RefreshAccessToken, this), 140 refresh_delay); 141 } 142 143 void CloudPrintAuth::OnOAuthError() { 144 UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", AUTH_EVENT_AUTH_ERROR, 145 AUTH_EVENT_MAX); 146 // Notify client about authentication error. 147 client_->OnInvalidCredentials(); 148 } 149 150 void CloudPrintAuth::OnNetworkError(int response_code) { 151 UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", AUTH_EVENT_NET_ERROR, 152 AUTH_EVENT_MAX); 153 // Since we specify infinite retries on network errors, this should never 154 // be called. 155 NOTREACHED() << 156 "OnNetworkError invoked when not expected, response code is " << 157 response_code; 158 } 159 160 CloudPrintURLFetcher::ResponseAction CloudPrintAuth::HandleJSONData( 161 const net::URLFetcher* source, 162 const GURL& url, 163 base::DictionaryValue* json_data, 164 bool succeeded) { 165 if (!succeeded) { 166 VLOG(1) << "CP_AUTH: Creating robot account failed"; 167 UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", 168 AUTH_EVENT_ROBO_FAILED, 169 AUTH_EVENT_MAX); 170 client_->OnInvalidCredentials(); 171 return CloudPrintURLFetcher::STOP_PROCESSING; 172 } 173 174 std::string auth_code; 175 if (!json_data->GetString(kOAuthCodeValue, &auth_code)) { 176 VLOG(1) << "CP_AUTH: Creating robot account returned invalid json response"; 177 UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", 178 AUTH_EVENT_ROBO_JSON_ERROR, 179 AUTH_EVENT_MAX); 180 client_->OnInvalidCredentials(); 181 return CloudPrintURLFetcher::STOP_PROCESSING; 182 } 183 184 UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", 185 AUTH_EVENT_ROBO_SUCCEEDED, 186 AUTH_EVENT_MAX); 187 188 json_data->GetString(kXMPPJidValue, &robot_email_); 189 // Now that we have an auth code we need to get the refresh and access tokens. 190 oauth_client_.reset(new gaia::GaiaOAuthClient( 191 g_service_process->GetServiceURLRequestContextGetter())); 192 oauth_client_->GetTokensFromAuthCode(oauth_client_info_, 193 auth_code, 194 kCloudPrintAPIMaxRetryCount, 195 this); 196 197 return CloudPrintURLFetcher::STOP_PROCESSING; 198 } 199 200 CloudPrintURLFetcher::ResponseAction CloudPrintAuth::OnRequestAuthError() { 201 VLOG(1) << "CP_AUTH: Creating robot account authentication error"; 202 203 UMA_HISTOGRAM_ENUMERATION("CloudPrint.AuthEvent", 204 AUTH_EVENT_ROBO_AUTH_ERROR, 205 AUTH_EVENT_MAX); 206 207 // Notify client about authentication error. 208 client_->OnInvalidCredentials(); 209 return CloudPrintURLFetcher::STOP_PROCESSING; 210 } 211 212 std::string CloudPrintAuth::GetAuthHeader() { 213 DCHECK(!client_login_token_.empty()); 214 std::string header; 215 header = "Authorization: GoogleLogin auth="; 216 header += client_login_token_; 217 return header; 218 } 219 220 CloudPrintAuth::~CloudPrintAuth() {} 221 222 } // namespace cloud_print 223