Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(321)

Side by Side Diff: chrome/browser/signin/profile_oauth2_token_service.cc

Issue 23382008: Making OAuth2TokenService multi-login aware, updating callers, minor fixes (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixing a unit test, addressing comments from courage@ Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/signin/profile_oauth2_token_service.h" 5 #include "chrome/browser/signin/profile_oauth2_token_service.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h" 8 #include "base/message_loop/message_loop.h"
9 #include "base/stl_util.h" 9 #include "base/stl_util.h"
10 #include "base/time/time.h" 10 #include "base/time/time.h"
(...skipping 28 matching lines...) Expand all
39 } 39 }
40 40
41 std::string ApplyAccountIdPrefix(const std::string& account_id) { 41 std::string ApplyAccountIdPrefix(const std::string& account_id) {
42 return kAccountIdPrefix + account_id; 42 return kAccountIdPrefix + account_id;
43 } 43 }
44 44
45 std::string RemoveAccountIdPrefix(const std::string& prefixed_account_id) { 45 std::string RemoveAccountIdPrefix(const std::string& prefixed_account_id) {
46 return prefixed_account_id.substr(kAccountIdPrefixLength); 46 return prefixed_account_id.substr(kAccountIdPrefixLength);
47 } 47 }
48 48
49 std::string GetAccountId(Profile* profile) {
50 SigninManagerBase* signin_manager =
51 SigninManagerFactory::GetForProfileIfExists(profile);
52 return signin_manager ? signin_manager->GetAuthenticatedUsername() :
53 std::string();
54 }
55
56 } // namespace 49 } // namespace
57 50
58 ProfileOAuth2TokenService::ProfileOAuth2TokenService() 51 ProfileOAuth2TokenService::ProfileOAuth2TokenService()
59 : profile_(NULL), 52 : profile_(NULL),
60 web_data_service_request_(0), 53 web_data_service_request_(0),
61 last_auth_error_(GoogleServiceAuthError::NONE) { 54 last_auth_error_(GoogleServiceAuthError::NONE) {
62 } 55 }
63 56
64 ProfileOAuth2TokenService::~ProfileOAuth2TokenService() { 57 ProfileOAuth2TokenService::~ProfileOAuth2TokenService() {
65 DCHECK(!signin_global_error_.get()) << 58 DCHECK(!signin_global_error_.get()) <<
(...skipping 26 matching lines...) Expand all
92 85
93 void ProfileOAuth2TokenService::Shutdown() { 86 void ProfileOAuth2TokenService::Shutdown() {
94 DCHECK(profile_) << "Shutdown() called without matching call to Initialize()"; 87 DCHECK(profile_) << "Shutdown() called without matching call to Initialize()";
95 CancelAllRequests(); 88 CancelAllRequests();
96 signin_global_error_->RemoveProvider(this); 89 signin_global_error_->RemoveProvider(this);
97 GlobalErrorServiceFactory::GetForProfile(profile_)->RemoveGlobalError( 90 GlobalErrorServiceFactory::GetForProfile(profile_)->RemoveGlobalError(
98 signin_global_error_.get()); 91 signin_global_error_.get());
99 signin_global_error_.reset(); 92 signin_global_error_.reset();
100 } 93 }
101 94
102 std::string ProfileOAuth2TokenService::GetRefreshToken() { 95 std::string ProfileOAuth2TokenService::GetRefreshToken(
103 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); 96 const std::string& account_id) {
104 if (!token_service || !token_service->HasOAuthLoginToken()) { 97 std::map<std::string, std::string>::const_iterator iter =
105 return std::string(); 98 refresh_tokens_.find(account_id);
106 } 99 if (iter != refresh_tokens_.end())
107 return token_service->GetOAuth2LoginRefreshToken(); 100 return iter->second;
101 return std::string();
108 } 102 }
109 103
110 net::URLRequestContextGetter* ProfileOAuth2TokenService::GetRequestContext() { 104 net::URLRequestContextGetter* ProfileOAuth2TokenService::GetRequestContext() {
111 return profile_->GetRequestContext(); 105 return profile_->GetRequestContext();
112 } 106 }
113 107
114 void ProfileOAuth2TokenService::UpdateAuthError( 108 void ProfileOAuth2TokenService::UpdateAuthError(
109 const std::string& account_id,
115 const GoogleServiceAuthError& error) { 110 const GoogleServiceAuthError& error) {
111 // TODO(fgorski): SigninGlobalError needs to be made multi-login aware.
116 // Do not report connection errors as these are not actually auth errors. 112 // Do not report connection errors as these are not actually auth errors.
117 // We also want to avoid masking a "real" auth error just because we 113 // We also want to avoid masking a "real" auth error just because we
118 // subsequently get a transient network error. 114 // subsequently get a transient network error.
119 if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED) 115 if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED)
120 return; 116 return;
121 117
122 if (error.state() != last_auth_error_.state()) { 118 if (error.state() != last_auth_error_.state()) {
123 last_auth_error_ = error; 119 last_auth_error_ = error;
124 signin_global_error_->AuthStatusChanged(); 120 signin_global_error_->AuthStatusChanged();
125 } 121 }
126 } 122 }
127 123
128 void ProfileOAuth2TokenService::Observe( 124 void ProfileOAuth2TokenService::Observe(
129 int type, 125 int type,
130 const content::NotificationSource& source, 126 const content::NotificationSource& source,
131 const content::NotificationDetails& details) { 127 const content::NotificationDetails& details) {
132 switch (type) { 128 switch (type) {
133 case chrome::NOTIFICATION_TOKEN_AVAILABLE: { 129 case chrome::NOTIFICATION_TOKEN_AVAILABLE: {
134 TokenService::TokenAvailableDetails* tok_details = 130 TokenService::TokenAvailableDetails* tok_details =
135 content::Details<TokenService::TokenAvailableDetails>(details).ptr(); 131 content::Details<TokenService::TokenAvailableDetails>(details).ptr();
136 if (tok_details->service() == 132 if (tok_details->service() ==
137 GaiaConstants::kGaiaOAuth2LoginRefreshToken) { 133 GaiaConstants::kGaiaOAuth2LoginRefreshToken) {
138 // TODO(fgorski): Canceling all requests will not be correct in a 134 // TODO(fgorski): Canceling all requests will not be correct in a
139 // multi-login environment. We should cancel only the requests related 135 // multi-login environment. We should cancel only the requests related
140 // to the token being replaced (old token for the same account_id). 136 // to the token being replaced (old token for the same account_id).
141 // Previous refresh token is not available at this point, but since 137 // Previous refresh token is not available at this point, but since
142 // there are no other refresh tokens, we cancel all active requests. 138 // there are no other refresh tokens, we cancel all active requests.
143 CancelAllRequests(); 139 CancelAllRequests();
144 ClearCache(); 140 ClearCache();
145 UpdateAuthError(GoogleServiceAuthError::AuthErrorNone()); 141 std::string account_id = GetPrimaryAccountId();
146 FireRefreshTokenAvailable(GetAccountId(profile_)); 142 UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
143 refresh_tokens_[account_id] = tok_details->token();
Andrew T Wilson (Slow) 2013/09/06 09:23:10 nit: I'd probably update refresh_tokens_[] before
fgorski 2013/09/12 23:46:24 Done.
144 FireRefreshTokenAvailable(account_id);
147 } 145 }
148 break; 146 break;
149 } 147 }
150 case chrome::NOTIFICATION_TOKENS_CLEARED: { 148 case chrome::NOTIFICATION_TOKENS_CLEARED: {
151 CancelAllRequests(); 149 CancelAllRequests();
152 ClearCache(); 150 ClearCache();
153 UpdateAuthError(GoogleServiceAuthError::AuthErrorNone()); 151 UpdateAuthError(GetPrimaryAccountId(),
154 FireRefreshTokensCleared(); 152 GoogleServiceAuthError::AuthErrorNone());
155 break; 153 break;
156 } 154 }
157 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: 155 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED:
158 FireRefreshTokensLoaded(); 156 FireRefreshTokensLoaded();
159 break; 157 break;
160 default: 158 default:
161 NOTREACHED() << "Invalid notification type=" << type; 159 NOTREACHED() << "Invalid notification type=" << type;
162 break; 160 break;
163 } 161 }
164 } 162 }
165 163
166 GoogleServiceAuthError ProfileOAuth2TokenService::GetAuthStatus() const { 164 GoogleServiceAuthError ProfileOAuth2TokenService::GetAuthStatus() const {
167 return last_auth_error_; 165 return last_auth_error_;
168 } 166 }
169 167
170 void ProfileOAuth2TokenService::RegisterCacheEntry( 168 void ProfileOAuth2TokenService::RegisterCacheEntry(
171 const std::string& refresh_token, 169 const std::string& refresh_token,
172 const ScopeSet& scopes, 170 const ScopeSet& scopes,
173 const std::string& access_token, 171 const std::string& access_token,
174 const base::Time& expiration_date) { 172 const base::Time& expiration_date) {
175 if (ShouldCacheForRefreshToken(TokenServiceFactory::GetForProfile(profile_), 173 if (ShouldCacheForRefreshToken(refresh_token)) {
176 refresh_token)) {
177 OAuth2TokenService::RegisterCacheEntry(refresh_token, 174 OAuth2TokenService::RegisterCacheEntry(refresh_token,
178 scopes, 175 scopes,
179 access_token, 176 access_token,
180 expiration_date); 177 expiration_date);
181 } 178 }
182 } 179 }
183 180
184 bool ProfileOAuth2TokenService::ShouldCacheForRefreshToken( 181 bool ProfileOAuth2TokenService::ShouldCacheForRefreshToken(
185 TokenService *token_service,
186 const std::string& refresh_token) { 182 const std::string& refresh_token) {
187 if (!token_service || 183 // Check below ensures that only refresh tokens belonging to one of the logged
188 !token_service->HasOAuthLoginToken() || 184 // in accounts will allow for the access tokens to be cached.
189 token_service->GetOAuth2LoginRefreshToken().compare(refresh_token) != 0) { 185 // TODO(fgorski): Convert to CHECK/DCHECK if it should not be possible.
190 DLOG(INFO) << 186 // Consider a re-auth scenario.
191 "Received a token with a refresh token not maintained by TokenService."; 187 for (std::map<std::string, std::string>::const_iterator iter =
192 return false; 188 refresh_tokens_.begin(); iter != refresh_tokens_.end(); ++iter) {
189 if (iter->second == refresh_token)
190 return true;
193 } 191 }
194 return true; 192
193 DLOG(INFO) <<
194 "Received a token with a refresh token not maintained by TokenService.";
195 return false;
196 }
197
198 std::string ProfileOAuth2TokenService::GetPrimaryAccountId() {
199 SigninManagerBase* signin_manager =
200 SigninManagerFactory::GetForProfileIfExists(profile_);
201 // TODO(fgorski): DCHECK(signin_manager) here - it may require update to test
202 // code and the line above (SigninManager might not exist yet).
203 return signin_manager ? signin_manager->GetAuthenticatedUsername()
204 : std::string();
205 }
206
207 std::vector<std::string> ProfileOAuth2TokenService::GetAccounts() {
208 std::vector<std::string> account_ids;
209 for (std::map<std::string, std::string>::const_iterator iter =
210 refresh_tokens_.begin(); iter != refresh_tokens_.end(); ++iter) {
211 account_ids.push_back(iter->first);
212 }
213 return account_ids;
195 } 214 }
196 215
197 void ProfileOAuth2TokenService::UpdateCredentials( 216 void ProfileOAuth2TokenService::UpdateCredentials(
198 const std::string& account_id, 217 const std::string& account_id,
199 const std::string& refresh_token) { 218 const std::string& refresh_token) {
200 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 219 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
201 DCHECK(!refresh_token.empty()); 220 DCHECK(!refresh_token.empty());
202 221
203 bool refresh_token_present = refresh_tokens_.count(account_id) > 0; 222 bool refresh_token_present = refresh_tokens_.count(account_id) > 0;
204 if (!refresh_token_present || 223 if (!refresh_token_present ||
205 refresh_tokens_[account_id] != refresh_token) { 224 refresh_tokens_[account_id] != refresh_token) {
206 // If token present, and different from the new one, cancel its requests. 225 // If token present, and different from the new one, cancel its requests,
207 if (refresh_token_present) 226 // and clear the entries in cache related to that account.
227 if (refresh_token_present) {
208 CancelRequestsForToken(refresh_tokens_[account_id]); 228 CancelRequestsForToken(refresh_tokens_[account_id]);
229 // TODO(fgorski): Call ClearCacheForAccount(account_id) from here.
230 }
209 231
210 // Save the token in memory and in persistent store. 232 // Save the token in memory and in persistent store.
211 refresh_tokens_[account_id] = refresh_token; 233 refresh_tokens_[account_id] = refresh_token;
212 scoped_refptr<TokenWebData> token_web_data = 234 PersistCredentials(account_id, refresh_token);
213 TokenWebData::FromBrowserContext(profile_);
214 if (token_web_data.get())
215 token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id),
216 refresh_token);
217 235
236 UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
218 FireRefreshTokenAvailable(account_id); 237 FireRefreshTokenAvailable(account_id);
219 // TODO(fgorski): Notify diagnostic observers. 238 // TODO(fgorski): Notify diagnostic observers.
220 } 239 }
221 } 240 }
222 241
223 void ProfileOAuth2TokenService::RevokeCredentials( 242 void ProfileOAuth2TokenService::RevokeCredentials(
224 const std::string& account_id) { 243 const std::string& account_id) {
225 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 244 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
226 245
227 if (refresh_tokens_.count(account_id) > 0) { 246 if (refresh_tokens_.count(account_id) > 0) {
228 CancelRequestsForToken(refresh_tokens_[account_id]); 247 CancelRequestsForToken(refresh_tokens_[account_id]);
248 // TODO(fgorski): Call ClearCacheForAccount(account_id) from here.
229 refresh_tokens_.erase(account_id); 249 refresh_tokens_.erase(account_id);
230 scoped_refptr<TokenWebData> token_web_data = 250 ClearPersistedCredentials(account_id);
231 TokenWebData::FromBrowserContext(profile_);
232 if (token_web_data.get())
233 token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id));
234 FireRefreshTokenRevoked(account_id); 251 FireRefreshTokenRevoked(account_id);
235 252
236 // TODO(fgorski): Notify diagnostic observers. 253 // TODO(fgorski): Notify diagnostic observers.
237 } 254 }
238 } 255 }
239 256
257 void ProfileOAuth2TokenService::PersistCredentials(
258 const std::string& account_id,
259 const std::string& refresh_token) {
260 scoped_refptr<TokenWebData> token_web_data =
261 TokenWebData::FromBrowserContext(profile_);
262 if (token_web_data.get()) {
263 token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id),
264 refresh_token);
265 }
266 }
267
268 void ProfileOAuth2TokenService::ClearPersistedCredentials(
269 const std::string& account_id) {
270 scoped_refptr<TokenWebData> token_web_data =
271 TokenWebData::FromBrowserContext(profile_);
272 if (token_web_data.get())
273 token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id));
274 }
275
240 void ProfileOAuth2TokenService::RevokeAllCredentials() { 276 void ProfileOAuth2TokenService::RevokeAllCredentials() {
241 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 277 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
242 278
243 CancelAllRequests(); 279 CancelAllRequests();
244 for (std::map<std::string, std::string>::const_iterator iter = 280 for (std::map<std::string, std::string>::const_iterator iter =
245 refresh_tokens_.begin(); 281 refresh_tokens_.begin();
246 iter != refresh_tokens_.end(); 282 iter != refresh_tokens_.end();
247 ++iter) { 283 ++iter) {
248 FireRefreshTokenRevoked(iter->first); 284 FireRefreshTokenRevoked(iter->first);
249 } 285 }
250 refresh_tokens_.clear(); 286 refresh_tokens_.clear();
251 287
252 scoped_refptr<TokenWebData> token_web_data = 288 scoped_refptr<TokenWebData> token_web_data =
253 TokenWebData::FromBrowserContext(profile_); 289 TokenWebData::FromBrowserContext(profile_);
254 if (token_web_data.get()) 290 if (token_web_data.get())
255 token_web_data->RemoveAllTokens(); 291 token_web_data->RemoveAllTokens();
256 FireRefreshTokensCleared();
257 292
258 // TODO(fgorski): Notify diagnostic observers. 293 // TODO(fgorski): Notify diagnostic observers.
259 } 294 }
260 295
261 void ProfileOAuth2TokenService::LoadCredentials() { 296 void ProfileOAuth2TokenService::LoadCredentials() {
262 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 297 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
263 DCHECK_EQ(0, web_data_service_request_); 298 DCHECK_EQ(0, web_data_service_request_);
264 299
265 CancelAllRequests(); 300 CancelAllRequests();
266 refresh_tokens_.clear(); 301 refresh_tokens_.clear();
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
308 } else { 343 } else {
309 DCHECK(!refresh_token.empty()); 344 DCHECK(!refresh_token.empty());
310 std::string account_id = RemoveAccountIdPrefix(prefixed_account_id); 345 std::string account_id = RemoveAccountIdPrefix(prefixed_account_id);
311 refresh_tokens_[account_id] = refresh_token; 346 refresh_tokens_[account_id] = refresh_token;
312 FireRefreshTokenAvailable(account_id); 347 FireRefreshTokenAvailable(account_id);
313 // TODO(fgorski): Notify diagnostic observers. 348 // TODO(fgorski): Notify diagnostic observers.
314 } 349 }
315 } 350 }
316 351
317 if (!old_login_token.empty() && 352 if (!old_login_token.empty() &&
318 refresh_tokens_.count(GetAccountId(profile_)) == 0) { 353 refresh_tokens_.count(GetPrimaryAccountId()) == 0) {
319 UpdateCredentials(GetAccountId(profile_), old_login_token); 354 UpdateCredentials(GetPrimaryAccountId(), old_login_token);
320 } 355 }
321 356
322 FireRefreshTokensLoaded(); 357 FireRefreshTokensLoaded();
323 } 358 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698