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 "base/command_line.h"
8#include "base/metrics/histogram.h"
9#include "base/prefs/pref_service.h"
10#include "base/strings/string_util.h"
11#include "chrome/browser/browser_process.h"
12#include "chrome/browser/chromeos/login/user_manager.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/signin/profile_oauth2_token_service.h"
15#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
16#include "chrome/browser/signin/token_service.h"
17#include "chrome/browser/signin/token_service_factory.h"
18#include "chrome/common/chrome_switches.h"
19#include "google_apis/gaia/gaia_constants.h"
20#include "net/url_request/url_request_context_getter.h"
21
22namespace chromeos {
23
24OAuth2LoginManager::OAuth2LoginManager(OAuthLoginManager::Delegate* delegate)
25    : OAuthLoginManager(delegate),
26      loading_reported_(false) {
27}
28
29OAuth2LoginManager::~OAuth2LoginManager() {
30  StopObservingRefreshToken();
31}
32
33void OAuth2LoginManager::RestoreSession(
34    Profile* user_profile,
35    net::URLRequestContextGetter* auth_request_context,
36    SessionRestoreStrategy restore_strategy,
37    const std::string& oauth2_refresh_token,
38    const std::string& auth_code) {
39  StopObservingRefreshToken();
40  user_profile_ = user_profile;
41  auth_request_context_ = auth_request_context;
42  state_ = OAuthLoginManager::SESSION_RESTORE_IN_PROGRESS;
43  restore_strategy_ = restore_strategy;
44  refresh_token_ = oauth2_refresh_token;
45  auth_code_ = auth_code;
46
47  // TODO(nkostylev): drop the previous fetchers if RestoreSession() is invoked
48  // for a second Profile, when using multi-profiles. This avoids the DCHECKs
49  // below until OAuthLoginManager fully supports multi-profiles.
50  Stop();
51
52  ProfileOAuth2TokenServiceFactory::GetForProfile(user_profile_)->
53      AddObserver(this);
54
55  ContinueSessionRestore();
56}
57
58void OAuth2LoginManager::ContinueSessionRestore() {
59  if (restore_strategy_ == RESTORE_FROM_COOKIE_JAR ||
60      restore_strategy_ == RESTORE_FROM_AUTH_CODE) {
61    FetchOAuth2Tokens();
62    return;
63  }
64
65  // Save passed OAuth2 refresh token.
66  if (restore_strategy_ == RESTORE_FROM_PASSED_OAUTH2_REFRESH_TOKEN) {
67    DCHECK(!refresh_token_.empty());
68    restore_strategy_ = RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN;
69    GaiaAuthConsumer::ClientOAuthResult oauth2_tokens;
70    oauth2_tokens.refresh_token = refresh_token_;
71    StoreOAuth2Tokens(oauth2_tokens);
72    return;
73  }
74
75  DCHECK(restore_strategy_ == RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN);
76  LoadAndVerifyOAuth2Tokens();
77}
78
79void OAuth2LoginManager::Stop() {
80  oauth2_token_fetcher_.reset();
81  login_verifier_.reset();
82}
83
84void OAuth2LoginManager::OnRefreshTokenAvailable(
85    const std::string& account_id) {
86  // TODO(fgorski): Once ProfileOAuth2TokenService supports multi-login, make
87  // sure to restore session cookies in the context of the correct account_id.
88  RestoreSessionCookies();
89}
90
91TokenService* OAuth2LoginManager::SetupTokenService() {
92  TokenService* token_service =
93      TokenServiceFactory::GetForProfile(user_profile_);
94  return token_service;
95}
96
97void OAuth2LoginManager::StoreOAuth2Tokens(
98    const GaiaAuthConsumer::ClientOAuthResult& oauth2_tokens) {
99  TokenService* token_service = SetupTokenService();
100  token_service->UpdateCredentialsWithOAuth2(oauth2_tokens);
101
102  delegate_->OnNewRefreshTokenAvaiable(user_profile_);
103}
104
105void OAuth2LoginManager::LoadAndVerifyOAuth2Tokens() {
106  // If we have no cookies, try to load saved OAuth2 token from TokenService.
107  TokenService* token_service = SetupTokenService();
108  token_service->Initialize(GaiaConstants::kChromeSource, user_profile_);
109  token_service->LoadTokensFromDB();
110}
111
112void OAuth2LoginManager::FetchOAuth2Tokens() {
113  DCHECK(auth_request_context_.get());
114  // If we have authenticated cookie jar, get OAuth1 token first, then fetch
115  // SID/LSID cookies through OAuthLogin call.
116  if (restore_strategy_ == RESTORE_FROM_COOKIE_JAR) {
117    oauth2_token_fetcher_.reset(
118        new OAuth2TokenFetcher(this, auth_request_context_.get()));
119    oauth2_token_fetcher_->StartExchangeFromCookies();
120  } else if (restore_strategy_ == RESTORE_FROM_AUTH_CODE) {
121    DCHECK(!auth_code_.empty());
122    oauth2_token_fetcher_.reset(
123        new OAuth2TokenFetcher(this,
124                               g_browser_process->system_request_context()));
125    oauth2_token_fetcher_->StartExchangeFromAuthCode(auth_code_);
126  } else {
127    NOTREACHED();
128  }
129}
130
131void OAuth2LoginManager::OnOAuth2TokensAvailable(
132    const GaiaAuthConsumer::ClientOAuthResult& oauth2_tokens) {
133  LOG(INFO) << "OAuth2 tokens fetched";
134  StoreOAuth2Tokens(oauth2_tokens);
135}
136
137void OAuth2LoginManager::OnOAuth2TokensFetchFailed() {
138  LOG(ERROR) << "OAuth2 tokens fetch failed!";
139  state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
140  UserManager::Get()->SaveUserOAuthStatus(
141      UserManager::Get()->GetLoggedInUser()->email(),
142      User::OAUTH2_TOKEN_STATUS_INVALID);
143  UMA_HISTOGRAM_ENUMERATION("OAuth2Login.SessionRestore",
144                            SESSION_RESTORE_TOKEN_FETCH_FAILED,
145                            SESSION_RESTORE_COUNT);
146}
147
148void OAuth2LoginManager::RestoreSessionCookies() {
149  DCHECK(!login_verifier_.get());
150  login_verifier_.reset(
151      new OAuth2LoginVerifier(this,
152                              g_browser_process->system_request_context(),
153                              user_profile_->GetRequestContext()));
154  login_verifier_->VerifyProfileTokens(user_profile_);
155}
156
157void OAuth2LoginManager::OnOAuthLoginSuccess(
158    const GaiaAuthConsumer::ClientLoginResult& gaia_credentials) {
159  LOG(INFO) << "OAuth2 refresh token successfully exchanged for GAIA token.";
160  StartTokenService(gaia_credentials);
161}
162
163void OAuth2LoginManager::OnOAuthLoginFailure() {
164  LOG(ERROR) << "OAuth2 refresh token verification failed!";
165  state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
166  UserManager::Get()->SaveUserOAuthStatus(
167      UserManager::Get()->GetLoggedInUser()->email(),
168      User::OAUTH2_TOKEN_STATUS_INVALID);
169  UMA_HISTOGRAM_ENUMERATION("OAuth2Login.SessionRestore",
170                            SESSION_RESTORE_OAUTHLOGIN_FAILED,
171                            SESSION_RESTORE_COUNT);
172  delegate_->OnCompletedMergeSession();
173}
174
175void OAuth2LoginManager::OnSessionMergeSuccess() {
176  LOG(INFO) << "OAuth2 refresh and/or GAIA token verification succeeded.";
177  state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
178  UserManager::Get()->SaveUserOAuthStatus(
179      UserManager::Get()->GetLoggedInUser()->email(),
180      User::OAUTH2_TOKEN_STATUS_VALID);
181  UMA_HISTOGRAM_ENUMERATION("OAuth2Login.SessionRestore",
182                            SESSION_RESTORE_SUCCESS,
183                            SESSION_RESTORE_COUNT);
184  delegate_->OnCompletedMergeSession();
185}
186
187void OAuth2LoginManager::OnSessionMergeFailure() {
188  LOG(ERROR) << "OAuth2 refresh and GAIA token verification failed!";
189  state_ = OAuthLoginManager::SESSION_RESTORE_DONE;
190  UserManager::Get()->SaveUserOAuthStatus(
191      UserManager::Get()->GetLoggedInUser()->email(),
192      User::OAUTH2_TOKEN_STATUS_INVALID);
193  UMA_HISTOGRAM_ENUMERATION("OAuth2Login.SessionRestore",
194                            SESSION_RESTORE_MERGE_SESSION_FAILED,
195                            SESSION_RESTORE_COUNT);
196  delegate_->OnCompletedMergeSession();
197}
198
199void OAuth2LoginManager::StartTokenService(
200    const GaiaAuthConsumer::ClientLoginResult& gaia_credentials) {
201  TokenService* token_service = SetupTokenService();
202  token_service->UpdateCredentials(gaia_credentials);
203  CompleteAuthentication();
204}
205
206void OAuth2LoginManager::StopObservingRefreshToken() {
207  if (user_profile_) {
208    ProfileOAuth2TokenServiceFactory::GetForProfile(user_profile_)->
209        RemoveObserver(this);
210  }
211}
212
213}  // namespace chromeos
214