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) { |
| 128 const std::string& account_id = GetPrimaryAccountId(); |
132 switch (type) { | 129 switch (type) { |
133 case chrome::NOTIFICATION_TOKEN_AVAILABLE: { | 130 case chrome::NOTIFICATION_TOKEN_AVAILABLE: { |
134 TokenService::TokenAvailableDetails* tok_details = | 131 TokenService::TokenAvailableDetails* tok_details = |
135 content::Details<TokenService::TokenAvailableDetails>(details).ptr(); | 132 content::Details<TokenService::TokenAvailableDetails>(details).ptr(); |
136 if (tok_details->service() == | 133 if (tok_details->service() == |
137 GaiaConstants::kGaiaOAuth2LoginRefreshToken) { | 134 GaiaConstants::kGaiaOAuth2LoginRefreshToken) { |
138 // TODO(fgorski): Canceling all requests will not be correct in a | 135 // TODO(fgorski): Work on removing this code altogether in favor of the |
139 // multi-login environment. We should cancel only the requests related | 136 // upgrade steps invoked by Initialize. |
140 // to the token being replaced (old token for the same account_id). | 137 // TODO(fgorski): Refresh token received that way is not persisted in |
141 // Previous refresh token is not available at this point, but since | 138 // the token DB. |
142 // there are no other refresh tokens, we cancel all active requests. | 139 CancelRequestsForAccount(account_id); |
143 CancelAllRequests(); | 140 ClearCacheForAccount(account_id); |
144 ClearCache(); | 141 refresh_tokens_[account_id] = tok_details->token(); |
145 UpdateAuthError(GoogleServiceAuthError::AuthErrorNone()); | 142 UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone()); |
146 FireRefreshTokenAvailable(GetAccountId(profile_)); | 143 FireRefreshTokenAvailable(account_id); |
147 } | 144 } |
148 break; | 145 break; |
149 } | 146 } |
150 case chrome::NOTIFICATION_TOKENS_CLEARED: { | 147 case chrome::NOTIFICATION_TOKENS_CLEARED: { |
151 CancelAllRequests(); | 148 CancelAllRequests(); |
152 ClearCache(); | 149 ClearCache(); |
153 UpdateAuthError(GoogleServiceAuthError::AuthErrorNone()); | 150 UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone()); |
154 FireRefreshTokensCleared(); | |
155 break; | 151 break; |
156 } | 152 } |
157 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: | 153 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: |
158 // During startup, if the user is signed in and the OAuth2 refresh token | 154 // 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 | 155 // 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: | 156 // 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. | 157 // One to surface the OAuth2 error, and a second one after signing in. |
162 // See crbug.com/276650. | 158 // See crbug.com/276650. |
163 if (!GetAccountId(profile_).empty() && GetRefreshToken().empty()) { | 159 if (!account_id.empty() && GetRefreshToken(account_id).empty()) { |
164 UpdateAuthError(GoogleServiceAuthError( | 160 UpdateAuthError(account_id, GoogleServiceAuthError( |
165 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); | 161 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); |
166 } | 162 } |
167 FireRefreshTokensLoaded(); | 163 FireRefreshTokensLoaded(); |
168 break; | 164 break; |
169 default: | 165 default: |
170 NOTREACHED() << "Invalid notification type=" << type; | 166 NOTREACHED() << "Invalid notification type=" << type; |
171 break; | 167 break; |
172 } | 168 } |
173 } | 169 } |
174 | 170 |
175 GoogleServiceAuthError ProfileOAuth2TokenService::GetAuthStatus() const { | 171 GoogleServiceAuthError ProfileOAuth2TokenService::GetAuthStatus() const { |
176 return last_auth_error_; | 172 return last_auth_error_; |
177 } | 173 } |
178 | 174 |
179 void ProfileOAuth2TokenService::RegisterCacheEntry( | 175 std::string ProfileOAuth2TokenService::GetPrimaryAccountId() { |
180 const std::string& client_id, | 176 SigninManagerBase* signin_manager = |
181 const std::string& refresh_token, | 177 SigninManagerFactory::GetForProfileIfExists(profile_); |
182 const ScopeSet& scopes, | 178 // TODO(fgorski): DCHECK(signin_manager) here - it may require update to test |
183 const std::string& access_token, | 179 // code and the line above (SigninManager might not exist yet). |
184 const base::Time& expiration_date) { | 180 return signin_manager ? signin_manager->GetAuthenticatedUsername() |
185 if (ShouldCacheForRefreshToken(TokenServiceFactory::GetForProfile(profile_), | 181 : std::string(); |
186 refresh_token)) { | |
187 OAuth2TokenService::RegisterCacheEntry(client_id, | |
188 refresh_token, | |
189 scopes, | |
190 access_token, | |
191 expiration_date); | |
192 } | |
193 } | 182 } |
194 | 183 |
195 bool ProfileOAuth2TokenService::ShouldCacheForRefreshToken( | 184 std::vector<std::string> ProfileOAuth2TokenService::GetAccounts() { |
196 TokenService *token_service, | 185 std::vector<std::string> account_ids; |
197 const std::string& refresh_token) { | 186 for (std::map<std::string, std::string>::const_iterator iter = |
198 if (!token_service || | 187 refresh_tokens_.begin(); iter != refresh_tokens_.end(); ++iter) { |
199 !token_service->HasOAuthLoginToken() || | 188 account_ids.push_back(iter->first); |
200 token_service->GetOAuth2LoginRefreshToken().compare(refresh_token) != 0) { | |
201 DLOG(INFO) << | |
202 "Received a token with a refresh token not maintained by TokenService."; | |
203 return false; | |
204 } | 189 } |
205 return true; | 190 return account_ids; |
206 } | 191 } |
207 | 192 |
208 void ProfileOAuth2TokenService::UpdateCredentials( | 193 void ProfileOAuth2TokenService::UpdateCredentials( |
209 const std::string& account_id, | 194 const std::string& account_id, |
210 const std::string& refresh_token) { | 195 const std::string& refresh_token) { |
211 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 196 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
212 DCHECK(!refresh_token.empty()); | 197 DCHECK(!refresh_token.empty()); |
213 | 198 |
214 bool refresh_token_present = refresh_tokens_.count(account_id) > 0; | 199 bool refresh_token_present = refresh_tokens_.count(account_id) > 0; |
215 if (!refresh_token_present || | 200 if (!refresh_token_present || |
216 refresh_tokens_[account_id] != refresh_token) { | 201 refresh_tokens_[account_id] != refresh_token) { |
217 // If token present, and different from the new one, cancel its requests. | 202 // If token present, and different from the new one, cancel its requests, |
218 if (refresh_token_present) | 203 // and clear the entries in cache related to that account. |
219 CancelRequestsForToken(refresh_tokens_[account_id]); | 204 if (refresh_token_present) { |
| 205 CancelRequestsForAccount(account_id); |
| 206 ClearCacheForAccount(account_id); |
| 207 } |
220 | 208 |
221 // Save the token in memory and in persistent store. | 209 // Save the token in memory and in persistent store. |
222 refresh_tokens_[account_id] = refresh_token; | 210 refresh_tokens_[account_id] = refresh_token; |
223 scoped_refptr<TokenWebData> token_web_data = | 211 PersistCredentials(account_id, refresh_token); |
224 TokenWebData::FromBrowserContext(profile_); | |
225 if (token_web_data.get()) | |
226 token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id), | |
227 refresh_token); | |
228 | 212 |
| 213 UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone()); |
229 FireRefreshTokenAvailable(account_id); | 214 FireRefreshTokenAvailable(account_id); |
230 // TODO(fgorski): Notify diagnostic observers. | 215 // TODO(fgorski): Notify diagnostic observers. |
231 } | 216 } |
232 } | 217 } |
233 | 218 |
234 void ProfileOAuth2TokenService::RevokeCredentials( | 219 void ProfileOAuth2TokenService::RevokeCredentials( |
235 const std::string& account_id) { | 220 const std::string& account_id) { |
236 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 221 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
237 | 222 |
238 if (refresh_tokens_.count(account_id) > 0) { | 223 if (refresh_tokens_.count(account_id) > 0) { |
239 CancelRequestsForToken(refresh_tokens_[account_id]); | 224 CancelRequestsForAccount(account_id); |
| 225 ClearCacheForAccount(account_id); |
240 refresh_tokens_.erase(account_id); | 226 refresh_tokens_.erase(account_id); |
241 scoped_refptr<TokenWebData> token_web_data = | 227 ClearPersistedCredentials(account_id); |
242 TokenWebData::FromBrowserContext(profile_); | |
243 if (token_web_data.get()) | |
244 token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id)); | |
245 FireRefreshTokenRevoked(account_id); | 228 FireRefreshTokenRevoked(account_id); |
246 | 229 |
247 // TODO(fgorski): Notify diagnostic observers. | 230 // TODO(fgorski): Notify diagnostic observers. |
248 } | 231 } |
249 } | 232 } |
250 | 233 |
| 234 void ProfileOAuth2TokenService::PersistCredentials( |
| 235 const std::string& account_id, |
| 236 const std::string& refresh_token) { |
| 237 scoped_refptr<TokenWebData> token_web_data = |
| 238 TokenWebData::FromBrowserContext(profile_); |
| 239 if (token_web_data.get()) { |
| 240 token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id), |
| 241 refresh_token); |
| 242 } |
| 243 } |
| 244 |
| 245 void ProfileOAuth2TokenService::ClearPersistedCredentials( |
| 246 const std::string& account_id) { |
| 247 scoped_refptr<TokenWebData> token_web_data = |
| 248 TokenWebData::FromBrowserContext(profile_); |
| 249 if (token_web_data.get()) |
| 250 token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id)); |
| 251 } |
| 252 |
251 void ProfileOAuth2TokenService::RevokeAllCredentials() { | 253 void ProfileOAuth2TokenService::RevokeAllCredentials() { |
252 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 254 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
253 | 255 |
254 CancelAllRequests(); | 256 CancelAllRequests(); |
255 for (std::map<std::string, std::string>::const_iterator iter = | 257 for (std::map<std::string, std::string>::const_iterator iter = |
256 refresh_tokens_.begin(); | 258 refresh_tokens_.begin(); |
257 iter != refresh_tokens_.end(); | 259 iter != refresh_tokens_.end(); |
258 ++iter) { | 260 ++iter) { |
259 FireRefreshTokenRevoked(iter->first); | 261 FireRefreshTokenRevoked(iter->first); |
260 } | 262 } |
261 refresh_tokens_.clear(); | 263 refresh_tokens_.clear(); |
262 | 264 |
263 scoped_refptr<TokenWebData> token_web_data = | 265 scoped_refptr<TokenWebData> token_web_data = |
264 TokenWebData::FromBrowserContext(profile_); | 266 TokenWebData::FromBrowserContext(profile_); |
265 if (token_web_data.get()) | 267 if (token_web_data.get()) |
266 token_web_data->RemoveAllTokens(); | 268 token_web_data->RemoveAllTokens(); |
267 FireRefreshTokensCleared(); | |
268 | 269 |
269 // TODO(fgorski): Notify diagnostic observers. | 270 // TODO(fgorski): Notify diagnostic observers. |
270 } | 271 } |
271 | 272 |
272 void ProfileOAuth2TokenService::LoadCredentials() { | 273 void ProfileOAuth2TokenService::LoadCredentials() { |
273 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 274 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
274 DCHECK_EQ(0, web_data_service_request_); | 275 DCHECK_EQ(0, web_data_service_request_); |
275 | 276 |
276 CancelAllRequests(); | 277 CancelAllRequests(); |
277 refresh_tokens_.clear(); | 278 refresh_tokens_.clear(); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
319 } else { | 320 } else { |
320 DCHECK(!refresh_token.empty()); | 321 DCHECK(!refresh_token.empty()); |
321 std::string account_id = RemoveAccountIdPrefix(prefixed_account_id); | 322 std::string account_id = RemoveAccountIdPrefix(prefixed_account_id); |
322 refresh_tokens_[account_id] = refresh_token; | 323 refresh_tokens_[account_id] = refresh_token; |
323 FireRefreshTokenAvailable(account_id); | 324 FireRefreshTokenAvailable(account_id); |
324 // TODO(fgorski): Notify diagnostic observers. | 325 // TODO(fgorski): Notify diagnostic observers. |
325 } | 326 } |
326 } | 327 } |
327 | 328 |
328 if (!old_login_token.empty() && | 329 if (!old_login_token.empty() && |
329 refresh_tokens_.count(GetAccountId(profile_)) == 0) { | 330 refresh_tokens_.count(GetPrimaryAccountId()) == 0) { |
330 UpdateCredentials(GetAccountId(profile_), old_login_token); | 331 UpdateCredentials(GetPrimaryAccountId(), old_login_token); |
331 } | 332 } |
332 | 333 |
333 FireRefreshTokensLoaded(); | 334 FireRefreshTokensLoaded(); |
334 } | 335 } |
OLD | NEW |