// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/in_session_auth/webauthn_dialog_controller_impl.h" #include "ash/in_session_auth/auth_dialog_contents_view.h" #include "ash/in_session_auth/webauthn_request_registrar_impl.h" #include "ash/public/cpp/in_session_auth_dialog_client.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "base/bind.h" #include "base/callback.h" #include "base/strings/string_util.h" #include "components/user_manager/known_user.h" #include "ui/aura/window.h" #include "ui/views/widget/widget.h" namespace ash { WebAuthNDialogControllerImpl::WebAuthNDialogControllerImpl() : webauthn_request_registrar_( std::make_unique()) {} WebAuthNDialogControllerImpl::~WebAuthNDialogControllerImpl() = default; void WebAuthNDialogControllerImpl::SetClient( InSessionAuthDialogClient* client) { client_ = client; } void WebAuthNDialogControllerImpl::ShowAuthenticationDialog( aura::Window* source_window, const std::string& origin_name, FinishCallback finish_callback) { DCHECK(client_); // Concurrent requests are not supported. DCHECK(!dialog_); source_window_tracker_.Add(source_window); finish_callback_ = std::move(finish_callback); AccountId account_id = Shell::Get()->session_controller()->GetActiveAccountId(); // GAIA password option is not offered. uint32_t auth_methods = AuthDialogContentsView::kAuthPassword; if (client_->IsFingerprintAuthAvailable(account_id)) { client_->StartFingerprintAuthSession( account_id, base::BindOnce( &WebAuthNDialogControllerImpl::OnStartFingerprintAuthSession, weak_factory_.GetWeakPtr(), account_id, auth_methods, source_window, origin_name)); // OnStartFingerprintAuthSession checks PIN availability. return; } client_->CheckPinAuthAvailability( account_id, base::BindOnce(&WebAuthNDialogControllerImpl::OnPinCanAuthenticate, weak_factory_.GetWeakPtr(), auth_methods, source_window, origin_name)); } void WebAuthNDialogControllerImpl::OnStartFingerprintAuthSession( AccountId account_id, uint32_t auth_methods, aura::Window* source_window, const std::string& origin_name, bool success) { if (success) auth_methods |= AuthDialogContentsView::kAuthFingerprint; client_->CheckPinAuthAvailability( account_id, base::BindOnce(&WebAuthNDialogControllerImpl::OnPinCanAuthenticate, weak_factory_.GetWeakPtr(), auth_methods, source_window, origin_name)); } void WebAuthNDialogControllerImpl::OnPinCanAuthenticate( uint32_t auth_methods, aura::Window* source_window, const std::string& origin_name, bool pin_auth_available) { if (pin_auth_available) auth_methods |= AuthDialogContentsView::kAuthPin; if (auth_methods == 0) { // If neither fingerprint nor PIN is available, we shouldn't receive the // request. LOG(ERROR) << "Neither fingerprint nor PIN is available."; Cancel(); return; } if (!source_window_tracker_.Contains(source_window)) { LOG(ERROR) << "Source window is no longer available."; Cancel(); return; } Shell* shell = Shell::Get(); AccountId account_id = shell->session_controller()->GetActiveAccountId(); const UserSession* session = shell->session_controller()->GetUserSessionByAccountId(account_id); DCHECK(session); UserAvatar avatar = session->user_info.avatar; // TODO(b/156258540): move UserSelectionScreen::BuildAshUserAvatarForUser to // somewhere that UserToUserSession could call, to support animated avatars. AuthDialogContentsView::AuthMethodsMetadata auth_metadata; auth_metadata.autosubmit_pin_length = user_manager::KnownUser(shell->local_state()) .GetUserPinLength(account_id); source_window_tracker_.Remove(source_window); dialog_ = std::make_unique( auth_methods, source_window, origin_name, auth_metadata, avatar); } void WebAuthNDialogControllerImpl::DestroyAuthenticationDialog() { DCHECK(client_); if (!dialog_) return; if (dialog_->GetAuthMethods() & AuthDialogContentsView::kAuthFingerprint) client_->EndFingerprintAuthSession(); dialog_.reset(); source_window_tracker_.RemoveAll(); } void WebAuthNDialogControllerImpl::AuthenticateUserWithPasswordOrPin( const std::string& password, bool authenticated_by_pin, OnAuthenticateCallback callback) { DCHECK(client_); // TODO(b/156258540): Check that PIN is enabled / set up for this user. if (authenticated_by_pin && !base::ContainsOnlyChars(password, "0123456789")) { OnAuthenticateComplete(std::move(callback), false); return; } client_->AuthenticateUserWithPasswordOrPin( password, authenticated_by_pin, base::BindOnce(&WebAuthNDialogControllerImpl::OnAuthenticateComplete, weak_factory_.GetWeakPtr(), std::move(callback))); } void WebAuthNDialogControllerImpl::AuthenticateUserWithFingerprint( base::OnceCallback views_callback) { DCHECK(client_); client_->AuthenticateUserWithFingerprint( base::BindOnce(&WebAuthNDialogControllerImpl::OnFingerprintAuthComplete, weak_factory_.GetWeakPtr(), std::move(views_callback))); } void WebAuthNDialogControllerImpl::OnAuthenticateComplete( OnAuthenticateCallback callback, bool success) { if (success) { std::move(callback).Run(/*success=*/true, /*can_use_pin=*/true); OnAuthSuccess(); return; } // PIN might be locked out after an unsuccessful authentication, check if it's // still available so the UI can be updated. AccountId account_id = Shell::Get()->session_controller()->GetActiveAccountId(); client_->CheckPinAuthAvailability( account_id, base::BindOnce(std::move(callback), /*success=*/false)); } void WebAuthNDialogControllerImpl::OnFingerprintAuthComplete( base::OnceCallback views_callback, bool success, FingerprintState fingerprint_state) { // If success is false and retry is allowed, the view will start another // fingerprint check. std::move(views_callback).Run(success, fingerprint_state); if (success) OnAuthSuccess(); } void WebAuthNDialogControllerImpl::OnAuthSuccess() { DestroyAuthenticationDialog(); if (finish_callback_) std::move(finish_callback_).Run(true); } void WebAuthNDialogControllerImpl::Cancel() { DestroyAuthenticationDialog(); if (finish_callback_) std::move(finish_callback_).Run(false); } void WebAuthNDialogControllerImpl::OpenInSessionAuthHelpPage() { DCHECK(client_); client_->OpenInSessionAuthHelpPage(); } void WebAuthNDialogControllerImpl::CheckAvailability( FinishCallback on_availability_checked) const { // Assumes the requests are for the active user (no teleported window). AccountId account_id = Shell::Get()->session_controller()->GetActiveAccountId(); if (client_->IsFingerprintAuthAvailable(account_id)) { std::move(on_availability_checked).Run(true); return; } client_->CheckPinAuthAvailability(account_id, std::move(on_availability_checked)); } } // namespace ash