Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 92 | 92 |
| 93 void ProfileOAuth2TokenService::Shutdown() { | 93 void ProfileOAuth2TokenService::Shutdown() { |
| 94 DCHECK(profile_) << "Shutdown() called without matching call to Initialize()"; | 94 DCHECK(profile_) << "Shutdown() called without matching call to Initialize()"; |
| 95 CancelAllRequests(); | 95 CancelAllRequests(); |
| 96 signin_global_error_->RemoveProvider(this); | 96 signin_global_error_->RemoveProvider(this); |
| 97 GlobalErrorServiceFactory::GetForProfile(profile_)->RemoveGlobalError( | 97 GlobalErrorServiceFactory::GetForProfile(profile_)->RemoveGlobalError( |
| 98 signin_global_error_.get()); | 98 signin_global_error_.get()); |
| 99 signin_global_error_.reset(); | 99 signin_global_error_.reset(); |
| 100 } | 100 } |
| 101 | 101 |
| 102 std::string ProfileOAuth2TokenService::GetRefreshToken() { | 102 std::string ProfileOAuth2TokenService::GetRefreshToken( |
| 103 TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); | 103 const std::string& account_id) { |
| 104 if (!token_service || !token_service->HasOAuthLoginToken()) { | 104 return refresh_tokens_[account_id]; |
| 105 return std::string(); | |
| 106 } | |
| 107 return token_service->GetOAuth2LoginRefreshToken(); | |
| 108 } | 105 } |
| 109 | 106 |
| 110 net::URLRequestContextGetter* ProfileOAuth2TokenService::GetRequestContext() { | 107 net::URLRequestContextGetter* ProfileOAuth2TokenService::GetRequestContext() { |
| 111 return profile_->GetRequestContext(); | 108 return profile_->GetRequestContext(); |
| 112 } | 109 } |
| 113 | 110 |
| 114 void ProfileOAuth2TokenService::UpdateAuthError( | 111 void ProfileOAuth2TokenService::UpdateAuthError( |
| 112 const std::string& account_id, | |
| 115 const GoogleServiceAuthError& error) { | 113 const GoogleServiceAuthError& error) { |
| 114 // TODO(fgorski): SigninGlobalError needs to be made multi-login aware. | |
| 116 // Do not report connection errors as these are not actually auth errors. | 115 // 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 | 116 // We also want to avoid masking a "real" auth error just because we |
| 118 // subsequently get a transient network error. | 117 // subsequently get a transient network error. |
| 119 if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED) | 118 if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED) |
| 120 return; | 119 return; |
| 121 | 120 |
| 122 if (error.state() != last_auth_error_.state()) { | 121 if (error.state() != last_auth_error_.state()) { |
| 123 last_auth_error_ = error; | 122 last_auth_error_ = error; |
| 124 signin_global_error_->AuthStatusChanged(); | 123 signin_global_error_->AuthStatusChanged(); |
| 125 } | 124 } |
| 126 } | 125 } |
| 127 | 126 |
| 128 void ProfileOAuth2TokenService::Observe( | 127 void ProfileOAuth2TokenService::Observe( |
| 129 int type, | 128 int type, |
| 130 const content::NotificationSource& source, | 129 const content::NotificationSource& source, |
| 131 const content::NotificationDetails& details) { | 130 const content::NotificationDetails& details) { |
| 132 switch (type) { | 131 switch (type) { |
| 133 case chrome::NOTIFICATION_TOKEN_AVAILABLE: { | 132 case chrome::NOTIFICATION_TOKEN_AVAILABLE: { |
| 134 TokenService::TokenAvailableDetails* tok_details = | 133 TokenService::TokenAvailableDetails* tok_details = |
| 135 content::Details<TokenService::TokenAvailableDetails>(details).ptr(); | 134 content::Details<TokenService::TokenAvailableDetails>(details).ptr(); |
| 136 if (tok_details->service() == | 135 if (tok_details->service() == |
| 137 GaiaConstants::kGaiaOAuth2LoginRefreshToken) { | 136 GaiaConstants::kGaiaOAuth2LoginRefreshToken) { |
| 138 // TODO(fgorski): Canceling all requests will not be correct in a | 137 // TODO(fgorski): Canceling all requests will not be correct in a |
| 139 // multi-login environment. We should cancel only the requests related | 138 // multi-login environment. We should cancel only the requests related |
| 140 // to the token being replaced (old token for the same account_id). | 139 // to the token being replaced (old token for the same account_id). |
| 141 // Previous refresh token is not available at this point, but since | 140 // Previous refresh token is not available at this point, but since |
| 142 // there are no other refresh tokens, we cancel all active requests. | 141 // there are no other refresh tokens, we cancel all active requests. |
| 143 CancelAllRequests(); | 142 CancelAllRequests(); |
| 144 ClearCache(); | 143 ClearCache(); |
| 145 UpdateAuthError(GoogleServiceAuthError::AuthErrorNone()); | 144 UpdateAuthError(GetPrimaryAccountId(), |
| 145 GoogleServiceAuthError::AuthErrorNone()); | |
| 146 FireRefreshTokenAvailable(GetAccountId(profile_)); | 146 FireRefreshTokenAvailable(GetAccountId(profile_)); |
| 147 } | 147 } |
| 148 break; | 148 break; |
| 149 } | 149 } |
| 150 case chrome::NOTIFICATION_TOKENS_CLEARED: { | 150 case chrome::NOTIFICATION_TOKENS_CLEARED: { |
| 151 CancelAllRequests(); | 151 CancelAllRequests(); |
| 152 ClearCache(); | 152 ClearCache(); |
| 153 UpdateAuthError(GoogleServiceAuthError::AuthErrorNone()); | 153 UpdateAuthError(GetPrimaryAccountId(), |
| 154 FireRefreshTokensCleared(); | 154 GoogleServiceAuthError::AuthErrorNone()); |
| 155 break; | 155 break; |
| 156 } | 156 } |
| 157 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: | 157 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: |
| 158 // During startup, if the user is signed in and the OAuth2 refresh token | 158 // During startup, if the user is signed in and the OAuth2 refresh token |
| 159 // is empty, flag it as an error by badging the menu. Otherwise, if the | 159 // is empty, flag it as an error by badging the menu. Otherwise, if the |
| 160 // user goes on to set up sync, they will have to make two attempts: | 160 // user goes on to set up sync, they will have to make two attempts: |
| 161 // One to surface the OAuth2 error, and a second one after signing in. | 161 // One to surface the OAuth2 error, and a second one after signing in. |
| 162 // See crbug.com/276650. | 162 // See crbug.com/276650. |
| 163 if (!GetAccountId(profile_).empty() && GetRefreshToken().empty()) { | 163 if (!GetPrimaryAccountId().empty() && |
| 164 UpdateAuthError(GoogleServiceAuthError( | 164 !RefreshTokenIsAvailable(GetPrimaryAccountId())) { |
| 165 UpdateAuthError(GetPrimaryAccountId(), GoogleServiceAuthError( | |
| 165 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); | 166 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); |
| 166 } | 167 } |
| 167 FireRefreshTokensLoaded(); | 168 FireRefreshTokensLoaded(); |
| 168 break; | 169 break; |
| 169 default: | 170 default: |
| 170 NOTREACHED() << "Invalid notification type=" << type; | 171 NOTREACHED() << "Invalid notification type=" << type; |
| 171 break; | 172 break; |
| 172 } | 173 } |
| 173 } | 174 } |
| 174 | 175 |
| 175 GoogleServiceAuthError ProfileOAuth2TokenService::GetAuthStatus() const { | 176 GoogleServiceAuthError ProfileOAuth2TokenService::GetAuthStatus() const { |
| 176 return last_auth_error_; | 177 return last_auth_error_; |
| 177 } | 178 } |
| 178 | 179 |
| 179 void ProfileOAuth2TokenService::RegisterCacheEntry( | 180 void ProfileOAuth2TokenService::RegisterCacheEntry( |
| 180 const std::string& refresh_token, | 181 const std::string& refresh_token, |
| 181 const ScopeSet& scopes, | 182 const ScopeSet& scopes, |
| 182 const std::string& access_token, | 183 const std::string& access_token, |
| 183 const base::Time& expiration_date) { | 184 const base::Time& expiration_date) { |
| 184 if (ShouldCacheForRefreshToken(TokenServiceFactory::GetForProfile(profile_), | 185 if (ShouldCacheForRefreshToken(refresh_token)) { |
| 185 refresh_token)) { | |
| 186 OAuth2TokenService::RegisterCacheEntry(refresh_token, | 186 OAuth2TokenService::RegisterCacheEntry(refresh_token, |
| 187 scopes, | 187 scopes, |
| 188 access_token, | 188 access_token, |
| 189 expiration_date); | 189 expiration_date); |
| 190 } | 190 } |
| 191 } | 191 } |
| 192 | 192 |
| 193 bool ProfileOAuth2TokenService::ShouldCacheForRefreshToken( | 193 bool ProfileOAuth2TokenService::ShouldCacheForRefreshToken( |
| 194 TokenService *token_service, | |
| 195 const std::string& refresh_token) { | 194 const std::string& refresh_token) { |
| 196 if (!token_service || | 195 // Check below ensures that only refresh tokens belonging to one of the logged |
| 197 !token_service->HasOAuthLoginToken() || | 196 // in accounts will allow for the access tokens to be cached. |
| 198 token_service->GetOAuth2LoginRefreshToken().compare(refresh_token) != 0) { | 197 // TODO(fgorski): Convert to CHECK/DCHECK if it should not be possible. |
| 199 DLOG(INFO) << | 198 // Consider a re-auth scenario. |
| 200 "Received a token with a refresh token not maintained by TokenService."; | 199 for (std::map<std::string, std::string>::const_iterator iter = |
| 201 return false; | 200 refresh_tokens_.begin(); iter != refresh_tokens_.end(); ++iter) { |
| 201 if (iter->second == refresh_token) | |
| 202 return true; | |
| 202 } | 203 } |
| 203 return true; | 204 |
| 205 DLOG(INFO) << | |
| 206 "Received a token with a refresh token not maintained by TokenService."; | |
| 207 return false; | |
| 208 } | |
| 209 | |
| 210 std::string ProfileOAuth2TokenService::GetPrimaryAccountId() { | |
| 211 SigninManagerBase* signin_manager = | |
| 212 SigninManagerFactory::GetForProfileIfExists(profile_); | |
| 213 return signin_manager ? signin_manager->GetAuthenticatedUsername() : | |
|
Roger Tawa OOO till Jul 10th
2013/08/29 15:41:40
Should probably DCHECK that SM is not null, at lea
fgorski
2013/08/29 23:04:14
Done.
| |
| 214 std::string(); | |
| 215 } | |
| 216 | |
| 217 std::vector<std::string> ProfileOAuth2TokenService::GetAccounts() { | |
| 218 std::vector<std::string> account_ids; | |
| 219 for (std::map<std::string, std::string>::const_iterator iter = | |
| 220 refresh_tokens_.begin(); iter != refresh_tokens_.end(); ++iter) { | |
| 221 account_ids.push_back(iter->first); | |
| 222 } | |
| 223 return account_ids; | |
| 204 } | 224 } |
| 205 | 225 |
| 206 void ProfileOAuth2TokenService::UpdateCredentials( | 226 void ProfileOAuth2TokenService::UpdateCredentials( |
| 207 const std::string& account_id, | 227 const std::string& account_id, |
| 208 const std::string& refresh_token) { | 228 const std::string& refresh_token) { |
| 209 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 229 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 210 DCHECK(!refresh_token.empty()); | 230 DCHECK(!refresh_token.empty()); |
| 211 | 231 |
| 212 bool refresh_token_present = refresh_tokens_.count(account_id) > 0; | 232 bool refresh_token_present = refresh_tokens_.count(account_id) > 0; |
| 213 if (!refresh_token_present || | 233 if (!refresh_token_present || |
| 214 refresh_tokens_[account_id] != refresh_token) { | 234 refresh_tokens_[account_id] != refresh_token) { |
| 215 // If token present, and different from the new one, cancel its requests. | 235 // If token present, and different from the new one, cancel its requests, |
| 216 if (refresh_token_present) | 236 // and clear the entries in cache related to that account. |
| 237 if (refresh_token_present) { | |
| 217 CancelRequestsForToken(refresh_tokens_[account_id]); | 238 CancelRequestsForToken(refresh_tokens_[account_id]); |
| 239 // ClearCacheForAccount(account_id); | |
| 240 } | |
| 218 | 241 |
| 219 // Save the token in memory and in persistent store. | 242 // Save the token in memory and in persistent store. |
| 220 refresh_tokens_[account_id] = refresh_token; | 243 refresh_tokens_[account_id] = refresh_token; |
| 221 scoped_refptr<TokenWebData> token_web_data = | 244 scoped_refptr<TokenWebData> token_web_data = |
| 222 TokenWebData::FromBrowserContext(profile_); | 245 TokenWebData::FromBrowserContext(profile_); |
| 223 if (token_web_data.get()) | 246 if (token_web_data.get()) |
| 224 token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id), | 247 token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id), |
| 225 refresh_token); | 248 refresh_token); |
| 226 | 249 |
| 250 UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone()); | |
| 227 FireRefreshTokenAvailable(account_id); | 251 FireRefreshTokenAvailable(account_id); |
| 228 // TODO(fgorski): Notify diagnostic observers. | 252 // TODO(fgorski): Notify diagnostic observers. |
| 229 } | 253 } |
| 230 } | 254 } |
| 231 | 255 |
| 232 void ProfileOAuth2TokenService::RevokeCredentials( | 256 void ProfileOAuth2TokenService::RevokeCredentials( |
| 233 const std::string& account_id) { | 257 const std::string& account_id) { |
| 234 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 258 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 235 | 259 |
| 236 if (refresh_tokens_.count(account_id) > 0) { | 260 if (refresh_tokens_.count(account_id) > 0) { |
| 237 CancelRequestsForToken(refresh_tokens_[account_id]); | 261 CancelRequestsForToken(refresh_tokens_[account_id]); |
| 262 // TODO(fgorski): Call ClearCacheForAccount(account_id) from here. | |
| 238 refresh_tokens_.erase(account_id); | 263 refresh_tokens_.erase(account_id); |
| 239 scoped_refptr<TokenWebData> token_web_data = | 264 scoped_refptr<TokenWebData> token_web_data = |
| 240 TokenWebData::FromBrowserContext(profile_); | 265 TokenWebData::FromBrowserContext(profile_); |
| 241 if (token_web_data.get()) | 266 if (token_web_data.get()) |
| 242 token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id)); | 267 token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id)); |
| 243 FireRefreshTokenRevoked(account_id); | 268 FireRefreshTokenRevoked(account_id); |
| 244 | 269 |
| 245 // TODO(fgorski): Notify diagnostic observers. | 270 // TODO(fgorski): Notify diagnostic observers. |
| 246 } | 271 } |
| 247 } | 272 } |
| 248 | 273 |
| 249 void ProfileOAuth2TokenService::RevokeAllCredentials() { | 274 void ProfileOAuth2TokenService::RevokeAllCredentials() { |
| 250 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 275 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 251 | 276 |
| 252 CancelAllRequests(); | 277 CancelAllRequests(); |
| 253 for (std::map<std::string, std::string>::const_iterator iter = | 278 for (std::map<std::string, std::string>::const_iterator iter = |
| 254 refresh_tokens_.begin(); | 279 refresh_tokens_.begin(); |
| 255 iter != refresh_tokens_.end(); | 280 iter != refresh_tokens_.end(); |
| 256 ++iter) { | 281 ++iter) { |
| 257 FireRefreshTokenRevoked(iter->first); | 282 FireRefreshTokenRevoked(iter->first); |
| 258 } | 283 } |
| 259 refresh_tokens_.clear(); | 284 refresh_tokens_.clear(); |
| 260 | 285 |
| 261 scoped_refptr<TokenWebData> token_web_data = | 286 scoped_refptr<TokenWebData> token_web_data = |
| 262 TokenWebData::FromBrowserContext(profile_); | 287 TokenWebData::FromBrowserContext(profile_); |
| 263 if (token_web_data.get()) | 288 if (token_web_data.get()) |
| 264 token_web_data->RemoveAllTokens(); | 289 token_web_data->RemoveAllTokens(); |
| 265 FireRefreshTokensCleared(); | |
| 266 | 290 |
| 267 // TODO(fgorski): Notify diagnostic observers. | 291 // TODO(fgorski): Notify diagnostic observers. |
| 268 } | 292 } |
| 269 | 293 |
| 270 void ProfileOAuth2TokenService::LoadCredentials() { | 294 void ProfileOAuth2TokenService::LoadCredentials() { |
| 271 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 295 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 272 DCHECK_EQ(0, web_data_service_request_); | 296 DCHECK_EQ(0, web_data_service_request_); |
| 273 | 297 |
| 274 CancelAllRequests(); | 298 CancelAllRequests(); |
| 275 refresh_tokens_.clear(); | 299 refresh_tokens_.clear(); |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 323 } | 347 } |
| 324 } | 348 } |
| 325 | 349 |
| 326 if (!old_login_token.empty() && | 350 if (!old_login_token.empty() && |
| 327 refresh_tokens_.count(GetAccountId(profile_)) == 0) { | 351 refresh_tokens_.count(GetAccountId(profile_)) == 0) { |
| 328 UpdateCredentials(GetAccountId(profile_), old_login_token); | 352 UpdateCredentials(GetAccountId(profile_), old_login_token); |
| 329 } | 353 } |
| 330 | 354 |
| 331 FireRefreshTokensLoaded(); | 355 FireRefreshTokensLoaded(); |
| 332 } | 356 } |
| OLD | NEW |