1// Copyright (c) 2011 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/sync/signin_manager.h"
6
7#include "base/string_util.h"
8#include "chrome/browser/net/gaia/token_service.h"
9#include "chrome/browser/prefs/pref_service.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/common/net/gaia/gaia_constants.h"
12#include "chrome/common/pref_names.h"
13#include "content/common/notification_service.h"
14
15const char kGetInfoEmailKey[] = "email";
16
17SigninManager::SigninManager()
18    : profile_(NULL), had_two_factor_error_(false) {}
19
20SigninManager::~SigninManager() {}
21
22// static
23void SigninManager::RegisterUserPrefs(PrefService* user_prefs) {
24  user_prefs->RegisterStringPref(prefs::kGoogleServicesUsername, "");
25}
26
27void SigninManager::Initialize(Profile* profile) {
28  profile_ = profile;
29  username_ = profile_->GetPrefs()->GetString(prefs::kGoogleServicesUsername);
30  profile_->GetTokenService()->Initialize(
31      GaiaConstants::kChromeSource, profile_);
32  if (!username_.empty()) {
33    profile_->GetTokenService()->LoadTokensFromDB();
34  }
35}
36
37// If a username already exists, the user is logged in.
38const std::string& SigninManager::GetUsername() {
39  return username_;
40}
41
42void SigninManager::SetUsername(const std::string& username) {
43  username_ = username;
44}
45
46// Users must always sign out before they sign in again.
47void SigninManager::StartSignIn(const std::string& username,
48                                const std::string& password,
49                                const std::string& login_token,
50                                const std::string& login_captcha) {
51  DCHECK(username_.empty());
52#if !defined(OS_CHROMEOS)
53  // The Sign out should clear the token service credentials.
54  // Note: In CHROMEOS we might have valid credentials but still need to
55  // set up 2-factor authentication.
56  DCHECK(!profile_->GetTokenService()->AreCredentialsValid());
57#endif
58  username_.assign(username);
59  password_.assign(password);
60
61  client_login_.reset(new GaiaAuthFetcher(this,
62                                          GaiaConstants::kChromeSource,
63                                          profile_->GetRequestContext()));
64  client_login_->StartClientLogin(username,
65                                  password,
66                                  "",
67                                  login_token,
68                                  login_captcha,
69                                  GaiaAuthFetcher::HostedAccountsNotAllowed);
70}
71
72void SigninManager::ProvideSecondFactorAccessCode(
73    const std::string& access_code) {
74  DCHECK(!username_.empty() && !password_.empty() &&
75      last_result_.data.empty());
76
77  client_login_.reset(new GaiaAuthFetcher(this,
78                                          GaiaConstants::kChromeSource,
79                                          profile_->GetRequestContext()));
80  client_login_->StartClientLogin(username_,
81                                  access_code,
82                                  "",
83                                  std::string(),
84                                  std::string(),
85                                  GaiaAuthFetcher::HostedAccountsNotAllowed);
86}
87
88void SigninManager::SignOut() {
89  if (!profile_)
90    return;
91
92  client_login_.reset();
93  last_result_ = ClientLoginResult();
94  username_.clear();
95  password_.clear();
96  had_two_factor_error_ = false;
97  profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, username_);
98  profile_->GetPrefs()->ScheduleSavePersistentPrefs();
99  profile_->GetTokenService()->ResetCredentialsInMemory();
100  profile_->GetTokenService()->EraseTokensFromDB();
101}
102
103void SigninManager::OnClientLoginSuccess(const ClientLoginResult& result) {
104  last_result_ = result;
105  // Make a request for the canonical email address.
106  client_login_->StartGetUserInfo(result.lsid, kGetInfoEmailKey);
107}
108
109void SigninManager::OnGetUserInfoSuccess(const std::string& key,
110                                         const std::string& value) {
111  DCHECK(key == kGetInfoEmailKey);
112
113  username_ = value;
114  profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, username_);
115  profile_->GetPrefs()->ScheduleSavePersistentPrefs();
116
117  GoogleServiceSigninSuccessDetails details(username_, password_);
118  NotificationService::current()->Notify(
119      NotificationType::GOOGLE_SIGNIN_SUCCESSFUL,
120      Source<Profile>(profile_),
121      Details<const GoogleServiceSigninSuccessDetails>(&details));
122
123  password_.clear();  // Don't need it anymore.
124
125  profile_->GetTokenService()->UpdateCredentials(last_result_);
126  DCHECK(profile_->GetTokenService()->AreCredentialsValid());
127  profile_->GetTokenService()->StartFetchingTokens();
128}
129
130void SigninManager::OnGetUserInfoKeyNotFound(const std::string& key) {
131  DCHECK(key == kGetInfoEmailKey);
132  LOG(ERROR) << "Account is not associated with a valid email address. "
133             << "Login failed.";
134  OnClientLoginFailure(GoogleServiceAuthError(
135      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
136}
137
138void SigninManager::OnGetUserInfoFailure(const GoogleServiceAuthError& error) {
139  LOG(ERROR) << "Unable to retreive the canonical email address. Login failed.";
140  OnClientLoginFailure(error);
141}
142
143void SigninManager::OnClientLoginFailure(const GoogleServiceAuthError& error) {
144  NotificationService::current()->Notify(
145      NotificationType::GOOGLE_SIGNIN_FAILED,
146      Source<Profile>(profile_),
147      Details<const GoogleServiceAuthError>(&error));
148
149  // We don't sign-out if the password was valid and we're just dealing with
150  // a second factor error, and we don't sign out if we're dealing with
151  // an invalid access code (again, because the password was valid).
152  bool invalid_gaia = error.state() ==
153      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS;
154  if (error.state() == GoogleServiceAuthError::TWO_FACTOR ||
155      (had_two_factor_error_ && invalid_gaia)) {
156    had_two_factor_error_ = true;
157    return;
158  }
159
160  SignOut();
161}
162