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 28 matching lines...) Expand all Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |