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
,
40 : oauth_credentials_(oauth_credentials
.Pass()),
42 new gaia::GaiaOAuthClient(url_request_context_getter
.get())),
43 url_request_context_getter_(url_request_context_getter
),
44 refreshing_oauth_token_(false) {
46 refresh_timer_
.reset(new base::OneShotTimer
<OAuthTokenGetter
>());
50 OAuthTokenGetter::~OAuthTokenGetter() {}
52 void OAuthTokenGetter::OnGetTokensResponse(const std::string
& user_email
,
53 const std::string
& access_token
,
54 int expires_seconds
) {
58 void OAuthTokenGetter::OnRefreshTokenResponse(
59 const std::string
& access_token
,
60 int expires_seconds
) {
61 DCHECK(CalledOnValidThread());
62 DCHECK(oauth_credentials_
.get());
63 HOST_LOG
<< "Received OAuth token.";
65 oauth_access_token_
= access_token
;
66 base::TimeDelta token_expiration
=
67 base::TimeDelta::FromSeconds(expires_seconds
) -
68 base::TimeDelta::FromSeconds(kTokenUpdateTimeBeforeExpirySeconds
);
69 auth_token_expiry_time_
= base::Time::Now() + token_expiration
;
72 refresh_timer_
->Stop();
73 refresh_timer_
->Start(FROM_HERE
, token_expiration
, this,
74 &OAuthTokenGetter::RefreshOAuthToken
);
77 if (verified_email_
.empty()) {
78 gaia_oauth_client_
->GetUserEmail(access_token
, kMaxRetries
, this);
80 refreshing_oauth_token_
= false;
82 OAuthTokenGetter::SUCCESS
, verified_email_
, oauth_access_token_
);
86 void OAuthTokenGetter::OnGetUserEmailResponse(const std::string
& user_email
) {
87 DCHECK(CalledOnValidThread());
88 DCHECK(oauth_credentials_
.get());
89 HOST_LOG
<< "Received user info.";
91 if (user_email
!= oauth_credentials_
->login
) {
92 LOG(ERROR
) << "OAuth token and email address do not refer to "
98 verified_email_
= user_email
;
99 refreshing_oauth_token_
= false;
101 // Now that we've refreshed the token and verified that it's for the correct
102 // user account, try to connect using the new token.
103 NotifyCallbacks(OAuthTokenGetter::SUCCESS
, user_email
, oauth_access_token_
);
106 void OAuthTokenGetter::NotifyCallbacks(Status status
,
107 const std::string
& user_email
,
108 const std::string
& access_token
) {
109 std::queue
<TokenCallback
> callbacks(pending_callbacks_
);
110 pending_callbacks_
= std::queue
<TokenCallback
>();
112 while (!callbacks
.empty()) {
113 callbacks
.front().Run(status
, user_email
, access_token
);
118 void OAuthTokenGetter::OnOAuthError() {
119 DCHECK(CalledOnValidThread());
120 LOG(ERROR
) << "OAuth: invalid credentials.";
121 refreshing_oauth_token_
= false;
123 // Throw away invalid credentials and force a refresh.
124 oauth_access_token_
.clear();
125 auth_token_expiry_time_
= base::Time();
126 verified_email_
.clear();
128 NotifyCallbacks(OAuthTokenGetter::AUTH_ERROR
, std::string(), std::string());
131 void OAuthTokenGetter::OnNetworkError(int response_code
) {
132 DCHECK(CalledOnValidThread());
133 LOG(ERROR
) << "Network error when trying to update OAuth token: "
135 refreshing_oauth_token_
= false;
137 OAuthTokenGetter::NETWORK_ERROR
, std::string(), std::string());
140 void OAuthTokenGetter::CallWithToken(const TokenCallback
& on_access_token
) {
141 DCHECK(CalledOnValidThread());
142 bool need_new_auth_token
= auth_token_expiry_time_
.is_null() ||
143 base::Time::Now() >= auth_token_expiry_time_
||
144 verified_email_
.empty();
146 if (need_new_auth_token
) {
147 pending_callbacks_
.push(on_access_token
);
148 if (!refreshing_oauth_token_
)
152 SUCCESS
, oauth_credentials_
->login
, oauth_access_token_
);
156 void OAuthTokenGetter::RefreshOAuthToken() {
157 DCHECK(CalledOnValidThread());
158 HOST_LOG
<< "Refreshing OAuth token.";
159 DCHECK(!refreshing_oauth_token_
);
161 // Service accounts use different API keys, as they use the client app flow.
162 google_apis::OAuth2Client oauth2_client
=
163 oauth_credentials_
->is_service_account
?
164 google_apis::CLIENT_REMOTING_HOST
: google_apis::CLIENT_REMOTING
;
166 gaia::OAuthClientInfo client_info
= {
167 google_apis::GetOAuth2ClientID(oauth2_client
),
168 google_apis::GetOAuth2ClientSecret(oauth2_client
),
169 // Redirect URL is only used when getting tokens from auth code. It
170 // is not required when getting access tokens.
174 refreshing_oauth_token_
= true;
175 std::vector
<std::string
> empty_scope_list
; // Use scope from refresh token.
176 gaia_oauth_client_
->RefreshToken(
177 client_info
, oauth_credentials_
->refresh_token
, empty_scope_list
,
181 } // namespace remoting