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.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 OAuthTokenGetter::OAuthCredentials::OAuthCredentials(
27 const std::string
& login
,
28 const std::string
& refresh_token
,
29 bool is_service_account
)
31 refresh_token(refresh_token
),
32 is_service_account(is_service_account
) {
35 OAuthTokenGetter::OAuthTokenGetter(
36 scoped_ptr
<OAuthCredentials
> oauth_credentials
,
37 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter
,
39 : oauth_credentials_(oauth_credentials
.Pass()),
41 new gaia::GaiaOAuthClient(url_request_context_getter
.get())),
42 url_request_context_getter_(url_request_context_getter
),
43 refreshing_oauth_token_(false) {
45 refresh_timer_
.reset(new base::OneShotTimer
<OAuthTokenGetter
>());
49 OAuthTokenGetter::~OAuthTokenGetter() {}
51 void OAuthTokenGetter::OnGetTokensResponse(const std::string
& user_email
,
52 const std::string
& access_token
,
53 int expires_seconds
) {
57 void OAuthTokenGetter::OnRefreshTokenResponse(
58 const std::string
& access_token
,
59 int expires_seconds
) {
60 DCHECK(CalledOnValidThread());
61 DCHECK(oauth_credentials_
.get());
62 HOST_LOG
<< "Received OAuth token.";
64 oauth_access_token_
= access_token
;
65 base::TimeDelta token_expiration
=
66 base::TimeDelta::FromSeconds(expires_seconds
) -
67 base::TimeDelta::FromSeconds(kTokenUpdateTimeBeforeExpirySeconds
);
68 auth_token_expiry_time_
= base::Time::Now() + token_expiration
;
71 refresh_timer_
->Stop();
72 refresh_timer_
->Start(FROM_HERE
, token_expiration
, this,
73 &OAuthTokenGetter::RefreshOAuthToken
);
76 if (verified_email_
.empty()) {
77 gaia_oauth_client_
->GetUserEmail(access_token
, kMaxRetries
, this);
79 refreshing_oauth_token_
= false;
81 OAuthTokenGetter::SUCCESS
, verified_email_
, oauth_access_token_
);
85 void OAuthTokenGetter::OnGetUserEmailResponse(const std::string
& user_email
) {
86 DCHECK(CalledOnValidThread());
87 DCHECK(oauth_credentials_
.get());
88 HOST_LOG
<< "Received user info.";
90 if (user_email
!= oauth_credentials_
->login
) {
91 LOG(ERROR
) << "OAuth token and email address do not refer to "
97 verified_email_
= user_email
;
98 refreshing_oauth_token_
= false;
100 // Now that we've refreshed the token and verified that it's for the correct
101 // user account, try to connect using the new token.
102 NotifyCallbacks(OAuthTokenGetter::SUCCESS
, user_email
, oauth_access_token_
);
105 void OAuthTokenGetter::NotifyCallbacks(Status status
,
106 const std::string
& user_email
,
107 const std::string
& access_token
) {
108 std::queue
<TokenCallback
> callbacks(pending_callbacks_
);
109 pending_callbacks_
= std::queue
<TokenCallback
>();
111 while (!callbacks
.empty()) {
112 callbacks
.front().Run(status
, user_email
, access_token
);
117 void OAuthTokenGetter::OnOAuthError() {
118 DCHECK(CalledOnValidThread());
119 LOG(ERROR
) << "OAuth: invalid credentials.";
120 refreshing_oauth_token_
= false;
122 // Throw away invalid credentials and force a refresh.
123 oauth_access_token_
.clear();
124 auth_token_expiry_time_
= base::Time();
125 verified_email_
.clear();
127 NotifyCallbacks(OAuthTokenGetter::AUTH_ERROR
, std::string(), std::string());
130 void OAuthTokenGetter::OnNetworkError(int response_code
) {
131 DCHECK(CalledOnValidThread());
132 LOG(ERROR
) << "Network error when trying to update OAuth token: "
134 refreshing_oauth_token_
= false;
136 OAuthTokenGetter::NETWORK_ERROR
, std::string(), std::string());
139 void OAuthTokenGetter::CallWithToken(const TokenCallback
& on_access_token
) {
140 DCHECK(CalledOnValidThread());
141 bool need_new_auth_token
= auth_token_expiry_time_
.is_null() ||
142 base::Time::Now() >= auth_token_expiry_time_
||
143 verified_email_
.empty();
145 if (need_new_auth_token
) {
146 pending_callbacks_
.push(on_access_token
);
147 if (!refreshing_oauth_token_
)
151 SUCCESS
, oauth_credentials_
->login
, oauth_access_token_
);
155 void OAuthTokenGetter::RefreshOAuthToken() {
156 DCHECK(CalledOnValidThread());
157 HOST_LOG
<< "Refreshing OAuth token.";
158 DCHECK(!refreshing_oauth_token_
);
160 // Service accounts use different API keys, as they use the client app flow.
161 google_apis::OAuth2Client oauth2_client
=
162 oauth_credentials_
->is_service_account
?
163 google_apis::CLIENT_REMOTING_HOST
: google_apis::CLIENT_REMOTING
;
165 gaia::OAuthClientInfo client_info
= {
166 google_apis::GetOAuth2ClientID(oauth2_client
),
167 google_apis::GetOAuth2ClientSecret(oauth2_client
),
168 // Redirect URL is only used when getting tokens from auth code. It
169 // is not required when getting access tokens.
173 refreshing_oauth_token_
= true;
174 std::vector
<std::string
> empty_scope_list
; // Use scope from refresh token.
175 gaia_oauth_client_
->RefreshToken(
176 client_info
, oauth_credentials_
->refresh_token
, empty_scope_list
,
180 } // namespace remoting