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 const scoped_refptr
<net::URLRequestContextGetter
>&
38 url_request_context_getter
,
41 : oauth_credentials_(oauth_credentials
.Pass()),
43 new gaia::GaiaOAuthClient(url_request_context_getter
.get())),
44 url_request_context_getter_(url_request_context_getter
),
45 verify_email_(verify_email
) {
47 refresh_timer_
.reset(new base::OneShotTimer
<OAuthTokenGetter
>());
51 OAuthTokenGetter::~OAuthTokenGetter() {}
53 void OAuthTokenGetter::OnGetTokensResponse(const std::string
& user_email
,
54 const std::string
& access_token
,
55 int expires_seconds
) {
59 void OAuthTokenGetter::OnRefreshTokenResponse(
60 const std::string
& access_token
,
61 int expires_seconds
) {
62 DCHECK(CalledOnValidThread());
63 DCHECK(oauth_credentials_
.get());
64 HOST_LOG
<< "Received OAuth token.";
66 oauth_access_token_
= access_token
;
67 base::TimeDelta token_expiration
=
68 base::TimeDelta::FromSeconds(expires_seconds
) -
69 base::TimeDelta::FromSeconds(kTokenUpdateTimeBeforeExpirySeconds
);
70 auth_token_expiry_time_
= base::Time::Now() + token_expiration
;
73 refresh_timer_
->Stop();
74 refresh_timer_
->Start(FROM_HERE
, token_expiration
, this,
75 &OAuthTokenGetter::RefreshOAuthToken
);
78 if (verify_email_
&& !email_verified_
) {
79 gaia_oauth_client_
->GetUserEmail(access_token
, kMaxRetries
, this);
81 refreshing_oauth_token_
= false;
82 NotifyCallbacks(OAuthTokenGetter::SUCCESS
, oauth_credentials_
->login
,
87 void OAuthTokenGetter::OnGetUserEmailResponse(const std::string
& user_email
) {
88 DCHECK(CalledOnValidThread());
89 DCHECK(oauth_credentials_
.get());
90 HOST_LOG
<< "Received user info.";
92 if (user_email
!= oauth_credentials_
->login
) {
93 LOG(ERROR
) << "OAuth token and email address do not refer to "
99 email_verified_
= true;
100 refreshing_oauth_token_
= false;
102 // Now that we've refreshed the token and verified that it's for the correct
103 // user account, try to connect using the new token.
104 NotifyCallbacks(OAuthTokenGetter::SUCCESS
, user_email
, oauth_access_token_
);
107 void OAuthTokenGetter::NotifyCallbacks(Status status
,
108 const std::string
& user_email
,
109 const std::string
& access_token
) {
110 std::queue
<TokenCallback
> callbacks(pending_callbacks_
);
111 pending_callbacks_
= std::queue
<TokenCallback
>();
113 while (!callbacks
.empty()) {
114 callbacks
.front().Run(status
, user_email
, access_token
);
119 void OAuthTokenGetter::OnOAuthError() {
120 DCHECK(CalledOnValidThread());
121 LOG(ERROR
) << "OAuth: invalid credentials.";
122 refreshing_oauth_token_
= false;
124 // Throw away invalid credentials and force a refresh.
125 oauth_access_token_
.clear();
126 auth_token_expiry_time_
= base::Time();
127 email_verified_
= false;
129 NotifyCallbacks(OAuthTokenGetter::AUTH_ERROR
, std::string(), std::string());
132 void OAuthTokenGetter::OnNetworkError(int response_code
) {
133 DCHECK(CalledOnValidThread());
134 LOG(ERROR
) << "Network error when trying to update OAuth token: "
136 refreshing_oauth_token_
= false;
138 OAuthTokenGetter::NETWORK_ERROR
, std::string(), std::string());
141 void OAuthTokenGetter::CallWithToken(const TokenCallback
& on_access_token
) {
142 DCHECK(CalledOnValidThread());
143 bool need_new_auth_token
= auth_token_expiry_time_
.is_null() ||
144 base::Time::Now() >= auth_token_expiry_time_
||
145 (verify_email_
&& !email_verified_
);
147 if (need_new_auth_token
) {
148 pending_callbacks_
.push(on_access_token
);
149 if (!refreshing_oauth_token_
)
153 SUCCESS
, oauth_credentials_
->login
, oauth_access_token_
);
157 void OAuthTokenGetter::RefreshOAuthToken() {
158 DCHECK(CalledOnValidThread());
159 HOST_LOG
<< "Refreshing OAuth token.";
160 DCHECK(!refreshing_oauth_token_
);
162 // Service accounts use different API keys, as they use the client app flow.
163 google_apis::OAuth2Client oauth2_client
=
164 oauth_credentials_
->is_service_account
?
165 google_apis::CLIENT_REMOTING_HOST
: google_apis::CLIENT_REMOTING
;
167 gaia::OAuthClientInfo client_info
= {
168 google_apis::GetOAuth2ClientID(oauth2_client
),
169 google_apis::GetOAuth2ClientSecret(oauth2_client
),
170 // Redirect URL is only used when getting tokens from auth code. It
171 // is not required when getting access tokens.
175 refreshing_oauth_token_
= true;
176 std::vector
<std::string
> empty_scope_list
; // Use scope from refresh token.
177 gaia_oauth_client_
->RefreshToken(
178 client_info
, oauth_credentials_
->refresh_token
, empty_scope_list
,
182 } // namespace remoting