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

#ifndef DEVICE_FIDO_WIN_AUTHENTICATOR_H_
#define DEVICE_FIDO_WIN_AUTHENTICATOR_H_

#include <Combaseapi.h>

#include <memory>
#include <optional>
#include <string>

#include "base/component_export.h"
#include "base/containers/span.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "device/fido/fido_authenticator.h"
#include "device/fido/public/fido_constants.h"

namespace device {

class WinWebAuthnApi;

// WinWebAuthnApiAuthenticator forwards WebAuthn requests to external
// authenticators via the native Windows WebAuthentication API
// (webauthn.dll).
//
// Callers must ensure that WinWebAuthnApi::IsAvailable() returns true
// before creating instances of this class.
class COMPONENT_EXPORT(DEVICE_FIDO) WinWebAuthnApiAuthenticator
    : public FidoAuthenticator {
 public:
  // Global observer for tests.
  class TestObserver {
   public:
    virtual void OnSignalUnknownCredential() {}
    virtual void OnSignalAllAcceptedCredentials() {}
  };

  // SetGlobalObserverForTesting sets the single |TestObserver| that is active
  // at a given time. Call be called with |nullptr| to unregister a
  // |TestObserver|. It is a fatal error to try and register a |TestObserver|
  // while one is still installed.
  static void SetGlobalObserverForTesting(TestObserver* observer);

  // This method is safe to call without checking WinWebAuthnApi::IsAvailable().
  // Returns false if |api| is nullptr.
  static void IsUserVerifyingPlatformAuthenticatorAvailable(
      WinWebAuthnApi* api,
      base::OnceCallback<void(bool is_available)>);

  // Get metadata for credentials in the platform authenticator. If `rp_id` has
  // a value, only credentials matching `rp_id` are returned. If such metadata
  // is not available then the callback will be invoked with an empty list.
  static void EnumeratePlatformCredentials(
      WinWebAuthnApi* api,
      std::optional<std::u16string> rp_id,
      base::OnceCallback<
          void(std::vector<device::DiscoverableCredentialMetadata>)> callback);

  // Deletes a credential from the platform authenticator. The callback will be
  // invoked with a boolean that indicates whether the deletion was successful.
  static void DeletePlatformCredential(WinWebAuthnApi* api,
                                       base::span<const uint8_t> credential_id,
                                       base::OnceCallback<void(bool)> callback);

  // If the Windows Hello version supports it, checks that |credential_id|
  // exists and is scoped to |relying_party_id|. If so, deletes the credential.
  // Otherwise, does nothing.
  static void SignalUnknownCredential(WinWebAuthnApi* api,
                                      const std::vector<uint8_t>& credential_id,
                                      const std::string& relying_party_id);

  // If the Windows Hello version supports it, removes all discoverable Windows
  // Hello credentials that are not present in |all_accepted_credential_ids|
  // associated to |relying_party_id| and |user_id|.
  // Credentials may not be discovered and therefore not removed in some cases,
  // like under RDP proxying.
  static void SignalAllAcceptedCredentials(
      WinWebAuthnApi* api,
      const std::string& relying_party_id,
      const std::vector<uint8_t>& user_id,
      const std::vector<std::vector<uint8_t>>& all_accepted_credential_ids);

  // Instantiates an authenticator that uses the default WinWebAuthnApi.
  //
  // Callers must ensure that WinWebAuthnApi::IsAvailable() returns true
  // before creating instances of this class.
  WinWebAuthnApiAuthenticator(HWND current_window, WinWebAuthnApi* win_api_);

  WinWebAuthnApiAuthenticator(const WinWebAuthnApiAuthenticator&) = delete;
  WinWebAuthnApiAuthenticator& operator=(const WinWebAuthnApiAuthenticator&) =
      delete;

  ~WinWebAuthnApiAuthenticator() override;

  // ShowsResidentCredentialNotice returns true if the Windows native UI will
  // show a privacy notice dialog before a MakeCredential request that might
  // create a resident key or that requests attestation.
  bool ShowsResidentCredentialNotice() const;

 private:
  // FidoAuthenticator:
  void InitializeAuthenticator(base::OnceClosure callback) override;
  void MakeCredential(CtapMakeCredentialRequest request,
                      MakeCredentialOptions options,
                      MakeCredentialCallback callback) override;
  void GetAssertion(CtapGetAssertionRequest request,
                    CtapGetAssertionOptions options,
                    GetAssertionCallback callback) override;
  void GetPlatformCredentialInfoForRequest(
      const CtapGetAssertionRequest& request,
      const CtapGetAssertionOptions& options,
      GetPlatformCredentialInfoForRequestCallback callback) override;
  void GetTouch(base::OnceClosure callback) override;
  void Cancel() override;
  AuthenticatorType GetType() const override;
  std::string GetId() const override;
  const AuthenticatorSupportedOptions& Options() const override;
  std::optional<FidoTransportProtocol> AuthenticatorTransport() const override;
  base::WeakPtr<FidoAuthenticator> GetWeakPtr() override;

  void MakeCredentialDone(
      MakeCredentialCallback callback,
      std::pair<MakeCredentialStatus,
                std::optional<AuthenticatorMakeCredentialResponse>> result);
  void GetAssertionDone(
      GetAssertionCallback callback,
      std::pair<GetAssertionStatus,
                std::optional<AuthenticatorGetAssertionResponse>> result);
  void GetPlatformCredentialInfoForRequestDone(
      std::vector<PublicKeyCredentialDescriptor> allow_list,
      GetPlatformCredentialInfoForRequestCallback callback,
      std::pair<bool, std::vector<DiscoverableCredentialMetadata>> result);

  // options_ is per-instance because the capabilities of `win_api_` can
  // change at run-time in tests.
  const AuthenticatorSupportedOptions options_;
  HWND current_window_;
  bool is_pending_ = false;
  bool waiting_for_cancellation_ = false;
  GUID cancellation_id_ = {};
  // The pointee of |win_api_| is assumed to be a singleton that outlives
  // this instance.
  raw_ptr<WinWebAuthnApi, DanglingUntriaged> win_api_;

  // Verifies callbacks from |win_api_| are posted back onto the originating
  // sequence.
  SEQUENCE_CHECKER(sequence_checker_);
  base::WeakPtrFactory<WinWebAuthnApiAuthenticator> weak_factory_{this};
};

}  // namespace device

#endif  // DEVICE_FIDO_WIN_AUTHENTICATOR_H_
