blob: 674e5af6984f000bce0b3df674b5cf0c95d4a2e6 [file]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/credential_management/android/third_party_credential_manager_impl.h"
#include "base/android/callback_android.h"
#include "base/check_deref.h"
#include "base/functional/bind.h"
#include "base/notimplemented.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/visibility.h"
namespace credential_management {
ThirdPartyCredentialManagerImpl::ThirdPartyCredentialManagerImpl(
content::WebContents* web_contents)
: bridge_(std::make_unique<ThirdPartyCredentialManagerBridge>()),
web_contents_(CHECK_DEREF(web_contents)) {}
ThirdPartyCredentialManagerImpl::ThirdPartyCredentialManagerImpl(
base::PassKey<class ThirdPartyCredentialManagerImplTest>,
content::WebContents* web_contents,
std::unique_ptr<CredentialManagerBridge> bridge)
: bridge_(std::move(bridge)), web_contents_(CHECK_DEREF(web_contents)) {}
ThirdPartyCredentialManagerImpl::~ThirdPartyCredentialManagerImpl() {
if (pending_request_id_ != 0) {
bridge_->Cancel();
}
}
void ThirdPartyCredentialManagerImpl::Store(
const password_manager::CredentialInfo& credential,
StoreCallback callback) {
if (credential.type ==
password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY ||
!credential.password.has_value() || credential.password.value().empty()) {
std::move(callback).Run();
return;
}
if (IsOffTheRecord() || !IsVisibleAndFocused()) {
std::move(callback).Run();
return;
}
if (pending_request_id_ != 0) {
std::move(callback).Run();
return;
}
uint32_t request_id = ++next_request_id_;
pending_request_id_ = request_id;
std::u16string username = credential.id.value_or(u"");
std::u16string password = credential.password.value();
bridge_->Store(*web_contents_, username, password,
web_contents_->GetPrimaryMainFrame()
->GetLastCommittedOrigin()
.Serialize(),
std::move(callback).Then(base::BindOnce(
&ThirdPartyCredentialManagerImpl::OnRequestComplete,
weak_ptr_factory_.GetWeakPtr(), request_id)));
}
void ThirdPartyCredentialManagerImpl::PreventSilentAccess(
PreventSilentAccessCallback callback) {
// Send acknowledge response back.
// We're currently preventing silent access for every get request by default
// in 3rd party mode so there is nothing more to do here for now.
std::move(callback).Run();
}
// This method decides if credential picker should be shown.
// Credential mediation can be silent, optional, conditional or
// required.
// Silent mediation should not show a credential picker even if there
// are multiple credentials available and return null.
// Silent mediation can't be implemented here, because the Android API
// doesn't support it. We'd have to know the amount of available credentials
// already before calling get.
// By default, the GetCredentialRequest will have optional
// mediation: if there's more than one matching credential, the system
// will show the credential picker UI to the user.
// Required mediation will show the credential picker, no matter the amount
// of choices.
// Conditional mediation allows the user to pick a credential from the
// picker or avoid selecting a credential without any user-visible error
// condition. That type of mediotion is also not supported in the Android
// API.
bool ShouldAllowAutoSelect(
password_manager::CredentialMediationRequirement mediation) {
switch (mediation) {
case password_manager::CredentialMediationRequirement::kOptional:
return true;
case password_manager::CredentialMediationRequirement::kRequired:
return false;
case password_manager::CredentialMediationRequirement::kSilent:
case password_manager::CredentialMediationRequirement::kConditional:
NOTIMPLEMENTED();
}
return false;
}
net::CertStatus ThirdPartyCredentialManagerImpl::GetMainFrameCertStatus()
const {
content::NavigationEntry* entry =
web_contents_->GetController().GetLastCommittedEntry();
if (!entry) {
return 0;
}
return entry->GetSSL().cert_status;
}
void ThirdPartyCredentialManagerImpl::Get(
password_manager::CredentialMediationRequirement mediation,
bool include_passwords,
const std::vector<GURL>& federations,
GetCallback callback) {
if (mediation == password_manager::CredentialMediationRequirement::kSilent ||
mediation ==
password_manager::CredentialMediationRequirement::kConditional) {
std::move(callback).Run(password_manager::CredentialManagerError::SUCCESS,
password_manager::CredentialInfo());
return;
}
if (net::IsCertStatusError(GetMainFrameCertStatus()) || IsOffTheRecord() ||
!IsVisibleAndFocused()) {
std::move(callback).Run(password_manager::CredentialManagerError::SUCCESS,
password_manager::CredentialInfo());
return;
}
if (pending_request_id_ != 0) {
std::move(callback).Run(
password_manager::CredentialManagerError::PENDING_REQUEST,
std::nullopt);
return;
}
uint32_t request_id = ++next_request_id_;
pending_request_id_ = request_id;
bridge_->Get(*web_contents_, ShouldAllowAutoSelect(mediation),
include_passwords, federations,
web_contents_->GetPrimaryMainFrame()
->GetLastCommittedOrigin()
.Serialize(),
std::move(callback).Then(base::BindOnce(
&ThirdPartyCredentialManagerImpl::OnRequestComplete,
weak_ptr_factory_.GetWeakPtr(), request_id)));
}
void ThirdPartyCredentialManagerImpl::ResetAfterDisconnecting() {
if (pending_request_id_ != 0) {
pending_request_id_ = 0;
bridge_->Cancel();
}
}
void ThirdPartyCredentialManagerImpl::OnRequestComplete(uint32_t request_id) {
if (request_id == pending_request_id_) {
pending_request_id_ = 0;
}
}
bool ThirdPartyCredentialManagerImpl::IsVisibleAndFocused() const {
if (web_contents_->GetVisibility() != content::Visibility::VISIBLE) {
return false;
}
content::RenderWidgetHostView* view =
web_contents_->GetTopLevelRenderWidgetHostView();
return view && view->HasFocus();
}
bool ThirdPartyCredentialManagerImpl::IsOffTheRecord() const {
return web_contents_->GetBrowserContext()->IsOffTheRecord();
}
} // namespace credential_management