cloud_print_auth.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
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/string_util.h"
9#include "chrome/common/cloud_print/cloud_print_constants.h"
10#include "chrome/common/cloud_print/cloud_print_helpers.h"
11#include "chrome/service/cloud_print/cloud_print_token_store.h"
12#include "chrome/service/gaia/service_gaia_authenticator.h"
13#include "chrome/service/net/service_url_request_context.h"
14#include "chrome/service/service_process.h"
15#include "google_apis/gaia/gaia_urls.h"
16
17namespace cloud_print {
18
19CloudPrintAuth::CloudPrintAuth(
20    Client* client,
21    const GURL& cloud_print_server_url,
22    const gaia::OAuthClientInfo& oauth_client_info,
23    const std::string& proxy_id)
24      : client_(client),
25        oauth_client_info_(oauth_client_info),
26        cloud_print_server_url_(cloud_print_server_url),
27        proxy_id_(proxy_id) {
28  DCHECK(client);
29}
30
31void CloudPrintAuth::AuthenticateWithLsid(
32    const std::string& lsid,
33    const std::string& last_robot_refresh_token,
34    const std::string& last_robot_email,
35    const std::string& last_user_email) {
36  // Keeping VLOGs for Cloud Print proxy logging. It is convinient for finding
37  // issues with GCP in the field, where only release version is avaialble.
38  VLOG(1) << "CP_AUTH: Authenticating with LSID";
39  scoped_refptr<ServiceGaiaAuthenticator> gaia_auth_for_print(
40      new ServiceGaiaAuthenticator(
41          kProxyAuthUserAgent, kCloudPrintGaiaServiceId,
42          GaiaUrls::GetInstance()->client_login_url(),
43          g_service_process->io_thread()->message_loop_proxy()));
44  gaia_auth_for_print->set_message_loop(base::MessageLoop::current());
45  if (gaia_auth_for_print->AuthenticateWithLsid(lsid)) {
46    // Stash away the user email so we can save it in prefs.
47    user_email_ = gaia_auth_for_print->email();
48    // If the same user is re-enabling Cloud Print and we have stashed robot
49    // credentials, we will use those.
50    if ((0 == base::strcasecmp(user_email_.c_str(), last_user_email.c_str())) &&
51        !last_robot_refresh_token.empty() &&
52        !last_robot_email.empty()) {
53      AuthenticateWithRobotToken(last_robot_refresh_token,
54                                 last_robot_email);
55    }
56    AuthenticateWithToken(gaia_auth_for_print->auth_token());
57  } else {
58    // Notify client about authentication error.
59    client_->OnInvalidCredentials();
60  }
61}
62
63void CloudPrintAuth::AuthenticateWithToken(
64    const std::string& cloud_print_token) {
65  VLOG(1) << "CP_AUTH: Authenticating with token";
66
67  client_login_token_ = cloud_print_token;
68
69  // We need to get the credentials of the robot here.
70  GURL get_authcode_url = GetUrlForGetAuthCode(cloud_print_server_url_,
71                                               oauth_client_info_.client_id,
72                                               proxy_id_);
73  request_ = CloudPrintURLFetcher::Create();
74  request_->StartGetRequest(get_authcode_url,
75                            this,
76                            kCloudPrintAuthMaxRetryCount,
77                            std::string());
78}
79
80void CloudPrintAuth::AuthenticateWithRobotToken(
81    const std::string& robot_oauth_refresh_token,
82    const std::string& robot_email) {
83  VLOG(1) << "CP_AUTH: Authenticating with robot token";
84
85  robot_email_ = robot_email;
86  refresh_token_ = robot_oauth_refresh_token;
87  RefreshAccessToken();
88}
89
90void CloudPrintAuth::AuthenticateWithRobotAuthCode(
91    const std::string& robot_oauth_auth_code,
92    const std::string& robot_email) {
93  VLOG(1) << "CP_AUTH: Authenticating with robot auth code";
94
95  robot_email_ = robot_email;
96  // Now that we have an auth code we need to get the refresh and access tokens.
97  oauth_client_.reset(new gaia::GaiaOAuthClient(
98      GaiaUrls::GetInstance()->oauth2_token_url(),
99      g_service_process->GetServiceURLRequestContextGetter()));
100  oauth_client_->GetTokensFromAuthCode(oauth_client_info_,
101                                       robot_oauth_auth_code,
102                                       kCloudPrintAuthMaxRetryCount,
103                                       this);
104}
105
106void CloudPrintAuth::RefreshAccessToken() {
107  oauth_client_.reset(new gaia::GaiaOAuthClient(
108      GaiaUrls::GetInstance()->oauth2_token_url(),
109      g_service_process->GetServiceURLRequestContextGetter()));
110  oauth_client_->RefreshToken(oauth_client_info_,
111                              refresh_token_,
112                              kCloudPrintAuthMaxRetryCount,
113                              this);
114}
115
116void CloudPrintAuth::OnGetTokensResponse(const std::string& refresh_token,
117                                         const std::string& access_token,
118                                         int expires_in_seconds) {
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
125void CloudPrintAuth::OnRefreshTokenResponse(const std::string& access_token,
126                                            int expires_in_seconds) {
127  client_->OnAuthenticationComplete(access_token, refresh_token_,
128                                    robot_email_, user_email_);
129
130  // Schedule a task to refresh the access token again when it is about to
131  // expire.
132  DCHECK(expires_in_seconds > kTokenRefreshGracePeriodSecs);
133  base::TimeDelta refresh_delay = base::TimeDelta::FromSeconds(
134      expires_in_seconds - kTokenRefreshGracePeriodSecs);
135  base::MessageLoop::current()->PostDelayedTask(
136      FROM_HERE,
137      base::Bind(&CloudPrintAuth::RefreshAccessToken, this),
138      refresh_delay);
139}
140
141void CloudPrintAuth::OnOAuthError() {
142  // Notify client about authentication error.
143  client_->OnInvalidCredentials();
144}
145
146void CloudPrintAuth::OnNetworkError(int response_code) {
147  // Since we specify infinite retries on network errors, this should never
148  // be called.
149  NOTREACHED() <<
150      "OnNetworkError invoked when not expected, response code is " <<
151      response_code;
152}
153
154CloudPrintURLFetcher::ResponseAction CloudPrintAuth::HandleJSONData(
155    const net::URLFetcher* source,
156    const GURL& url,
157    base::DictionaryValue* json_data,
158    bool succeeded) {
159  if (!succeeded) {
160    VLOG(1) << "CP_AUTH: Creating robot account failed";
161    client_->OnInvalidCredentials();
162    return CloudPrintURLFetcher::STOP_PROCESSING;
163  }
164
165  std::string auth_code;
166  if (!json_data->GetString(kOAuthCodeValue, &auth_code)) {
167    VLOG(1) << "CP_AUTH: Creating robot account returned invalid json response";
168    client_->OnInvalidCredentials();
169    return CloudPrintURLFetcher::STOP_PROCESSING;
170  }
171
172  json_data->GetString(kXMPPJidValue, &robot_email_);
173  // Now that we have an auth code we need to get the refresh and access tokens.
174  oauth_client_.reset(new gaia::GaiaOAuthClient(
175      GaiaUrls::GetInstance()->oauth2_token_url(),
176      g_service_process->GetServiceURLRequestContextGetter()));
177  oauth_client_->GetTokensFromAuthCode(oauth_client_info_,
178                                       auth_code,
179                                       kCloudPrintAPIMaxRetryCount,
180                                       this);
181
182  return CloudPrintURLFetcher::STOP_PROCESSING;
183}
184
185CloudPrintURLFetcher::ResponseAction CloudPrintAuth::OnRequestAuthError() {
186  VLOG(1) << "CP_AUTH: Creating robot account authentication error";
187  // Notify client about authentication error.
188  client_->OnInvalidCredentials();
189  return CloudPrintURLFetcher::STOP_PROCESSING;
190}
191
192std::string CloudPrintAuth::GetAuthHeader() {
193  DCHECK(!client_login_token_.empty());
194  std::string header;
195  header = "Authorization: GoogleLogin auth=";
196  header += client_login_token_;
197  return header;
198}
199
200CloudPrintAuth::~CloudPrintAuth() {}
201
202}  // namespace cloud_print
203