chromium/device/fido/mac/touch_id_context.h

// 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_MAC_TOUCH_ID_CONTEXT_H_
#define DEVICE_FIDO_MAC_TOUCH_ID_CONTEXT_H_

#import <LocalAuthentication/LocalAuthentication.h>
#include <Security/Security.h>

#include <string>

#include "base/apple/scoped_cftyperef.h"
#include "base/component_export.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "device/fido/mac/credential_store.h"

namespace device::fido::mac {

struct AuthenticatorConfig;

// TouchIdContext wraps a macOS user authentication prompt for signing with a
// secure enclave key. It is a essentially a simpler facade for the LAContext
// class from the macOS LocalAuthentication framework (c.f.
// https://developer.apple.com/documentation/localauthentication/lacontext?language=objc).
//
// Use |Create| to instantiate a new context. Multiple instances can be created
// at the same time. However, calling |PromptTouchId| on one instance will
// cancel any other pending evaluations with an error. Deleting an instance
// will invalidate any pending evaluation prompts (i.e. the dialog will
// disappear and evaluation will fail with an error).
class COMPONENT_EXPORT(DEVICE_FIDO) TouchIdContext {
 public:
  // The callback is invoked when the local user authentication prompt
  // completes. It receives a boolean indicating whether obtaining the
  // fingerprint was successful.
  using Callback = base::OnceCallback<void(bool)>;

  // Factory method for instantiating a TouchIdContext.
  static std::unique_ptr<TouchIdContext> Create();

  // Returns whether the device has a secure enclave and can authenticate the
  // local user, and whether the main executable carries a
  // keychain-access-groups entitlement that matches the one set in |config|.
  static void TouchIdAvailable(AuthenticatorConfig config,
                               base::OnceCallback<void(bool is_available)>);

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

  virtual ~TouchIdContext();

  // PromptTouchId displays a local user authentication prompt with the provided
  // reason string to the user. On completion or error, the provided callback is
  // invoked, unless the TouchIdContext instance has been destroyed in the
  // meantime (in which case nothing happens).
  virtual void PromptTouchId(const std::u16string& reason, Callback callback);

  // authentication_context returns the LAContext used for the local user
  // authentication prompt.
  LAContext* authentication_context() const { return context_; }

 protected:
  TouchIdContext();

 private:
  using CreateFuncPtr = decltype(&Create);
  static CreateFuncPtr g_create_;

  static bool TouchIdAvailableImpl(AuthenticatorConfig config);
  using TouchIdAvailableFuncPtr = decltype(&TouchIdAvailableImpl);
  static TouchIdAvailableFuncPtr g_touch_id_available_;

  static std::unique_ptr<TouchIdContext> CreateImpl();

  void RunCallback(bool success);

  LAContext* __strong context_;
  Callback callback_;
  base::WeakPtrFactory<TouchIdContext> weak_ptr_factory_{this};

  friend class ScopedTouchIdTestEnvironment;
};

}  // namespace device::fido::mac

#endif  // DEVICE_FIDO_MAC_TOUCH_ID_CONTEXT_H_