chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.h"

#include <utility>

#include "ash/constants/ash_features.h"
#include "base/functional/bind.h"
#include "chrome/browser/ash/login/quick_unlock/auth_token.h"
#include "chrome/browser/ash/login/quick_unlock/fingerprint_storage.h"
#include "chrome/browser/ash/login/quick_unlock/pin_storage_prefs.h"
#include "chrome/browser/ash/login/quick_unlock/quick_unlock_factory.h"
#include "chrome/browser/ash/login/quick_unlock/quick_unlock_storage.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/quick_unlock_private.h"
#include "chromeos/ash/components/cryptohome/constants.h"
#include "chromeos/ash/components/login/auth/auth_performer.h"
#include "chromeos/ash/components/login/auth/public/user_context.h"
#include "chromeos/ash/components/osauth/public/auth_session_storage.h"
#include "components/user_manager/known_user.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"

namespace extensions {

using AuthToken = ash::quick_unlock::AuthToken;
using TokenInfo = api::quick_unlock_private::TokenInfo;
using QuickUnlockStorage = ash::quick_unlock::QuickUnlockStorage;

QuickUnlockPrivateGetAuthTokenHelper::QuickUnlockPrivateGetAuthTokenHelper(
    Profile* profile,
    std::string password)
    : profile_(profile),
      password_(std::move(password)),
      auth_performer_(ash::UserDataAuthClient::Get()),
      auth_factor_editor_(ash::UserDataAuthClient::Get()) {}

QuickUnlockPrivateGetAuthTokenHelper::~QuickUnlockPrivateGetAuthTokenHelper() =
    default;

void QuickUnlockPrivateGetAuthTokenHelper::Run(Callback callback) {
  content::GetUIThreadTaskRunner({})->PostTask(
      FROM_HERE,
      base::BindOnce(&QuickUnlockPrivateGetAuthTokenHelper::RunOnUIThread,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void QuickUnlockPrivateGetAuthTokenHelper::RunOnUIThread(Callback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  const user_manager::User* const user =
      ash::ProfileHelper::Get()->GetUserByProfile(profile_);
  auto user_context = std::make_unique<ash::UserContext>(*user);

  const bool is_ephemeral =
      ash::ProfileHelper::IsEphemeralUserProfile(profile_);

  auto on_auth_started = base::BindOnce(
      &QuickUnlockPrivateGetAuthTokenHelper::OnAuthSessionStarted,
      weak_factory_.GetWeakPtr(), std::move(callback));

  auth_performer_.StartAuthSession(
      std::move(user_context), is_ephemeral /*ephemeral*/,
      ash::AuthSessionIntent::kDecrypt, std::move(on_auth_started));
}

void QuickUnlockPrivateGetAuthTokenHelper::OnAuthSessionStarted(
    Callback callback,
    bool user_exists,
    std::unique_ptr<ash::UserContext> user_context,
    std::optional<ash::AuthenticationError> error) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  DCHECK(user_exists);
  if (error.has_value()) {
    LOG(ERROR) << "Failed to start auth session, code "
               << error->get_cryptohome_error();
    std::move(callback).Run(std::nullopt, *error);
    return;
  }

  const auto* password_factor =
      user_context->GetAuthFactorsData().FindFactorByType(
          cryptohome::AuthFactorType::kPassword);
  if (!password_factor) {
    LOG(ERROR) << "Could not find password key";
    std::move(callback).Run(
        std::nullopt, ash::AuthenticationError(
                          cryptohome::ErrorWrapper::CreateFromErrorCodeOnly(
                              user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND)));
    return;
  }

  auto on_authenticated =
      base::BindOnce(&QuickUnlockPrivateGetAuthTokenHelper::OnAuthenticated,
                     weak_factory_.GetWeakPtr(), std::move(callback));

  auth_performer_.AuthenticateWithPassword(
      *(password_factor->ref().label()), std::move(password_),
      std::move(user_context), std::move(on_authenticated));
}

void QuickUnlockPrivateGetAuthTokenHelper::OnAuthenticated(
    Callback callback,
    std::unique_ptr<ash::UserContext> user_context,
    std::optional<ash::AuthenticationError> error) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  if (error.has_value()) {
    LOG(ERROR) << "Failed to authenticate with password, code "
               << error->get_cryptohome_error();
    std::move(callback).Run(std::nullopt, *error);
    return;
  }

  auto on_auth_factors_configuration = base::BindOnce(
      &QuickUnlockPrivateGetAuthTokenHelper::OnAuthFactorsConfiguration,
      weak_factory_.GetWeakPtr(), std::move(callback));

  auth_factor_editor_.GetAuthFactorsConfiguration(
      std::move(user_context), std::move(on_auth_factors_configuration));
}

void QuickUnlockPrivateGetAuthTokenHelper::OnAuthFactorsConfiguration(
    Callback callback,
    std::unique_ptr<ash::UserContext> user_context,
    std::optional<ash::AuthenticationError> error) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  if (error.has_value()) {
    LOG(ERROR) << "Failed to load auth factors configuration, code "
               << error->get_cryptohome_error();
    std::move(callback).Run(std::nullopt, *error);
    return;
  }

  // The user context stored in quick_unlock storage must have a device ID, so
  // we retrieve and set it here.
  user_manager::KnownUser known_user{g_browser_process->local_state()};
  std::string device_id = known_user.GetDeviceId(user_context->GetAccountId());
  LOG_IF(WARNING, device_id.empty())
      << "Missing DeviceID for auth factor edits";
  user_context->SetDeviceId(std::move(device_id));

  QuickUnlockStorage* quick_unlock_storage =
      ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile_);
  quick_unlock_storage->MarkStrongAuth();
  // The user has successfully authenticated, so we should reset pin/fingerprint
  // attempt counts.
  quick_unlock_storage->pin_storage_prefs()->ResetUnlockAttemptCount();
  quick_unlock_storage->fingerprint_storage()->ResetUnlockAttemptCount();

  TokenInfo token_info;
  token_info.token =
      ash::AuthSessionStorage::Get()->Store(std::move(user_context));
  token_info.lifetime_seconds =
      cryptohome::kAuthsessionInitialLifetime.InSeconds();

  std::move(callback).Run(std::move(token_info), std::nullopt);
}

}  // namespace extensions