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
CloudPrintAuth(Client * client,const GURL & cloud_print_server_url,const gaia::OAuthClientInfo & oauth_client_info,const std::string & proxy_id)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
AuthenticateWithToken(const std::string & cloud_print_token)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
AuthenticateWithRobotToken(const std::string & robot_oauth_refresh_token,const std::string & robot_email)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
AuthenticateWithRobotAuthCode(const std::string & robot_oauth_auth_code,const std::string & robot_email)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
RefreshAccessToken()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
OnGetTokensResponse(const std::string & refresh_token,const std::string & access_token,int expires_in_seconds)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
OnRefreshTokenResponse(const std::string & access_token,int expires_in_seconds)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
OnOAuthError()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
OnNetworkError(int response_code)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
HandleJSONData(const net::URLFetcher * source,const GURL & url,base::DictionaryValue * json_data,bool succeeded)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
OnRequestAuthError()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
GetAuthHeader()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
~CloudPrintAuth()220 CloudPrintAuth::~CloudPrintAuth() {}
221
222 } // namespace cloud_print
223