blob: bb57c7db1b59a67a36c7b602b77463b2746fe975 [file] [log] [blame]
jrwdd87d07c2015-06-02 02:44:341// Copyright 2014 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
nicholssf620a47c2017-02-02 23:39:085#include "remoting/base/oauth_token_getter_impl.h"
jrwdd87d07c2015-06-02 02:44:346
sergeyu1417e0132015-12-23 19:01:227#include <utility>
8
jrwdd87d07c2015-06-02 02:44:349#include "base/bind.h"
10#include "base/callback.h"
11#include "base/strings/string_util.h"
12#include "google_apis/google_api_keys.h"
13#include "net/url_request/url_request_context_getter.h"
14#include "remoting/base/logging.h"
nicholss1e1a3f892017-02-21 17:55:0515#include "remoting/base/oauth_helper.h"
jrwdd87d07c2015-06-02 02:44:3416
17namespace remoting {
18
19namespace {
20
21// Maximum number of retries on network/500 errors.
22const int kMaxRetries = 3;
23
24// Time when we we try to update OAuth token before its expiration.
25const int kTokenUpdateTimeBeforeExpirySeconds = 60;
26
27} // namespace
28
29OAuthTokenGetterImpl::OAuthTokenGetterImpl(
nicholss1e1a3f892017-02-21 17:55:0530 std::unique_ptr<OAuthIntermediateCredentials> intermediate_credentials,
31 const OAuthTokenGetter::CredentialsUpdatedCallback& on_credentials_update,
jrwdd87d07c2015-06-02 02:44:3432 const scoped_refptr<net::URLRequestContextGetter>&
33 url_request_context_getter,
jrw5b3e1c462015-07-17 02:52:3934 bool auto_refresh)
nicholss1e1a3f892017-02-21 17:55:0535 : intermediate_credentials_(std::move(intermediate_credentials)),
36 gaia_oauth_client_(
37 new gaia::GaiaOAuthClient(url_request_context_getter.get())),
38 credentials_updated_callback_(on_credentials_update),
39 url_request_context_getter_(url_request_context_getter) {
40 if (auto_refresh) {
41 refresh_timer_.reset(new base::OneShotTimer());
42 }
43}
44
45OAuthTokenGetterImpl::OAuthTokenGetterImpl(
46 std::unique_ptr<OAuthAuthorizationCredentials> authorization_credentials,
47 const scoped_refptr<net::URLRequestContextGetter>&
48 url_request_context_getter,
49 bool auto_refresh)
50 : authorization_credentials_(std::move(authorization_credentials)),
jrwdd87d07c2015-06-02 02:44:3451 gaia_oauth_client_(
52 new gaia::GaiaOAuthClient(url_request_context_getter.get())),
jrw5b3e1c462015-07-17 02:52:3953 url_request_context_getter_(url_request_context_getter) {
jrwdd87d07c2015-06-02 02:44:3454 if (auto_refresh) {
danakj8c3eb802015-09-24 07:53:0055 refresh_timer_.reset(new base::OneShotTimer());
jrwdd87d07c2015-06-02 02:44:3456 }
57}
58
gabbf77513a2017-06-01 14:35:3459OAuthTokenGetterImpl::~OAuthTokenGetterImpl() {
60 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
61}
jrwdd87d07c2015-06-02 02:44:3462
nicholss1e1a3f892017-02-21 17:55:0563void OAuthTokenGetterImpl::OnGetTokensResponse(const std::string& refresh_token,
jrwdd87d07c2015-06-02 02:44:3464 const std::string& access_token,
65 int expires_seconds) {
gabbf77513a2017-06-01 14:35:3466 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
nicholss1e1a3f892017-02-21 17:55:0567 DCHECK(intermediate_credentials_);
68 VLOG(1) << "Received OAuth tokens.";
69
70 // Update the access token and any other auto-update timers.
71 UpdateAccessToken(access_token, expires_seconds);
72
73 // Keep the refresh token in the authorization_credentials.
74 authorization_credentials_.reset(
75 new OAuthTokenGetter::OAuthAuthorizationCredentials(
76 std::string(), refresh_token,
77 intermediate_credentials_->is_service_account));
78
79 // Clear out the one time use token.
80 intermediate_credentials_.reset();
81
82 // At this point we don't know the email address so we need to fetch it.
83 email_discovery_ = true;
84 gaia_oauth_client_->GetUserEmail(access_token, kMaxRetries, this);
jrwdd87d07c2015-06-02 02:44:3485}
86
87void OAuthTokenGetterImpl::OnRefreshTokenResponse(
88 const std::string& access_token,
89 int expires_seconds) {
gabbf77513a2017-06-01 14:35:3490 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
nicholss1e1a3f892017-02-21 17:55:0591 DCHECK(authorization_credentials_);
nicholssf620a47c2017-02-02 23:39:0892 VLOG(1) << "Received OAuth token.";
jrwdd87d07c2015-06-02 02:44:3493
nicholss1e1a3f892017-02-21 17:55:0594 // Update the access token and any other auto-update timers.
95 UpdateAccessToken(access_token, expires_seconds);
jrwdd87d07c2015-06-02 02:44:3496
nicholss1e1a3f892017-02-21 17:55:0597 if (!authorization_credentials_->is_service_account && !email_verified_) {
jrwdd87d07c2015-06-02 02:44:3498 gaia_oauth_client_->GetUserEmail(access_token, kMaxRetries, this);
99 } else {
nicholss1e1a3f892017-02-21 17:55:05100 response_pending_ = false;
101 NotifyTokenCallbacks(OAuthTokenGetterImpl::SUCCESS,
102 authorization_credentials_->login,
103 oauth_access_token_);
jrwdd87d07c2015-06-02 02:44:34104 }
105}
106
107void OAuthTokenGetterImpl::OnGetUserEmailResponse(
108 const std::string& user_email) {
gabbf77513a2017-06-01 14:35:34109 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
nicholss1e1a3f892017-02-21 17:55:05110 DCHECK(authorization_credentials_);
nicholssf620a47c2017-02-02 23:39:08111 VLOG(1) << "Received user info.";
jrwdd87d07c2015-06-02 02:44:34112
nicholss1e1a3f892017-02-21 17:55:05113 if (email_discovery_) {
114 authorization_credentials_->login = user_email;
115 email_discovery_ = false;
116 NotifyUpdatedCallbacks(authorization_credentials_->login,
117 authorization_credentials_->refresh_token);
118 } else if (user_email != authorization_credentials_->login) {
jrwdd87d07c2015-06-02 02:44:34119 LOG(ERROR) << "OAuth token and email address do not refer to "
120 "the same account.";
121 OnOAuthError();
122 return;
123 }
124
125 email_verified_ = true;
nicholss1e1a3f892017-02-21 17:55:05126 response_pending_ = false;
jrwdd87d07c2015-06-02 02:44:34127
128 // Now that we've refreshed the token and verified that it's for the correct
129 // user account, try to connect using the new token.
nicholss1e1a3f892017-02-21 17:55:05130 NotifyTokenCallbacks(OAuthTokenGetterImpl::SUCCESS, user_email,
131 oauth_access_token_);
jrwdd87d07c2015-06-02 02:44:34132}
133
nicholss1e1a3f892017-02-21 17:55:05134void OAuthTokenGetterImpl::UpdateAccessToken(const std::string& access_token,
135 int expires_seconds) {
136 oauth_access_token_ = access_token;
137 base::TimeDelta token_expiration =
138 base::TimeDelta::FromSeconds(expires_seconds) -
139 base::TimeDelta::FromSeconds(kTokenUpdateTimeBeforeExpirySeconds);
140 access_token_expiry_time_ = base::Time::Now() + token_expiration;
141
142 if (refresh_timer_) {
143 refresh_timer_->Stop();
144 refresh_timer_->Start(FROM_HERE, token_expiration, this,
145 &OAuthTokenGetterImpl::RefreshAccessToken);
146 }
147}
148
149void OAuthTokenGetterImpl::NotifyTokenCallbacks(
150 Status status,
151 const std::string& user_email,
152 const std::string& access_token) {
gabbf77513a2017-06-01 14:35:34153 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
jrwdd87d07c2015-06-02 02:44:34154 std::queue<TokenCallback> callbacks(pending_callbacks_);
155 pending_callbacks_ = std::queue<TokenCallback>();
156
157 while (!callbacks.empty()) {
158 callbacks.front().Run(status, user_email, access_token);
159 callbacks.pop();
160 }
161}
162
nicholss1e1a3f892017-02-21 17:55:05163void OAuthTokenGetterImpl::NotifyUpdatedCallbacks(
164 const std::string& user_email,
165 const std::string& refresh_token) {
gabbf77513a2017-06-01 14:35:34166 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
nicholss1e1a3f892017-02-21 17:55:05167 if (credentials_updated_callback_) {
168 credentials_updated_callback_.Run(user_email, refresh_token);
169 }
170}
171
jrwdd87d07c2015-06-02 02:44:34172void OAuthTokenGetterImpl::OnOAuthError() {
gabbf77513a2017-06-01 14:35:34173 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
jrwdd87d07c2015-06-02 02:44:34174 LOG(ERROR) << "OAuth: invalid credentials.";
nicholss1e1a3f892017-02-21 17:55:05175 response_pending_ = false;
jrwdd87d07c2015-06-02 02:44:34176
177 // Throw away invalid credentials and force a refresh.
178 oauth_access_token_.clear();
nicholss1e1a3f892017-02-21 17:55:05179 access_token_expiry_time_ = base::Time();
jrwdd87d07c2015-06-02 02:44:34180 email_verified_ = false;
181
nicholss1e1a3f892017-02-21 17:55:05182 NotifyTokenCallbacks(OAuthTokenGetterImpl::AUTH_ERROR, std::string(),
183 std::string());
jrwdd87d07c2015-06-02 02:44:34184}
185
186void OAuthTokenGetterImpl::OnNetworkError(int response_code) {
gabbf77513a2017-06-01 14:35:34187 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
jrwdd87d07c2015-06-02 02:44:34188 LOG(ERROR) << "Network error when trying to update OAuth token: "
189 << response_code;
nicholss1e1a3f892017-02-21 17:55:05190 response_pending_ = false;
191 NotifyTokenCallbacks(OAuthTokenGetterImpl::NETWORK_ERROR, std::string(),
192 std::string());
jrwdd87d07c2015-06-02 02:44:34193}
194
195void OAuthTokenGetterImpl::CallWithToken(const TokenCallback& on_access_token) {
gabbf77513a2017-06-01 14:35:34196 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
nicholss1e1a3f892017-02-21 17:55:05197 if (intermediate_credentials_) {
jrwdd87d07c2015-06-02 02:44:34198 pending_callbacks_.push(on_access_token);
nicholss1e1a3f892017-02-21 17:55:05199 if (!response_pending_) {
200 GetOauthTokensFromAuthCode();
201 }
jrwdd87d07c2015-06-02 02:44:34202 } else {
nicholss1e1a3f892017-02-21 17:55:05203 bool need_new_auth_token =
204 access_token_expiry_time_.is_null() ||
205 base::Time::Now() >= access_token_expiry_time_ ||
206 (!authorization_credentials_->is_service_account && !email_verified_);
207
208 if (need_new_auth_token) {
209 pending_callbacks_.push(on_access_token);
210 if (!response_pending_) {
211 RefreshAccessToken();
212 }
213 } else {
214 on_access_token.Run(SUCCESS, authorization_credentials_->login,
215 oauth_access_token_);
216 }
jrwdd87d07c2015-06-02 02:44:34217 }
218}
219
sergeyu6134f582015-12-12 01:09:06220void OAuthTokenGetterImpl::InvalidateCache() {
gabbf77513a2017-06-01 14:35:34221 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
nicholss1e1a3f892017-02-21 17:55:05222 access_token_expiry_time_ = base::Time();
sergeyu6134f582015-12-12 01:09:06223}
224
nicholss1e1a3f892017-02-21 17:55:05225void OAuthTokenGetterImpl::GetOauthTokensFromAuthCode() {
gabbf77513a2017-06-01 14:35:34226 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
nicholss1e1a3f892017-02-21 17:55:05227 VLOG(1) << "Fetching OAuth token from Auth Code.";
228 DCHECK(!response_pending_);
jrwdd87d07c2015-06-02 02:44:34229
230 // Service accounts use different API keys, as they use the client app flow.
231 google_apis::OAuth2Client oauth2_client =
nicholss1e1a3f892017-02-21 17:55:05232 intermediate_credentials_->is_service_account
233 ? google_apis::CLIENT_REMOTING_HOST
234 : google_apis::CLIENT_REMOTING;
235
236 std::string redirect_uri;
237 if (intermediate_credentials_->oauth_redirect_uri.empty()) {
238 if (intermediate_credentials_->is_service_account) {
239 redirect_uri = "oob";
240 } else {
241 redirect_uri = GetDefaultOauthRedirectUrl();
242 }
243 } else {
244 redirect_uri = intermediate_credentials_->oauth_redirect_uri;
245 }
246
247 gaia::OAuthClientInfo client_info = {
248 google_apis::GetOAuth2ClientID(oauth2_client),
249 google_apis::GetOAuth2ClientSecret(oauth2_client), redirect_uri};
250
251 response_pending_ = true;
252
253 gaia_oauth_client_->GetTokensFromAuthCode(
254 client_info, intermediate_credentials_->authorization_code, kMaxRetries,
255 this);
256}
257
258void OAuthTokenGetterImpl::RefreshAccessToken() {
gabbf77513a2017-06-01 14:35:34259 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
nicholss1e1a3f892017-02-21 17:55:05260 VLOG(1) << "Refreshing OAuth Access token.";
261 DCHECK(!response_pending_);
262
263 // Service accounts use different API keys, as they use the client app flow.
264 google_apis::OAuth2Client oauth2_client =
265 authorization_credentials_->is_service_account
266 ? google_apis::CLIENT_REMOTING_HOST
267 : google_apis::CLIENT_REMOTING;
jrwdd87d07c2015-06-02 02:44:34268
269 gaia::OAuthClientInfo client_info = {
270 google_apis::GetOAuth2ClientID(oauth2_client),
271 google_apis::GetOAuth2ClientSecret(oauth2_client),
272 // Redirect URL is only used when getting tokens from auth code. It
nicholss1e1a3f892017-02-21 17:55:05273 // is not required when getting access tokens from refresh tokens.
jrwdd87d07c2015-06-02 02:44:34274 ""};
275
nicholss1e1a3f892017-02-21 17:55:05276 response_pending_ = true;
jrwdd87d07c2015-06-02 02:44:34277 std::vector<std::string> empty_scope_list; // Use scope from refresh token.
278 gaia_oauth_client_->RefreshToken(client_info,
nicholss1e1a3f892017-02-21 17:55:05279 authorization_credentials_->refresh_token,
jrwdd87d07c2015-06-02 02:44:34280 empty_scope_list, kMaxRetries, this);
281}
282
283} // namespace remoting