1 // Copyright 2014 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.
5 #include "chrome/browser/chromeos/login/easy_unlock/bootstrap_user_context_initializer.h"
9 #include "base/strings/string_util.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h"
12 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
13 #include "chrome/browser/chromeos/profiles/profile_helper.h"
14 #include "chrome/browser/signin/easy_unlock_service_signin_chromeos.h"
15 #include "components/user_manager/user_manager.h"
16 #include "crypto/random.h"
17 #include "google_apis/gaia/gaia_constants.h"
18 #include "google_apis/gaia/gaia_urls.h"
24 const int kMaxGaiaRetries
= 5;
25 const int kUserKeyByteSize
= 16;
29 BootstrapUserContextInitializer::CompleteCallback
*
30 BootstrapUserContextInitializer::complete_callback_for_testing_
= NULL
;
33 void BootstrapUserContextInitializer::SetCompleteCallbackForTesting(
34 CompleteCallback
* callback
) {
35 complete_callback_for_testing_
= callback
;
38 BootstrapUserContextInitializer::BootstrapUserContextInitializer()
39 : random_key_used_(false),
40 weak_ptr_factory_(this) {
43 BootstrapUserContextInitializer::~BootstrapUserContextInitializer() {
46 void BootstrapUserContextInitializer::Start(const std::string
& auth_code
,
47 const CompleteCallback
& callback
) {
48 DCHECK(!callback
.is_null());
51 user_context_
.SetAuthFlow(UserContext::AUTH_FLOW_EASY_BOOTSTRAP
);
52 StartTokenFetch(auth_code
);
55 void BootstrapUserContextInitializer::StartTokenFetch(
56 const std::string
& auth_code
) {
57 DCHECK(!token_fetcher_
);
59 gaia::OAuthClientInfo client_info
;
60 GaiaUrls
* gaia_urls
= GaiaUrls::GetInstance();
61 client_info
.client_id
= gaia_urls
->oauth2_chrome_client_id();
62 client_info
.client_secret
= gaia_urls
->oauth2_chrome_client_secret();
65 new gaia::GaiaOAuthClient(g_browser_process
->system_request_context()));
66 token_fetcher_
->GetTokensFromAuthCode(client_info
, auth_code
, kMaxGaiaRetries
,
70 void BootstrapUserContextInitializer::StartCheckExistingKeys() {
71 const std::string
& user_id
= user_context_
.GetUserID();
73 // Use random key for the first time user.
74 if (!user_manager::UserManager::Get()->IsKnownUser(user_id
)) {
79 EasyUnlockKeyManager
* key_manager
=
80 UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
81 key_manager
->GetDeviceDataList(
83 base::Bind(&BootstrapUserContextInitializer::OnGetEasyUnlockData
,
84 weak_ptr_factory_
.GetWeakPtr()));
87 void BootstrapUserContextInitializer::OnGetEasyUnlockData(
89 const EasyUnlockDeviceKeyDataList
& data_list
) {
90 // Existing user must have Smart lock keys to use bootstrap flow.
91 if (!success
|| data_list
.empty()) {
92 LOG(ERROR
) << "Unable to get Easy unlock key data.";
97 EasyUnlockService
* service
=
98 EasyUnlockService::Get(ProfileHelper::GetSigninProfile());
99 service
->AddObserver(this);
101 static_cast<EasyUnlockServiceSignin
*>(service
)
102 ->SetCurrentUser(user_context_
.GetUserID());
103 OnScreenlockStateChanged(service
->GetScreenlockState());
106 void BootstrapUserContextInitializer::OnEasyUnlockAuthenticated(
107 EasyUnlockAuthAttempt::Type auth_attempt_type
,
109 const std::string
& user_id
,
110 const std::string
& key_secret
,
111 const std::string
& key_label
) {
112 DCHECK_EQ(EasyUnlockAuthAttempt::TYPE_SIGNIN
, auth_attempt_type
);
113 if (!success
|| key_secret
.empty()) {
114 LOG(ERROR
) << "Failed to sign-in using existing Smart lock key.";
119 user_context_
.SetKey(Key(key_secret
));
120 user_context_
.GetKey()->SetLabel(key_label
);
124 void BootstrapUserContextInitializer::CreateRandomKey() {
125 std::string random_initial_key
;
126 crypto::RandBytes(base::WriteInto(&random_initial_key
, kUserKeyByteSize
+ 1),
128 user_context_
.SetKey(Key(random_initial_key
));
129 random_key_used_
= true;
133 void BootstrapUserContextInitializer::Notify(bool success
) {
134 if (complete_callback_for_testing_
)
135 complete_callback_for_testing_
->Run(success
, user_context_
);
137 if (callback_
.is_null())
140 callback_
.Run(success
, user_context_
);
143 void BootstrapUserContextInitializer::OnGetTokensResponse(
144 const std::string
& refresh_token
,
145 const std::string
& access_token
,
146 int expires_in_seconds
) {
147 user_context_
.SetRefreshToken(refresh_token
);
149 gaia::OAuthClientInfo client_info
;
150 GaiaUrls
* gaia_urls
= GaiaUrls::GetInstance();
151 client_info
.client_id
= gaia_urls
->oauth2_chrome_client_id();
152 client_info
.client_secret
= gaia_urls
->oauth2_chrome_client_secret();
154 std::vector
<std::string
> scopes
;
155 scopes
.push_back(GaiaConstants::kGoogleUserInfoEmail
);
156 scopes
.push_back(GaiaConstants::kGoogleUserInfoProfile
);
158 token_fetcher_
->RefreshToken(client_info
, refresh_token
, scopes
,
159 kMaxGaiaRetries
, this);
162 void BootstrapUserContextInitializer::OnRefreshTokenResponse(
163 const std::string
& access_token
,
164 int expires_in_seconds
) {
165 token_fetcher_
->GetUserInfo(access_token
, kMaxGaiaRetries
, this);
168 void BootstrapUserContextInitializer::OnGetUserInfoResponse(
169 scoped_ptr
<base::DictionaryValue
> user_info
) {
172 if (!user_info
->GetString("email", &email
) ||
173 !user_info
->GetString("id", &gaia_id
)) {
174 LOG(ERROR
) << "Bad user info.";
179 user_context_
.SetUserID(email
);
180 user_context_
.SetGaiaID(gaia_id
);
181 StartCheckExistingKeys();
184 void BootstrapUserContextInitializer::OnOAuthError() {
185 LOG(ERROR
) << "Auth error.";
189 void BootstrapUserContextInitializer::OnNetworkError(int response_code
) {
190 LOG(ERROR
) << "Network error.";
194 void BootstrapUserContextInitializer::OnScreenlockStateChanged(
195 proximity_auth::ScreenlockState state
) {
196 // TODO(xiyuan): Add timeout and hook up with error UI after
197 // http://crbug.com/471067.
198 if (state
!= proximity_auth::ScreenlockState::AUTHENTICATED
)
201 EasyUnlockService
* service
=
202 EasyUnlockService::Get(ProfileHelper::GetSigninProfile());
203 service
->RemoveObserver(this);
205 service
->AttemptAuth(
206 user_context_
.GetUserID(),
207 base::Bind(&BootstrapUserContextInitializer::OnEasyUnlockAuthenticated
,
208 weak_ptr_factory_
.GetWeakPtr()));
211 } // namespace chromeos