1 // 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.
5 #include "remoting/host/oauth_token_getter_impl.h"
8 #include "base/callback.h"
9 #include "base/strings/string_util.h"
10 #include "google_apis/google_api_keys.h"
11 #include "net/url_request/url_request_context_getter.h"
12 #include "remoting/base/logging.h"
18 // Maximum number of retries on network/500 errors.
19 const int kMaxRetries
= 3;
21 // Time when we we try to update OAuth token before its expiration.
22 const int kTokenUpdateTimeBeforeExpirySeconds
= 60;
26 OAuthTokenGetterImpl::OAuthTokenGetterImpl(
27 scoped_ptr
<OAuthCredentials
> oauth_credentials
,
28 const scoped_refptr
<net::URLRequestContextGetter
>&
29 url_request_context_getter
,
31 : oauth_credentials_(oauth_credentials
.Pass()),
33 new gaia::GaiaOAuthClient(url_request_context_getter
.get())),
34 url_request_context_getter_(url_request_context_getter
) {
36 refresh_timer_
.reset(new base::OneShotTimer
<OAuthTokenGetterImpl
>());
40 OAuthTokenGetterImpl::~OAuthTokenGetterImpl() {
43 void OAuthTokenGetterImpl::OnGetTokensResponse(const std::string
& user_email
,
44 const std::string
& access_token
,
45 int expires_seconds
) {
49 void OAuthTokenGetterImpl::OnRefreshTokenResponse(
50 const std::string
& access_token
,
51 int expires_seconds
) {
52 DCHECK(CalledOnValidThread());
53 DCHECK(oauth_credentials_
.get());
54 HOST_LOG
<< "Received OAuth token.";
56 oauth_access_token_
= access_token
;
57 base::TimeDelta token_expiration
=
58 base::TimeDelta::FromSeconds(expires_seconds
) -
59 base::TimeDelta::FromSeconds(kTokenUpdateTimeBeforeExpirySeconds
);
60 auth_token_expiry_time_
= base::Time::Now() + token_expiration
;
63 refresh_timer_
->Stop();
64 refresh_timer_
->Start(FROM_HERE
, token_expiration
, this,
65 &OAuthTokenGetterImpl::RefreshOAuthToken
);
68 if (!oauth_credentials_
->is_service_account
&& !email_verified_
) {
69 gaia_oauth_client_
->GetUserEmail(access_token
, kMaxRetries
, this);
71 refreshing_oauth_token_
= false;
72 NotifyCallbacks(OAuthTokenGetterImpl::SUCCESS
, oauth_credentials_
->login
,
77 void OAuthTokenGetterImpl::OnGetUserEmailResponse(
78 const std::string
& user_email
) {
79 DCHECK(CalledOnValidThread());
80 DCHECK(oauth_credentials_
.get());
81 HOST_LOG
<< "Received user info.";
83 if (user_email
!= oauth_credentials_
->login
) {
84 LOG(ERROR
) << "OAuth token and email address do not refer to "
90 email_verified_
= true;
91 refreshing_oauth_token_
= false;
93 // Now that we've refreshed the token and verified that it's for the correct
94 // user account, try to connect using the new token.
95 NotifyCallbacks(OAuthTokenGetterImpl::SUCCESS
, user_email
,
99 void OAuthTokenGetterImpl::NotifyCallbacks(Status status
,
100 const std::string
& user_email
,
101 const std::string
& access_token
) {
102 std::queue
<TokenCallback
> callbacks(pending_callbacks_
);
103 pending_callbacks_
= std::queue
<TokenCallback
>();
105 while (!callbacks
.empty()) {
106 callbacks
.front().Run(status
, user_email
, access_token
);
111 void OAuthTokenGetterImpl::OnOAuthError() {
112 DCHECK(CalledOnValidThread());
113 LOG(ERROR
) << "OAuth: invalid credentials.";
114 refreshing_oauth_token_
= false;
116 // Throw away invalid credentials and force a refresh.
117 oauth_access_token_
.clear();
118 auth_token_expiry_time_
= base::Time();
119 email_verified_
= false;
121 NotifyCallbacks(OAuthTokenGetterImpl::AUTH_ERROR
, std::string(),
125 void OAuthTokenGetterImpl::OnNetworkError(int response_code
) {
126 DCHECK(CalledOnValidThread());
127 LOG(ERROR
) << "Network error when trying to update OAuth token: "
129 refreshing_oauth_token_
= false;
130 NotifyCallbacks(OAuthTokenGetterImpl::NETWORK_ERROR
, std::string(),
134 void OAuthTokenGetterImpl::CallWithToken(const TokenCallback
& on_access_token
) {
135 DCHECK(CalledOnValidThread());
136 bool need_new_auth_token
= auth_token_expiry_time_
.is_null() ||
137 base::Time::Now() >= auth_token_expiry_time_
||
138 (!oauth_credentials_
->is_service_account
&&
141 if (need_new_auth_token
) {
142 pending_callbacks_
.push(on_access_token
);
143 if (!refreshing_oauth_token_
)
146 on_access_token
.Run(SUCCESS
, oauth_credentials_
->login
,
147 oauth_access_token_
);
151 void OAuthTokenGetterImpl::RefreshOAuthToken() {
152 DCHECK(CalledOnValidThread());
153 HOST_LOG
<< "Refreshing OAuth token.";
154 DCHECK(!refreshing_oauth_token_
);
156 // Service accounts use different API keys, as they use the client app flow.
157 google_apis::OAuth2Client oauth2_client
=
158 oauth_credentials_
->is_service_account
? google_apis::CLIENT_REMOTING_HOST
159 : google_apis::CLIENT_REMOTING
;
161 gaia::OAuthClientInfo client_info
= {
162 google_apis::GetOAuth2ClientID(oauth2_client
),
163 google_apis::GetOAuth2ClientSecret(oauth2_client
),
164 // Redirect URL is only used when getting tokens from auth code. It
165 // is not required when getting access tokens.
168 refreshing_oauth_token_
= true;
169 std::vector
<std::string
> empty_scope_list
; // Use scope from refresh token.
170 gaia_oauth_client_
->RefreshToken(client_info
,
171 oauth_credentials_
->refresh_token
,
172 empty_scope_list
, kMaxRetries
, this);
175 } // namespace remoting