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

Unified 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: wired policy with OAuth2 path 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chromeos/login/oauth_login_manager.cc
diff --git a/chrome/browser/chromeos/login/oauth_login_manager.cc b/chrome/browser/chromeos/login/oauth_login_manager.cc
new file mode 100644
index 0000000000000000000000000000000000000000..68e905a48c9e7a07a4ea6665269b0d92bca76067
--- /dev/null
+++ b/chrome/browser/chromeos/login/oauth_login_manager.cc
@@ -0,0 +1,483 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/login/oauth_login_manager.h"
+
+#include "base/command_line.h"
+#include "base/metrics/histogram.h"
+#include "base/string_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/policy/browser_policy_connector.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/token_service.h"
+#include "chrome/browser/signin/token_service_factory.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/notification_observer.h"
Joao da Silva 2013/01/11 16:45:07 Already in .h
zel 2013/01/11 19:51:16 Done.
+#include "content/public/browser/notification_service.h"
+#include "google_apis/gaia/gaia_auth_fetcher.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "net/url_request/url_request_context_getter.h"
+
+using content::BrowserThread;
+
+namespace {
+
+// OAuth token verification max retry count.
+const int kMaxCookieRecoveryAttemptCount = 5;
+// OAuth token verification retry delay in milliseconds.
+const int kCookieRecoveryRestartDelay = 10000;
Joao da Silva 2013/01/11 16:45:07 These 2 aren't used.
zel 2013/01/11 19:51:16 Done.
+
+} // namespace
+
+
+namespace chromeos {
+
+// OAuthLoginManagerFactory.
+OAuthLoginManager* OAuthLoginManagerFactory::Create(
+ OAuthLoginManager::Delegate* delegate) {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(::switches::kForceOAuth2))
+ return new OAuth2LoginManager(delegate);
+
+ return new OAuth1LoginManager(delegate);
+}
+
+// OAuthLoginManager.
+
+void OAuthLoginManager::CompleteAuthentication() {
+ delegate_->OnCompletedAuthentication(user_profile_);
+ TokenService* token_service =
+ TokenServiceFactory::GetForProfile(user_profile_);
+ if (token_service->AreCredentialsValid())
+ token_service->StartFetchingTokens();
+}
+
+OAuthLoginManager::OAuthLoginManager(Delegate* delegate)
+ : delegate_(delegate),
+ user_profile_(NULL),
+ restore_from_auth_cookies_(false),
+ state_(SESSION_RESTORE_NOT_STARTED) {
+}
+
+// OAuth2LoginManager.
+
+OAuth2LoginManager::OAuth2LoginManager(OAuthLoginManager::Delegate* delegate)
+ : OAuthLoginManager(delegate),
+ loading_reported_(false),
+ restore_attempt_count_(0) {
+}
+
+void OAuth2LoginManager::RestoreSession(
+ Profile* user_profile,
+ net::URLRequestContextGetter* auth_request_context,
+ bool restore_from_auth_cookies) {
+ // TODO(zelidrag): Remove eventualy the next line in some future milestone.
+ RemoveLegacyTokens();
+
+ user_profile_ = user_profile;
+ auth_request_context_ = auth_request_context;
+ state_ = OAuthLoginManager::SESSION_RESTORE_IN_PROGRESS;
+
+ // Reuse the access token fetched by the OAuth2PolicyFetcher, if it was
+ // used to fetch policies before Profile creation.
+ if (oauth2_policy_fetcher_.get() &&
+ oauth2_policy_fetcher_->has_oauth2_tokens()) {
+ VLOG(1) << "Resuming profile creation after fetching policy token";
+ refresh_token_ = oauth2_policy_fetcher_->oauth2_tokens().refresh_token;
+ restore_from_auth_cookies = false;
+ }
+ restore_from_auth_cookies_ = restore_from_auth_cookies;
+ ContinueSessionRestore();
+}
+
+void OAuth2LoginManager::ContinueSessionRestore() {
+ if (restore_from_auth_cookies_) {
+ FetchOAuth2Tokens();
+ return;
+ }
+
+ // Did we already fetch the refresh token (either policy or db)?
+ if (!refresh_token_.empty()) {
+ // TODO(zelidrag): Figure out where to stick that refresh_token_ into.
+ // We probalby need bit more than that.
+ }
+ LoadAndVerifyOAuth2Tokens();
+}
+
+
+void OAuth2LoginManager::RestorePolicyTokens(
+ net::URLRequestContextGetter* auth_request_context) {
+ oauth2_policy_fetcher_.reset(
+ new OAuth2PolicyFetcher(auth_request_context,
+ g_browser_process->system_request_context()));
+ oauth2_policy_fetcher_->Start();
+}
+
+void OAuth2LoginManager::Stop() {
+ oauth2_token_fetcher_.reset();
+ login_verifier_.reset();
+}
+
+TokenService* OAuth2LoginManager::SetupTokenService() {
+ DCHECK(registrar_.IsEmpty());
+ TokenService* token_service =
+ TokenServiceFactory::GetForProfile(user_profile_);
+ registrar_.Add(this,
+ chrome::NOTIFICATION_TOKEN_LOADING_FINISHED,
+ content::Source<TokenService>(token_service));
+ registrar_.Add(this,
+ chrome::NOTIFICATION_TOKEN_AVAILABLE,
+ content::Source<TokenService>(token_service));
+ registrar_.Add(this,
+ chrome::NOTIFICATION_TOKEN_REQUEST_FAILED,
+ content::Source<TokenService>(token_service));
+ return token_service;
+}
+
+void OAuth2LoginManager::RemoveLegacyTokens() {
+ PrefServiceSyncable* prefs = user_profile_->GetPrefs();
+ prefs->RegisterStringPref(prefs::kOAuth1Token,
+ "",
+ PrefServiceSyncable::UNSYNCABLE_PREF);
+ prefs->RegisterStringPref(prefs::kOAuth1Secret,
+ "",
+ PrefServiceSyncable::UNSYNCABLE_PREF);
+ prefs->ClearPref(prefs::kOAuth1Token);
+ prefs->ClearPref(prefs::kOAuth1Secret);
+ prefs->UnregisterPreference(prefs::kOAuth1Token);
+ prefs->UnregisterPreference(prefs::kOAuth1Secret);
+}
+
+void OAuth2LoginManager::LoadAndVerifyOAuth2Tokens() {
+ // If we have no cookies, try to load saved OAuth2 token from TokenService.
+ TokenService* token_service = SetupTokenService();
+ token_service->Initialize(GaiaConstants::kChromeSource, user_profile_);
+ token_service->LoadTokensFromDB();
+}
+
+void OAuth2LoginManager::FetchOAuth2Tokens() {
+ DCHECK(auth_request_context_.get());
+ // If we have authenticated cookie jar, get OAuth1 token first, then fetch
+ // SID/LSID cookies through OAuthLogin call.
+ oauth2_token_fetcher_.reset(
+ new OAuth2TokenFetcher(this, auth_request_context_));
+ oauth2_token_fetcher_->Start();
+}
+
+void OAuth2LoginManager::OnOAuth2TokenAvailable(
+ const GaiaAuthConsumer::ClientLoginResult& gaia_credentials,
+ const GaiaAuthConsumer::ClientOAuthResult& oauth2_tokens) {
+ TokenService* token_service = SetupTokenService();
+ token_service->UpdateCredentialsWithOAuth2(oauth2_tokens);
+ token_service->UpdateCredentials(gaia_credentials);
+ CompleteAuthentication();
+}
+
+void OAuth2LoginManager::OnOAuth2TokenFetchFailed() {
+ state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
+ UserManager::Get()->SaveUserOAuthStatus(
+ UserManager::Get()->GetLoggedInUser()->email(),
+ User::OAUTH2_TOKEN_STATUS_INVALID);
+}
+
+void OAuth2LoginManager::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE {
+ TokenService* token_service =
+ TokenServiceFactory::GetForProfile(user_profile_);
+ switch (type) {
+ case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: {
+ refresh_token_ = token_service->GetOAuth2LoginRefreshToken();
+ ReportOAuth2TokensLoaded();
+ // Have we started restoring GAIA auth cookies yet?
+ if (!refresh_token_.empty() && !login_verifier_.get())
+ RestoreSessionCookies();
+
+ break;
+ }
+ case chrome::NOTIFICATION_TOKEN_REQUEST_FAILED: {
+ // TODO(zelidrag): Figure out how to recover from transient errors with
+ // TokenService class - similar to what we do in RetryOnError() here.
+ TokenService::TokenAvailableDetails* token_details =
+ content::Details<TokenService::TokenAvailableDetails>(
+ details).ptr();
+ if (token_details->service() ==
+ GaiaConstants::kGaiaOAuth2LoginRefreshToken) {
+ UserManager::Get()->SaveUserOAuthStatus(
+ UserManager::Get()->GetLoggedInUser()->email(),
+ User::OAUTH2_TOKEN_STATUS_INVALID);
+ }
+ break;
+ }
+ case chrome::NOTIFICATION_TOKEN_AVAILABLE: {
+ TokenService::TokenAvailableDetails* token_details =
+ content::Details<TokenService::TokenAvailableDetails>(
+ details).ptr();
+ if (token_details->service() ==
+ GaiaConstants::kGaiaOAuth2LoginRefreshToken) {
+ DCHECK(!login_verifier_.get());
+ refresh_token_ = token_details->token();
+ RestoreSessionCookies();
+ }
+
+ break;
+ }
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+void OAuth2LoginManager::RestoreSessionCookies() {
+ VLOG(1) << "Fetched refresh token!";
+ DCHECK(!refresh_token_.empty());
+ if (!login_verifier_.get()) {
+ login_verifier_.reset(
+ new OAuth2LoginVerifier(this,
+ g_browser_process->system_request_context(),
+ user_profile_->GetRequestContext()));
+ }
+ login_verifier_->VerifyRefreshToken(refresh_token_);
+}
+
+void OAuth2LoginManager::FetchPolicyTokens() {
Joao da Silva 2013/01/11 16:45:07 This is never used. I think it should be called fr
zel 2013/01/11 19:51:16 It's actually just needed in RestoreSessionCookies
+ if (!oauth2_policy_fetcher_.get() || oauth2_policy_fetcher_->failed()) {
+ // Trigger OAuth2 token fetch for user policy.
+ oauth2_policy_fetcher_.reset(
+ new OAuth2PolicyFetcher(g_browser_process->system_request_context(),
+ refresh_token_));
+ oauth2_policy_fetcher_->Start();
+ }
+}
+
+void OAuth2LoginManager::OnOAuth2LoginVerifierSuccess(
+ const std::string& sid,
+ const std::string& lsid,
+ const std::string& auth) {
+ state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
+ UserManager::Get()->SaveUserOAuthStatus(
+ UserManager::Get()->GetLoggedInUser()->email(),
+ User::OAUTH2_TOKEN_STATUS_VALID);
+}
+
+void OAuth2LoginManager::OnOAuth2LoginVerifierFaulure() {
Joao da Silva 2013/01/11 16:45:07 *Failure
zel 2013/01/11 19:51:16 Done.
+ state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
+ UserManager::Get()->SaveUserOAuthStatus(
+ UserManager::Get()->GetLoggedInUser()->email(),
+ User::OAUTH2_TOKEN_STATUS_INVALID);
+}
+
+void OAuth2LoginManager::ReportOAuth2TokensLoaded() {
+ VLOG(1) << "Got OAuth2 refresh token!";
+ DCHECK(!loading_reported_);
+ loading_reported_ = true;
+ if (refresh_token_.empty()) {
+ state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
+ UserManager::Get()->SaveUserOAuthStatus(
+ UserManager::Get()->GetLoggedInUser()->email(),
+ User::OAUTH2_TOKEN_STATUS_INVALID);
+ }
+}
+
+// OAuth1LoginManager.
+// TODO(zelidrag): Nuke this one once we switch everything to OAuth2.
+
+OAuth1LoginManager::OAuth1LoginManager(
+ OAuthLoginManager::Delegate* delegate)
+ : OAuthLoginManager(delegate) {
+}
+
+void OAuth1LoginManager::RestoreSession(
+ Profile* user_profile,
+ net::URLRequestContextGetter* auth_request_context,
+ bool restore_from_auth_cookies) {
+ user_profile_ = user_profile;
+ auth_request_context_ = auth_request_context;
+ state_ = OAuthLoginManager::SESSION_RESTORE_IN_PROGRESS;
+
+ // Reuse the access token fetched by the PolicyOAuthFetcher, if it was
+ // used to fetch policies before Profile creation.
+ if (policy_oauth_fetcher_.get() &&
+ !policy_oauth_fetcher_->oauth1_token().empty()) {
+ VLOG(1) << "Resuming profile creation after fetching policy token";
+ oauth1_token_ = policy_oauth_fetcher_->oauth1_token();
+ oauth1_secret_ = policy_oauth_fetcher_->oauth1_secret();
+ StoreOAuth1Tokens();
+ restore_from_auth_cookies = false;
+ }
+ restore_from_auth_cookies_ = restore_from_auth_cookies;
+ ContinueSessionRestore();
+}
+
+void OAuth1LoginManager::ContinueSessionRestore() {
+ // Have we even started session restore?
+ if (!user_profile_)
+ return;
+
+ if (restore_from_auth_cookies_) {
+ DCHECK(auth_request_context_.get());
+ // If we don't have it, fetch OAuth1 access token.
+ // Once we get that, we will kick off individual requests for OAuth2
+ // tokens for all our services.
+ // Use off-the-record profile that was used for this step. It should
+ // already contain all needed cookies that will let us skip GAIA's user
+ // authentication UI.
+ oauth1_token_fetcher_.reset(
+ new OAuth1TokenFetcher(this, auth_request_context_));
+ oauth1_token_fetcher_->Start();
+ } else if (ReadOAuth1Tokens()) {
+ // Verify OAuth access token when we find it in the profile and always if
+ // if we don't have cookies.
+ // TODO(xiyuan): Change back to use authenticator to verify token when
+ // we support Gaia in lock screen.
+ VerifyOAuth1AccessToken();
+ } else {
+ UserManager::Get()->SaveUserOAuthStatus(
+ UserManager::Get()->GetLoggedInUser()->email(),
+ User::OAUTH1_TOKEN_STATUS_INVALID);
+ }
+}
+
+void OAuth1LoginManager::RestorePolicyTokens(
+ net::URLRequestContextGetter* auth_request_context) {
+ DCHECK(!policy_oauth_fetcher_.get());
+ policy_oauth_fetcher_.reset(
+ new PolicyOAuthFetcher(auth_request_context));
+ policy_oauth_fetcher_->Start();
+}
+
+void OAuth1LoginManager::Stop() {
+ oauth1_token_fetcher_.reset();
+ oauth1_login_verifier_.reset();
+ state_ = OAuthLoginManager::SESSION_RESTORE_NOT_STARTED;
+}
+
+void OAuth1LoginManager::VerifyOAuth1AccessToken() {
+ // Kick off verification of OAuth1 access token (via OAuthLogin), this should
+ // let us fetch credentials that will be used to initialize sync engine.
+ FetchCredentialsWithOAuth1();
+ delegate_->OnFoundStoredTokens();
+ FetchPolicyTokens();
+}
+
+void OAuth1LoginManager::FetchPolicyTokens() {
+ if (!policy_oauth_fetcher_.get() || policy_oauth_fetcher_->failed()) {
+ // Trigger oauth token fetch for user policy.
+ policy_oauth_fetcher_.reset(
+ new PolicyOAuthFetcher(g_browser_process->system_request_context(),
+ oauth1_token_,
+ oauth1_secret_));
+ policy_oauth_fetcher_->Start();
+ }
+}
+
+void OAuth1LoginManager::FetchCredentialsWithOAuth1() {
+ oauth1_login_verifier_.reset(
+ new OAuth1LoginVerifier(this,
+ user_profile_->GetRequestContext(),
+ oauth1_token_,
+ oauth1_secret_,
+ UserManager::Get()->GetLoggedInUser()->email()));
+ oauth1_login_verifier_->StartOAuthVerification();
+}
+
+void OAuth1LoginManager::OnOAuth1AccessTokenAvailable(
+ const std::string& token,
+ const std::string& secret) {
+ oauth1_token_ = token;
+ oauth1_secret_ = secret;
+ StoreOAuth1Tokens();
+ // Verify OAuth1 token by doing OAuthLogin and fetching credentials. If we
+ // have just transfered auth cookies out of authenticated cookie jar, there
+ // is no need to try to mint them from OAuth token again.
+ VerifyOAuth1AccessToken();
+}
+
+void OAuth1LoginManager::OnOAuth1AccessTokenFetchFailed() {
+ // TODO(kochi): Show failure notification UI here?
+ LOG(ERROR) << "Failed to fetch OAuth1 access token.";
+ state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
+ g_browser_process->browser_policy_connector()->RegisterForUserPolicy(
+ EmptyString());
+}
+
+void OAuth1LoginManager::OnOAuth1VerificationSucceeded(
+ const std::string& user_name, const std::string& sid,
+ const std::string& lsid, const std::string& auth) {
+ // Kick off sync engine.
+ GaiaAuthConsumer::ClientLoginResult credentials(sid, lsid, auth,
+ std::string());
+ TokenService* token_service =
+ TokenServiceFactory::GetForProfile(user_profile_);
+ token_service->UpdateCredentials(credentials);
+ CompleteAuthentication();
+}
+
+void OAuth1LoginManager::OnOAuth1VerificationFailed(
+ const std::string& user_name) {
+ state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
+ UserManager::Get()->SaveUserOAuthStatus(user_name,
+ User::OAUTH1_TOKEN_STATUS_INVALID);
+}
+
+bool OAuth1LoginManager::ReadOAuth1Tokens() {
+ // Skip reading oauth token if user does not have a valid status.
+ if (UserManager::Get()->IsUserLoggedIn() &&
+ UserManager::Get()->GetLoggedInUser()->oauth_token_status() !=
+ User::OAUTH1_TOKEN_STATUS_VALID) {
+ return false;
+ }
+
+ PrefService* pref_service = user_profile_->GetPrefs();
+ std::string encoded_token = pref_service->GetString(prefs::kOAuth1Token);
+ std::string encoded_secret = pref_service->GetString(prefs::kOAuth1Secret);
+ if (!encoded_token.length() || !encoded_secret.length())
+ return false;
+
+ std::string decoded_token =
+ CrosLibrary::Get()->GetCertLibrary()->DecryptToken(encoded_token);
+ std::string decoded_secret =
+ CrosLibrary::Get()->GetCertLibrary()->DecryptToken(encoded_secret);
+
+ if (!decoded_token.length() || !decoded_secret.length())
+ return false;
+
+ oauth1_token_ = decoded_token;
+ oauth1_secret_ = decoded_secret;
+ return true;
+}
+
+void OAuth1LoginManager::StoreOAuth1Tokens() {
+ DCHECK(!oauth1_token_.empty());
+ DCHECK(!oauth1_secret_.empty());
+ // First store OAuth1 token + service for the current user profile...
+ std::string encrypted_token =
+ CrosLibrary::Get()->GetCertLibrary()->EncryptToken(oauth1_token_);
+ std::string encrypted_secret =
+ CrosLibrary::Get()->GetCertLibrary()->EncryptToken(oauth1_secret_);
+
+ PrefService* pref_service = user_profile_->GetPrefs();
+ User* user = UserManager::Get()->GetLoggedInUser();
+ if (!encrypted_token.empty() && !encrypted_secret.empty()) {
+ pref_service->SetString(prefs::kOAuth1Token, encrypted_token);
+ pref_service->SetString(prefs::kOAuth1Secret, encrypted_secret);
+
+ // ...then record the presence of valid OAuth token for this account in
+ // local state as well.
+ UserManager::Get()->SaveUserOAuthStatus(
+ user->email(), User::OAUTH1_TOKEN_STATUS_VALID);
+ } else {
+ LOG(WARNING) << "Failed to get OAuth1 token/secret encrypted.";
+ // Set the OAuth status invalid so that the user will go through full
+ // GAIA login next time.
+ UserManager::Get()->SaveUserOAuthStatus(
+ user->email(), User::OAUTH1_TOKEN_STATUS_INVALID);
+ }
+}
+
+} // namespace chromeos

Powered by Google App Engine
This is Rietveld 408576698