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/oauth2_login_manager.h"
6
7#include <vector>
8
9#include "base/command_line.h"
10#include "base/metrics/histogram.h"
11#include "base/prefs/pref_service.h"
12#include "base/strings/string_util.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/chromeos/login/user_manager.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/signin/profile_oauth2_token_service.h"
17#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
18#include "chrome/common/chrome_switches.h"
19#include "chromeos/chromeos_switches.h"
20#include "google_apis/gaia/gaia_auth_util.h"
21#include "google_apis/gaia/gaia_constants.h"
22#include "google_apis/gaia/gaia_urls.h"
23#include "net/url_request/url_request_context_getter.h"
24
25namespace chromeos {
26
27namespace {
28
29static const char kServiceScopeGetUserInfo[] =
30    "https://www.googleapis.com/auth/userinfo.email";
31static const int kMaxRetries = 5;
32
33}  // namespace
34
35OAuth2LoginManager::OAuth2LoginManager(Profile* user_profile)
36    : user_profile_(user_profile),
37      restore_strategy_(RESTORE_FROM_COOKIE_JAR),
38      state_(SESSION_RESTORE_NOT_STARTED) {
39  GetTokenService()->AddObserver(this);
40  if (CommandLine::ForCurrentProcess()->
41          HasSwitch(chromeos::switches::kOobeSkipPostLogin)) {
42    // For telemetry we should mark session restore completed to avoid
43    // warnings from MergeSessionThrottle.
44    SetSessionRestoreState(SESSION_RESTORE_DONE);
45  }
46}
47
48OAuth2LoginManager::~OAuth2LoginManager() {
49}
50
51void OAuth2LoginManager::AddObserver(OAuth2LoginManager::Observer* observer) {
52  observer_list_.AddObserver(observer);
53}
54
55void OAuth2LoginManager::RemoveObserver(
56    OAuth2LoginManager::Observer* observer) {
57  observer_list_.RemoveObserver(observer);
58}
59
60void OAuth2LoginManager::RestoreSession(
61    net::URLRequestContextGetter* auth_request_context,
62    SessionRestoreStrategy restore_strategy,
63    const std::string& oauth2_refresh_token,
64    const std::string& auth_code) {
65  DCHECK(user_profile_);
66  auth_request_context_ = auth_request_context;
67  restore_strategy_ = restore_strategy;
68  refresh_token_ = oauth2_refresh_token;
69  auth_code_ = auth_code;
70  session_restore_start_ = base::Time::Now();
71  SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_PREPARING);
72  ContinueSessionRestore();
73}
74
75void OAuth2LoginManager::ContinueSessionRestore() {
76  if (restore_strategy_ == RESTORE_FROM_COOKIE_JAR ||
77      restore_strategy_ == RESTORE_FROM_AUTH_CODE) {
78    FetchOAuth2Tokens();
79    return;
80  }
81
82  // Save passed OAuth2 refresh token.
83  if (restore_strategy_ == RESTORE_FROM_PASSED_OAUTH2_REFRESH_TOKEN) {
84    DCHECK(!refresh_token_.empty());
85    restore_strategy_ = RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN;
86    StoreOAuth2Token();
87    return;
88  }
89
90  DCHECK(restore_strategy_ == RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN);
91  RestoreSessionFromSavedTokens();
92}
93
94void OAuth2LoginManager::RestoreSessionFromSavedTokens() {
95  ProfileOAuth2TokenService* token_service = GetTokenService();
96  const std::string& primary_account_id = token_service->GetPrimaryAccountId();
97  if (token_service->RefreshTokenIsAvailable(primary_account_id)) {
98    LOG(WARNING) << "OAuth2 refresh token is already loaded.";
99    RestoreSessionCookies();
100  } else {
101    LOG(WARNING) << "Loading OAuth2 refresh token from database.";
102
103    // Flag user with unknown token status in case there are no saved tokens
104    // and OnRefreshTokenAvailable is not called. Flagging it here would
105    // cause user to go through Gaia in next login to obtain a new refresh
106    // token.
107    UserManager::Get()->SaveUserOAuthStatus(primary_account_id,
108                                            User::OAUTH_TOKEN_STATUS_UNKNOWN);
109
110    token_service->LoadCredentials();
111  }
112}
113
114void OAuth2LoginManager::Stop() {
115  oauth2_token_fetcher_.reset();
116  login_verifier_.reset();
117}
118
119bool OAuth2LoginManager::ShouldBlockTabLoading() {
120  return state_ == SESSION_RESTORE_PREPARING ||
121         state_ == SESSION_RESTORE_IN_PROGRESS;
122}
123
124void OAuth2LoginManager::OnRefreshTokenAvailable(
125    const std::string& account_id) {
126  LOG(WARNING) << "OnRefreshTokenAvailable";
127
128  if (state_ == SESSION_RESTORE_NOT_STARTED)
129    return;
130
131  // TODO(fgorski): Once ProfileOAuth2TokenService supports multi-login, make
132  // sure to restore session cookies in the context of the correct account_id.
133
134  // Do not validate tokens for supervised users, as they don't actually have
135  // oauth2 token.
136  if (UserManager::Get()->IsLoggedInAsLocallyManagedUser()) {
137    LOG(WARNING) << "Logged in as managed user, skip token validation.";
138    return;
139  }
140  // Only restore session cookies for the primary account in the profile.
141  if (GetTokenService()->GetPrimaryAccountId() == account_id) {
142    // Token is loaded. Undo the flagging before token loading.
143    UserManager::Get()->SaveUserOAuthStatus(account_id,
144                                            User::OAUTH2_TOKEN_STATUS_VALID);
145    RestoreSessionCookies();
146  }
147}
148
149ProfileOAuth2TokenService* OAuth2LoginManager::GetTokenService() {
150  ProfileOAuth2TokenService* token_service =
151      ProfileOAuth2TokenServiceFactory::GetForProfile(user_profile_);
152  return token_service;
153}
154
155void OAuth2LoginManager::StoreOAuth2Token() {
156  ProfileOAuth2TokenService* token_service = GetTokenService();
157  std::string primary_account_id = token_service->GetPrimaryAccountId();
158  if (primary_account_id.empty()) {
159    GetAccountIdOfRefreshToken(refresh_token_);
160    return;
161  }
162
163  OnGetUserEmailResponse(primary_account_id);
164}
165
166void OAuth2LoginManager::GetAccountIdOfRefreshToken(
167    const std::string& refresh_token) {
168  gaia::OAuthClientInfo client_info;
169  GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
170  client_info.client_id = gaia_urls->oauth2_chrome_client_id();
171  client_info.client_secret = gaia_urls->oauth2_chrome_client_secret();
172
173  account_id_fetcher_.reset(new gaia::GaiaOAuthClient(
174      auth_request_context_.get()));
175  account_id_fetcher_->RefreshToken(client_info, refresh_token,
176      std::vector<std::string>(1, kServiceScopeGetUserInfo), kMaxRetries,
177      this);
178}
179
180void OAuth2LoginManager::OnRefreshTokenResponse(
181    const std::string& access_token,
182    int expires_in_seconds) {
183  account_id_fetcher_->GetUserEmail(access_token, kMaxRetries, this);
184}
185
186void OAuth2LoginManager::OnGetUserEmailResponse(
187    const std::string& user_email)  {
188  DCHECK(!refresh_token_.empty());
189  account_id_fetcher_.reset();
190  std::string canonicalized = gaia::CanonicalizeEmail(user_email);
191  GetTokenService()->UpdateCredentials(canonicalized, refresh_token_);
192
193  FOR_EACH_OBSERVER(Observer, observer_list_,
194                    OnNewRefreshTokenAvaiable(user_profile_));
195}
196
197void OAuth2LoginManager::OnOAuthError() {
198  account_id_fetcher_.reset();
199  LOG(ERROR) << "Account id fetch failed!";
200  SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED);
201}
202
203void OAuth2LoginManager::OnNetworkError(int response_code) {
204  account_id_fetcher_.reset();
205  LOG(ERROR) << "Account id fetch failed! response_code=" << response_code;
206  SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED);
207}
208
209void OAuth2LoginManager::FetchOAuth2Tokens() {
210  DCHECK(auth_request_context_.get());
211  // If we have authenticated cookie jar, get OAuth1 token first, then fetch
212  // SID/LSID cookies through OAuthLogin call.
213  if (restore_strategy_ == RESTORE_FROM_COOKIE_JAR) {
214    oauth2_token_fetcher_.reset(
215        new OAuth2TokenFetcher(this, auth_request_context_.get()));
216    oauth2_token_fetcher_->StartExchangeFromCookies();
217  } else if (restore_strategy_ == RESTORE_FROM_AUTH_CODE) {
218    DCHECK(!auth_code_.empty());
219    oauth2_token_fetcher_.reset(
220        new OAuth2TokenFetcher(this,
221                               g_browser_process->system_request_context()));
222    oauth2_token_fetcher_->StartExchangeFromAuthCode(auth_code_);
223  } else {
224    NOTREACHED();
225    SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED);
226  }
227}
228
229void OAuth2LoginManager::OnOAuth2TokensAvailable(
230    const GaiaAuthConsumer::ClientOAuthResult& oauth2_tokens) {
231  VLOG(1) << "OAuth2 tokens fetched";
232  DCHECK(refresh_token_.empty());
233  refresh_token_.assign(oauth2_tokens.refresh_token);
234  StoreOAuth2Token();
235}
236
237void OAuth2LoginManager::OnOAuth2TokensFetchFailed() {
238  LOG(ERROR) << "OAuth2 tokens fetch failed!";
239  UMA_HISTOGRAM_ENUMERATION("OAuth2Login.SessionRestore",
240                            SESSION_RESTORE_TOKEN_FETCH_FAILED,
241                            SESSION_RESTORE_COUNT);
242  SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED);
243}
244
245void OAuth2LoginManager::RestoreSessionCookies() {
246  DCHECK(!login_verifier_.get());
247  SetSessionRestoreState(SESSION_RESTORE_IN_PROGRESS);
248  login_verifier_.reset(
249      new OAuth2LoginVerifier(this,
250                              g_browser_process->system_request_context(),
251                              user_profile_->GetRequestContext()));
252  login_verifier_->VerifyProfileTokens(user_profile_);
253}
254
255void OAuth2LoginManager::Shutdown() {
256  GetTokenService()->RemoveObserver(this);
257  login_verifier_.reset();
258  oauth2_token_fetcher_.reset();
259}
260
261void OAuth2LoginManager::OnSessionMergeSuccess() {
262  VLOG(1) << "OAuth2 refresh and/or GAIA token verification succeeded.";
263  UMA_HISTOGRAM_ENUMERATION("OAuth2Login.SessionRestore",
264                            SESSION_RESTORE_SUCCESS,
265                            SESSION_RESTORE_COUNT);
266  SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_DONE);
267}
268
269void OAuth2LoginManager::OnSessionMergeFailure(bool connection_error) {
270  LOG(ERROR) << "OAuth2 refresh and GAIA token verification failed!"
271             << " connection_error: " << connection_error;
272  UMA_HISTOGRAM_ENUMERATION("OAuth2Login.SessionRestore",
273                            SESSION_RESTORE_MERGE_SESSION_FAILED,
274                            SESSION_RESTORE_COUNT);
275  SetSessionRestoreState(connection_error ?
276      OAuth2LoginManager::SESSION_RESTORE_CONNECTION_FAILED :
277      OAuth2LoginManager::SESSION_RESTORE_FAILED);
278}
279
280void OAuth2LoginManager::OnListAccountsSuccess(const std::string& data) {
281  PostMergeVerificationOutcome outcome = POST_MERGE_SUCCESS;
282  // Let's analyze which accounts we see logged in here:
283  std::vector<std::string> accounts = gaia::ParseListAccountsData(data);
284  std::string user_email = gaia::CanonicalizeEmail(
285      GetTokenService()->GetPrimaryAccountId());
286  if (!accounts.empty()) {
287    bool found = false;
288    bool first = true;
289    for (std::vector<std::string>::const_iterator iter = accounts.begin();
290         iter != accounts.end(); ++iter) {
291      if (gaia::CanonicalizeEmail(*iter) == user_email) {
292        found = true;
293        break;
294      }
295
296      first = false;
297    }
298
299    if (!found)
300      outcome = POST_MERGE_MISSING_PRIMARY_ACCOUNT;
301    else if (!first)
302      outcome = POST_MERGE_PRIMARY_NOT_FIRST_ACCOUNT;
303
304  } else {
305    outcome = POST_MERGE_NO_ACCOUNTS;
306  }
307
308  RecordPostMergeOutcome(outcome);
309}
310
311void OAuth2LoginManager::OnListAccountsFailure(bool connection_error) {
312  RecordPostMergeOutcome(connection_error ? POST_MERGE_CONNECTION_FAILED :
313                                            POST_MERGE_VERIFICATION_FAILED);
314}
315
316// static
317void OAuth2LoginManager::RecordPostMergeOutcome(
318    PostMergeVerificationOutcome outcome) {
319  UMA_HISTOGRAM_ENUMERATION("OAuth2Login.PostMergeVerification",
320                            outcome,
321                            POST_MERGE_COUNT);
322}
323
324
325void OAuth2LoginManager::SetSessionRestoreState(
326    OAuth2LoginManager::SessionRestoreState state) {
327  if (state_ == state)
328    return;
329
330  state_ = state;
331  if (state == OAuth2LoginManager::SESSION_RESTORE_FAILED) {
332    UMA_HISTOGRAM_TIMES("OAuth2Login.SessionRestoreTimeToFailure",
333                        base::Time::Now() - session_restore_start_);
334  } else if (state == OAuth2LoginManager::SESSION_RESTORE_DONE) {
335    UMA_HISTOGRAM_TIMES("OAuth2Login.SessionRestoreTimeToSuccess",
336                        base::Time::Now() - session_restore_start_);
337  }
338
339  FOR_EACH_OBSERVER(Observer, observer_list_,
340                    OnSessionRestoreStateChanged(user_profile_, state_));
341}
342
343void OAuth2LoginManager::SetSessionRestoreStartForTesting(
344    const base::Time& time) {
345  session_restore_start_ = time;
346}
347
348}  // namespace chromeos
349