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

Side by Side Diff: chrome/browser/chromeos/login/oauth_login_manager.cc

Issue 11649055: OAuth2 sign-in flow for ChromeOS (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 11 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013 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.
4
5 #include "chrome/browser/chromeos/login/oauth_login_manager.h"
6
7 #include "base/command_line.h"
8 #include "base/metrics/histogram.h"
9 #include "base/string_util.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/chromeos/login/user_manager.h"
12 #include "chrome/browser/policy/browser_policy_connector.h"
13 #include "chrome/browser/prefs/pref_service.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/signin/token_service.h"
16 #include "chrome/browser/signin/token_service_factory.h"
17 #include "chrome/common/chrome_notification_types.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/common/pref_names.h"
20 #include "content/public/browser/notification_service.h"
21 #include "google_apis/gaia/gaia_auth_fetcher.h"
22 #include "google_apis/gaia/gaia_constants.h"
23 #include "net/url_request/url_request_context_getter.h"
24
25 using content::BrowserThread;
26
27 namespace chromeos {
28
29 // OAuthLoginManager.
30
31 // static.
32 OAuthLoginManager* OAuthLoginManager::Create(
33 OAuthLoginManager::Delegate* delegate) {
34 if (CommandLine::ForCurrentProcess()->HasSwitch(::switches::kForceOAuth2))
35 return new OAuth2LoginManager(delegate);
36
37 return new OAuth1LoginManager(delegate);
38 }
39
40 void OAuthLoginManager::CompleteAuthentication() {
41 delegate_->OnCompletedAuthentication(user_profile_);
42 TokenService* token_service =
43 TokenServiceFactory::GetForProfile(user_profile_);
44 if (token_service->AreCredentialsValid())
45 token_service->StartFetchingTokens();
46 }
47
48 OAuthLoginManager::OAuthLoginManager(Delegate* delegate)
49 : delegate_(delegate),
50 user_profile_(NULL),
51 restore_from_auth_cookies_(false),
52 state_(SESSION_RESTORE_NOT_STARTED) {
53 }
54
55 // OAuth2LoginManager.
56
57 OAuth2LoginManager::OAuth2LoginManager(OAuthLoginManager::Delegate* delegate)
58 : OAuthLoginManager(delegate),
59 loading_reported_(false) {
60 }
61
62 void OAuth2LoginManager::RestoreSession(
63 Profile* user_profile,
64 net::URLRequestContextGetter* auth_request_context,
65 bool restore_from_auth_cookies) {
66 // TODO(zelidrag): Remove eventualy the next line in some future milestone.
67 RemoveLegacyTokens();
68
69 user_profile_ = user_profile;
70 auth_request_context_ = auth_request_context;
71 state_ = OAuthLoginManager::SESSION_RESTORE_IN_PROGRESS;
72
73 // Reuse the access token fetched by the OAuth2PolicyFetcher, if it was
74 // used to fetch policies before Profile creation.
75 if (oauth2_policy_fetcher_.get() &&
76 oauth2_policy_fetcher_->has_oauth2_tokens()) {
77 VLOG(1) << "Resuming profile creation after fetching policy token";
78 refresh_token_ = oauth2_policy_fetcher_->oauth2_tokens().refresh_token;
79 restore_from_auth_cookies = false;
80 }
81 restore_from_auth_cookies_ = restore_from_auth_cookies;
82 ContinueSessionRestore();
83 }
84
85 void OAuth2LoginManager::ContinueSessionRestore() {
86 if (restore_from_auth_cookies_) {
87 FetchOAuth2Tokens();
88 return;
89 }
90
91 // Did we already fetch the refresh token (either policy or db)?
92 if (!refresh_token_.empty()) {
93 // TODO(zelidrag): Figure out where to stick that refresh_token_ into.
94 // We probalby need bit more than that.
95 }
96 LoadAndVerifyOAuth2Tokens();
97 }
98
99
100 void OAuth2LoginManager::RestorePolicyTokens(
101 net::URLRequestContextGetter* auth_request_context) {
102 oauth2_policy_fetcher_.reset(
103 new OAuth2PolicyFetcher(auth_request_context,
104 g_browser_process->system_request_context()));
105 oauth2_policy_fetcher_->Start();
106 }
107
108 void OAuth2LoginManager::Stop() {
109 oauth2_token_fetcher_.reset();
110 login_verifier_.reset();
111 }
112
113 TokenService* OAuth2LoginManager::SetupTokenService() {
114 DCHECK(registrar_.IsEmpty());
115 TokenService* token_service =
116 TokenServiceFactory::GetForProfile(user_profile_);
117 registrar_.Add(this,
118 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED,
119 content::Source<TokenService>(token_service));
120 registrar_.Add(this,
121 chrome::NOTIFICATION_TOKEN_AVAILABLE,
122 content::Source<TokenService>(token_service));
123 registrar_.Add(this,
124 chrome::NOTIFICATION_TOKEN_REQUEST_FAILED,
125 content::Source<TokenService>(token_service));
126 return token_service;
127 }
128
129 void OAuth2LoginManager::RemoveLegacyTokens() {
130 PrefServiceSyncable* prefs = user_profile_->GetPrefs();
131 prefs->RegisterStringPref(prefs::kOAuth1Token,
132 "",
133 PrefServiceSyncable::UNSYNCABLE_PREF);
134 prefs->RegisterStringPref(prefs::kOAuth1Secret,
135 "",
136 PrefServiceSyncable::UNSYNCABLE_PREF);
137 prefs->ClearPref(prefs::kOAuth1Token);
138 prefs->ClearPref(prefs::kOAuth1Secret);
139 prefs->UnregisterPreference(prefs::kOAuth1Token);
140 prefs->UnregisterPreference(prefs::kOAuth1Secret);
141 }
142
143 void OAuth2LoginManager::LoadAndVerifyOAuth2Tokens() {
144 // If we have no cookies, try to load saved OAuth2 token from TokenService.
145 TokenService* token_service = SetupTokenService();
146 token_service->Initialize(GaiaConstants::kChromeSource, user_profile_);
147 token_service->LoadTokensFromDB();
148 }
149
150 void OAuth2LoginManager::FetchOAuth2Tokens() {
151 DCHECK(auth_request_context_.get());
152 // If we have authenticated cookie jar, get OAuth1 token first, then fetch
153 // SID/LSID cookies through OAuthLogin call.
154 oauth2_token_fetcher_.reset(
155 new OAuth2TokenFetcher(this, auth_request_context_));
156 oauth2_token_fetcher_->Start();
157 }
158
159 void OAuth2LoginManager::OnOAuth2TokenAvailable(
160 const GaiaAuthConsumer::ClientLoginResult& gaia_credentials,
161 const GaiaAuthConsumer::ClientOAuthResult& oauth2_tokens) {
162 TokenService* token_service = SetupTokenService();
163 token_service->UpdateCredentialsWithOAuth2(oauth2_tokens);
164 token_service->UpdateCredentials(gaia_credentials);
165 CompleteAuthentication();
166 }
167
168 void OAuth2LoginManager::OnOAuth2TokenFetchFailed() {
169 state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
170 UserManager::Get()->SaveUserOAuthStatus(
171 UserManager::Get()->GetLoggedInUser()->email(),
172 User::OAUTH2_TOKEN_STATUS_INVALID);
173 }
174
175 void OAuth2LoginManager::Observe(
176 int type,
177 const content::NotificationSource& source,
178 const content::NotificationDetails& details) OVERRIDE {
179 TokenService* token_service =
180 TokenServiceFactory::GetForProfile(user_profile_);
181 switch (type) {
182 case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: {
183 refresh_token_ = token_service->GetOAuth2LoginRefreshToken();
184 ReportOAuth2TokensLoaded();
185 // Have we started restoring GAIA auth cookies yet?
186 if (!refresh_token_.empty() && !login_verifier_.get())
187 RestoreSessionCookies();
188
189 break;
190 }
191 case chrome::NOTIFICATION_TOKEN_REQUEST_FAILED: {
192 // TODO(zelidrag): Figure out how to recover from transient errors with
193 // TokenService class - similar to what we do in RetryOnError() here.
194 TokenService::TokenAvailableDetails* token_details =
195 content::Details<TokenService::TokenAvailableDetails>(
196 details).ptr();
197 if (token_details->service() ==
198 GaiaConstants::kGaiaOAuth2LoginRefreshToken) {
199 UserManager::Get()->SaveUserOAuthStatus(
200 UserManager::Get()->GetLoggedInUser()->email(),
201 User::OAUTH2_TOKEN_STATUS_INVALID);
202 }
203 break;
204 }
205 case chrome::NOTIFICATION_TOKEN_AVAILABLE: {
206 TokenService::TokenAvailableDetails* token_details =
207 content::Details<TokenService::TokenAvailableDetails>(
208 details).ptr();
209 if (token_details->service() ==
210 GaiaConstants::kGaiaOAuth2LoginRefreshToken) {
211 DCHECK(!login_verifier_.get());
212 refresh_token_ = token_details->token();
213 RestoreSessionCookies();
214 }
215
216 break;
217 }
218 default:
219 NOTREACHED();
220 break;
221 }
222 }
223
224 void OAuth2LoginManager::RestoreSessionCookies() {
225 VLOG(1) << "Fetched refresh token!";
226 DCHECK(!refresh_token_.empty());
227 if (!login_verifier_.get()) {
228 login_verifier_.reset(
229 new OAuth2LoginVerifier(this,
230 g_browser_process->system_request_context(),
231 user_profile_->GetRequestContext()));
232 }
233 login_verifier_->VerifyRefreshToken(refresh_token_);
234 FetchPolicyTokens();
235 }
236
237 void OAuth2LoginManager::FetchPolicyTokens() {
238 DCHECK(!refresh_token_.empty());
239 if (!oauth2_policy_fetcher_.get() || oauth2_policy_fetcher_->failed()) {
240 // Trigger OAuth2 token fetch for user policy.
241 oauth2_policy_fetcher_.reset(
242 new OAuth2PolicyFetcher(g_browser_process->system_request_context(),
243 refresh_token_));
244 oauth2_policy_fetcher_->Start();
245 }
246 }
247
248 void OAuth2LoginManager::OnOAuth2LoginVerifierSuccess(
249 const std::string& sid,
250 const std::string& lsid,
251 const std::string& auth) {
252 state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
253 UserManager::Get()->SaveUserOAuthStatus(
254 UserManager::Get()->GetLoggedInUser()->email(),
255 User::OAUTH2_TOKEN_STATUS_VALID);
256 }
257
258 void OAuth2LoginManager::OnOAuth2LoginVerifierFailure() {
259 state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
260 UserManager::Get()->SaveUserOAuthStatus(
261 UserManager::Get()->GetLoggedInUser()->email(),
262 User::OAUTH2_TOKEN_STATUS_INVALID);
263 }
264
265 void OAuth2LoginManager::ReportOAuth2TokensLoaded() {
266 VLOG(1) << "Got OAuth2 refresh token!";
267 DCHECK(!loading_reported_);
268 loading_reported_ = true;
269 if (refresh_token_.empty()) {
270 state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
271 UserManager::Get()->SaveUserOAuthStatus(
272 UserManager::Get()->GetLoggedInUser()->email(),
273 User::OAUTH2_TOKEN_STATUS_INVALID);
274 }
275 }
276
277 // OAuth1LoginManager.
278 // TODO(zelidrag): Nuke this one once we switch everything to OAuth2.
279
280 OAuth1LoginManager::OAuth1LoginManager(
281 OAuthLoginManager::Delegate* delegate)
282 : OAuthLoginManager(delegate) {
283 }
284
285 void OAuth1LoginManager::RestoreSession(
286 Profile* user_profile,
287 net::URLRequestContextGetter* auth_request_context,
288 bool restore_from_auth_cookies) {
289 user_profile_ = user_profile;
290 auth_request_context_ = auth_request_context;
291 state_ = OAuthLoginManager::SESSION_RESTORE_IN_PROGRESS;
292
293 // Reuse the access token fetched by the PolicyOAuthFetcher, if it was
294 // used to fetch policies before Profile creation.
295 if (policy_oauth_fetcher_.get() &&
296 !policy_oauth_fetcher_->oauth1_token().empty()) {
297 VLOG(1) << "Resuming profile creation after fetching policy token";
298 oauth1_token_ = policy_oauth_fetcher_->oauth1_token();
299 oauth1_secret_ = policy_oauth_fetcher_->oauth1_secret();
300 StoreOAuth1Tokens();
301 restore_from_auth_cookies = false;
302 }
303 restore_from_auth_cookies_ = restore_from_auth_cookies;
304 ContinueSessionRestore();
305 }
306
307 void OAuth1LoginManager::ContinueSessionRestore() {
308 // Have we even started session restore?
309 if (!user_profile_)
310 return;
311
312 if (restore_from_auth_cookies_) {
313 DCHECK(auth_request_context_.get());
314 // If we don't have it, fetch OAuth1 access token.
315 // Once we get that, we will kick off individual requests for OAuth2
316 // tokens for all our services.
317 // Use off-the-record profile that was used for this step. It should
318 // already contain all needed cookies that will let us skip GAIA's user
319 // authentication UI.
320 oauth1_token_fetcher_.reset(
321 new OAuth1TokenFetcher(this, auth_request_context_));
322 oauth1_token_fetcher_->Start();
323 } else if (ReadOAuth1Tokens()) {
324 // Verify OAuth access token when we find it in the profile and always if
325 // if we don't have cookies.
326 // TODO(xiyuan): Change back to use authenticator to verify token when
327 // we support Gaia in lock screen.
328 VerifyOAuth1AccessToken();
329 } else {
330 UserManager::Get()->SaveUserOAuthStatus(
331 UserManager::Get()->GetLoggedInUser()->email(),
332 User::OAUTH1_TOKEN_STATUS_INVALID);
333 }
334 }
335
336 void OAuth1LoginManager::RestorePolicyTokens(
337 net::URLRequestContextGetter* auth_request_context) {
338 DCHECK(!policy_oauth_fetcher_.get());
339 policy_oauth_fetcher_.reset(
340 new PolicyOAuthFetcher(auth_request_context));
341 policy_oauth_fetcher_->Start();
342 }
343
344 void OAuth1LoginManager::Stop() {
345 oauth1_token_fetcher_.reset();
346 oauth1_login_verifier_.reset();
347 state_ = OAuthLoginManager::SESSION_RESTORE_NOT_STARTED;
348 }
349
350 void OAuth1LoginManager::VerifyOAuth1AccessToken() {
351 // Kick off verification of OAuth1 access token (via OAuthLogin), this should
352 // let us fetch credentials that will be used to initialize sync engine.
353 FetchCredentialsWithOAuth1();
354 delegate_->OnFoundStoredTokens();
355 FetchPolicyTokens();
356 }
357
358 void OAuth1LoginManager::FetchPolicyTokens() {
359 if (!policy_oauth_fetcher_.get() || policy_oauth_fetcher_->failed()) {
360 // Trigger oauth token fetch for user policy.
361 policy_oauth_fetcher_.reset(
362 new PolicyOAuthFetcher(g_browser_process->system_request_context(),
363 oauth1_token_,
364 oauth1_secret_));
365 policy_oauth_fetcher_->Start();
366 }
367 }
368
369 void OAuth1LoginManager::FetchCredentialsWithOAuth1() {
370 oauth1_login_verifier_.reset(
371 new OAuth1LoginVerifier(this,
372 user_profile_->GetRequestContext(),
373 oauth1_token_,
374 oauth1_secret_,
375 UserManager::Get()->GetLoggedInUser()->email()));
376 oauth1_login_verifier_->StartOAuthVerification();
377 }
378
379 void OAuth1LoginManager::OnOAuth1AccessTokenAvailable(
380 const std::string& token,
381 const std::string& secret) {
382 oauth1_token_ = token;
383 oauth1_secret_ = secret;
384 StoreOAuth1Tokens();
385 // Verify OAuth1 token by doing OAuthLogin and fetching credentials. If we
386 // have just transfered auth cookies out of authenticated cookie jar, there
387 // is no need to try to mint them from OAuth token again.
388 VerifyOAuth1AccessToken();
389 }
390
391 void OAuth1LoginManager::OnOAuth1AccessTokenFetchFailed() {
392 // TODO(kochi): Show failure notification UI here?
393 LOG(ERROR) << "Failed to fetch OAuth1 access token.";
394 state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
395 g_browser_process->browser_policy_connector()->RegisterForUserPolicy(
396 EmptyString());
397 }
398
399 void OAuth1LoginManager::OnOAuth1VerificationSucceeded(
400 const std::string& user_name, const std::string& sid,
401 const std::string& lsid, const std::string& auth) {
402 // Kick off sync engine.
403 GaiaAuthConsumer::ClientLoginResult credentials(sid, lsid, auth,
404 std::string());
405 TokenService* token_service =
406 TokenServiceFactory::GetForProfile(user_profile_);
407 token_service->UpdateCredentials(credentials);
408 CompleteAuthentication();
409 }
410
411 void OAuth1LoginManager::OnOAuth1VerificationFailed(
412 const std::string& user_name) {
413 state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
414 UserManager::Get()->SaveUserOAuthStatus(user_name,
415 User::OAUTH1_TOKEN_STATUS_INVALID);
416 }
417
418 bool OAuth1LoginManager::ReadOAuth1Tokens() {
419 // Skip reading oauth token if user does not have a valid status.
420 if (UserManager::Get()->IsUserLoggedIn() &&
421 UserManager::Get()->GetLoggedInUser()->oauth_token_status() !=
422 User::OAUTH1_TOKEN_STATUS_VALID) {
423 return false;
424 }
425
426 PrefService* pref_service = user_profile_->GetPrefs();
427 std::string encoded_token = pref_service->GetString(prefs::kOAuth1Token);
428 std::string encoded_secret = pref_service->GetString(prefs::kOAuth1Secret);
429 if (!encoded_token.length() || !encoded_secret.length())
430 return false;
431
432 std::string decoded_token =
433 CrosLibrary::Get()->GetCertLibrary()->DecryptToken(encoded_token);
434 std::string decoded_secret =
435 CrosLibrary::Get()->GetCertLibrary()->DecryptToken(encoded_secret);
436
437 if (!decoded_token.length() || !decoded_secret.length())
438 return false;
439
440 oauth1_token_ = decoded_token;
441 oauth1_secret_ = decoded_secret;
442 return true;
443 }
444
445 void OAuth1LoginManager::StoreOAuth1Tokens() {
446 DCHECK(!oauth1_token_.empty());
447 DCHECK(!oauth1_secret_.empty());
448 // First store OAuth1 token + service for the current user profile...
449 std::string encrypted_token =
450 CrosLibrary::Get()->GetCertLibrary()->EncryptToken(oauth1_token_);
451 std::string encrypted_secret =
452 CrosLibrary::Get()->GetCertLibrary()->EncryptToken(oauth1_secret_);
453
454 PrefService* pref_service = user_profile_->GetPrefs();
455 User* user = UserManager::Get()->GetLoggedInUser();
456 if (!encrypted_token.empty() && !encrypted_secret.empty()) {
457 pref_service->SetString(prefs::kOAuth1Token, encrypted_token);
458 pref_service->SetString(prefs::kOAuth1Secret, encrypted_secret);
459
460 // ...then record the presence of valid OAuth token for this account in
461 // local state as well.
462 UserManager::Get()->SaveUserOAuthStatus(
463 user->email(), User::OAUTH1_TOKEN_STATUS_VALID);
464 } else {
465 LOG(WARNING) << "Failed to get OAuth1 token/secret encrypted.";
466 // Set the OAuth status invalid so that the user will go through full
467 // GAIA login next time.
468 UserManager::Get()->SaveUserOAuthStatus(
469 user->email(), User::OAUTH1_TOKEN_STATUS_INVALID);
470 }
471 }
472
473 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698