[M130 Migration][Passkey] Added callback & EWK api to communicate linked data 12/324712/6
authorfeifei <feifei08.liu@samsung.com>
Fri, 23 May 2025 10:48:34 +0000 (18:48 +0800)
committerBot Blink <blinkbot@samsung.com>
Wed, 4 Jun 2025 07:38:19 +0000 (07:38 +0000)
1. Add Smart callback "webauth,update,linked,data" to inform the ewk layer
   whenever the Update Linked Data callback is trigged
2. Add Smart callback "webauth,display,dialog" to inform the ewk layer
   whenever to show dialog for user to choose between QR code and linked devices.
3. Add Smart callback "webauth,update,result" to inform the ewk layer
   with MC/GA result, so they can do different processing according to the results
4. Add a switch "enable-linked-device" in cmd line for browser to enable
   linked device scenerio without affecting QR code scenerio.
5. Added ewk api "ewk_view_wauthn_method_set" to communicate any linked data
   from ewk layer to AuthenticatorCommonTizen.
6. Add necessary logs to trace passkey function in engine.

References: https://archive.tizen.org/gerrit/c/318765/
            https://archive.tizen.org/gerrit/c/319522/
            https://review.tizen.org/gerrit/c/319636
            https://review.tizen.org/gerrit/c/320451
            https://review.tizen.org/gerrit/c/320927

Change-Id: I03d3814a1430b0cebc3253eb6f399c470418cdc9
Signed-off-by: feifei <feifei08.liu@samsung.com>
22 files changed:
content/browser/renderer_host/render_frame_host_impl.cc
content/browser/renderer_host/render_frame_host_impl.h
content/browser/renderer_host/render_widget_host_impl.cc
content/browser/renderer_host/render_widget_host_impl.h
content/browser/renderer_host/render_widget_host_view_aura.cc
content/browser/renderer_host/render_widget_host_view_aura.h
content/browser/renderer_host/render_widget_host_view_base.h
content/public/browser/web_contents_delegate.h
tizen_src/chromium_impl/content/browser/renderer_host/rwhv_aura_common_helper_efl.cc
tizen_src/chromium_impl/content/browser/renderer_host/rwhv_aura_common_helper_efl.h
tizen_src/chromium_impl/content/browser/webauthn/authenticator_common_tizen.cc
tizen_src/chromium_impl/content/browser/webauthn/authenticator_common_tizen.h
tizen_src/ewk/efl_integration/common/content_switches_efl.cc
tizen_src/ewk/efl_integration/common/content_switches_efl.h
tizen_src/ewk/efl_integration/eweb_view.cc
tizen_src/ewk/efl_integration/eweb_view.h
tizen_src/ewk/efl_integration/eweb_view_callbacks.h
tizen_src/ewk/efl_integration/public/ewk_view.cc
tizen_src/ewk/efl_integration/web_contents_delegate_efl.cc
tizen_src/ewk/efl_integration/web_contents_delegate_efl.h
tizen_src/ewk/ubrowser/window.cc
tizen_src/ewk/ubrowser/window.h

index 9fcc3896be487f14e4be8a4514e9023bd3e59ee2..db222dacf09837b1dcabfa4044e01fb347b870cb 100644 (file)
@@ -17573,6 +17573,26 @@ void RenderFrameHostImpl::set_authenticator(
 AuthenticatorCommonTizen* RenderFrameHostImpl::get_authenticator() {
   return authenticator_.get();
 }
+
+void RenderFrameHostImpl::DisplayWauthnDialog() {
+  LOG(INFO) << "[Passkey] DisplayWauthnDialog";
+  if (GetLocalRenderWidgetHost()) {
+    GetLocalRenderWidgetHost()->DisplayWauthnDialog();
+  }
+}
+void RenderFrameHostImpl::UpdateLinkedData(const void* linked_data) {
+  LOG(INFO) << "[Passkey] UpdateLinkedData, linked_data : " << linked_data;
+  if (GetLocalRenderWidgetHost()) {
+    GetLocalRenderWidgetHost()->UpdateLinkedData(linked_data);
+  }
+}
+
+void RenderFrameHostImpl::UpdateWAuthnResult(int result) {
+  LOG(INFO) << "[Passkey] UpdateWAuthnResult, result : " << result;
+  if (GetLocalRenderWidgetHost()) {
+    GetLocalRenderWidgetHost()->UpdateWAuthnResult(result);
+  }
+}
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
 bool RenderFrameHostImpl::ShouldReuseCompositing(
index 96e341ccaba7fc2ee7a6c0945efc5bc5733d622e..a6bf8575b2e58646cf9dd776f29e287f537fc9ce 100644 (file)
@@ -580,6 +580,9 @@ class CONTENT_EXPORT RenderFrameHostImpl
   void CloseQRCode();
   void set_authenticator(base::WeakPtr<AuthenticatorCommonTizen> authenticator);
   AuthenticatorCommonTizen* get_authenticator();
+  void DisplayWauthnDialog();
+  void UpdateLinkedData(const void* linked_data);
+  void UpdateWAuthnResult(int result);
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
   // Additional non-override const version of GetMainFrame.
index 624deaac3fc9a9ff5203f75e201d285201f99530..8c3c64d5ade542befb2892c579542e5b394c93cd 100644 (file)
@@ -646,6 +646,30 @@ void RenderWidgetHostImpl::CloseQRCode() {
   }
   view_->CloseQRCode();
 }
+void RenderWidgetHostImpl::DisplayWauthnDialog() {
+  LOG(INFO) << "[Passkey] DisplayWauthnDialog";
+  if (!view_) {
+    LOG(ERROR) << "[Passkey] view_ is null";
+    return;
+  }
+  view_->DisplayWauthnDialog();
+}
+void RenderWidgetHostImpl::UpdateLinkedData(const void* linked_data) {
+  LOG(INFO) << "[Passkey] UpdateLinkedData, linked_data : " << linked_data;
+  if (!view_) {
+    LOG(ERROR) << "[Passkey] view_ is null";
+    return;
+  }
+  view_->UpdateLinkedData(linked_data);
+}
+void RenderWidgetHostImpl::UpdateWAuthnResult(int result) {
+  LOG(INFO) << "[Passkey] UpdateWAuthnResult, result : " << result;
+  if (!view_) {
+    LOG(ERROR) << "[Passkey] view_ is null";
+    return;
+  }
+  view_->UpdateWAuthnResult(result);
+}
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
 // static
index 05644cdb68a485badbc0aa92c6b826443ae6ac88..de39be6ae02c150d73cbe9acf4431b74df714068 100644 (file)
@@ -438,6 +438,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl
 #if defined(TIZEN_PASSKEY_SUPPORT)
   void DisplayQRCode(std::string contents);
   void CloseQRCode();
+  void DisplayWauthnDialog();
+  void UpdateLinkedData(const void* linked_data);
+  void UpdateWAuthnResult(int result);
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
   RenderWidgetHostDelegate* delegate() const { return delegate_; }
index 09744ad5889a391ed243875a799c0f9c48c11142..4416420fa122ebc1ad52ed74ee4c2d0e91b622ff 100644 (file)
@@ -943,6 +943,24 @@ void RenderWidgetHostViewAura::CloseQRCode() {
     efl_helper_->CloseQRCode();
   }
 }
+void RenderWidgetHostViewAura::DisplayWauthnDialog() {
+  LOG(INFO) << "[Passkey] DisplayWauthnDialog";
+  if (efl_helper_) {
+    efl_helper_->DisplayWauthnDialog();
+  }
+}
+void RenderWidgetHostViewAura::UpdateLinkedData(const void* linked_data) {
+  LOG(INFO) << "[Passkey] UpdateLinkedData, linked_data : " << linked_data;
+  if (efl_helper_) {
+    efl_helper_->UpdateLinkedData(linked_data);
+  }
+}
+void RenderWidgetHostViewAura::UpdateWAuthnResult(int result) {
+  LOG(INFO) << "[Passkey] UpdateWAuthnResult, result : " << result;
+  if (efl_helper_) {
+    efl_helper_->UpdateWAuthnResult(result);
+  }
+}
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
 #if BUILDFLAG(IS_WIN)
index 07be3b3e29d7f73738a4bd56e1e28367685ce03d..afc7ba481bbf2c18003531c884a824f1989f30a7 100644 (file)
@@ -540,6 +540,9 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
 #if defined(TIZEN_PASSKEY_SUPPORT)
   void DisplayQRCode(std::string contents) override;
   void CloseQRCode() override;
+  void DisplayWauthnDialog() override;
+  void UpdateLinkedData(const void* linked_data) override;
+  void UpdateWAuthnResult(int result) override;
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
   // May be overridden in tests.
index 4a7fe9a0741f8f4a8477e817c8519deac36fe837..a883506a3b07c4f316f27e0820f0e50012adfa90 100644 (file)
@@ -535,6 +535,9 @@ class CONTENT_EXPORT RenderWidgetHostViewBase
 #if defined(TIZEN_PASSKEY_SUPPORT)
   virtual void DisplayQRCode(std::string contents) {}
   virtual void CloseQRCode() {}
+  virtual void DisplayWauthnDialog() {}
+  virtual void UpdateLinkedData(const void* linked_data) {}
+  virtual void UpdateWAuthnResult(int result) {}
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
   void SetViewTransitionResources(
index 8da2cb8f9785cb6afdcca7ac8853a0a8469a4e0d..d4bbb35e8832632322e27560ce03e959be28693c 100644 (file)
@@ -424,6 +424,10 @@ class CONTENT_EXPORT WebContentsDelegate {
 #if defined(TIZEN_PASSKEY_SUPPORT)
   virtual void DisplayQRCode(std::string contents) {}
   virtual void CloseQRCode() {}
+  virtual void DisplayWauthnDialog() {}
+  virtual void UpdateLinkedData(const void* linked_data) {}
+  virtual void OnSetAuthenticationMethod() {}
+  virtual void UpdateWAuthnResult(int result) {}
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
   // Returns a pointer to a service to manage JavaScript dialogs. May return
index dffcc0394a38e7b2b30cc1ee7a424df02465dc53..ea90919ae08423b9786c801ade873762b54aac75 100644 (file)
@@ -856,6 +856,21 @@ void RWHVAuraCommonHelperEfl::CloseQRCode() {
   web_contents_->GetDelegate()->CloseQRCode();
 }
 
+void RWHVAuraCommonHelperEfl::DisplayWauthnDialog() {
+  LOG(INFO) << "[Passkey] DisplayWauthnDialog";
+  web_contents_->GetDelegate()->DisplayWauthnDialog();
+}
+
+void RWHVAuraCommonHelperEfl::UpdateLinkedData(const void* linked_data) {
+  LOG(INFO) << "[Passkey] UpdateLinkedData, linked_data : " << linked_data;
+  web_contents_->GetDelegate()->UpdateLinkedData(linked_data);
+}
+
+void RWHVAuraCommonHelperEfl::UpdateWAuthnResult(int result) {
+  LOG(INFO) << "[Passkey] UpdateWAuthnResult, result : " << result;
+  web_contents_->GetDelegate()->UpdateWAuthnResult(result);
+}
+
 void RWHVAuraCommonHelperEfl::CancelAuthentication() {
   // Get AuthenticatorCommonTizen reference to invoke Cancel()
   if (rwhva()->GetFocusedFrame() &&
@@ -863,6 +878,21 @@ void RWHVAuraCommonHelperEfl::CancelAuthentication() {
     rwhva()->GetFocusedFrame()->get_authenticator()->Cancel();
   }
 }
+
+void RWHVAuraCommonHelperEfl::SetWauthnLinkedDevice(void* linkDevice) {
+  if (rwhva()->GetFocusedFrame() &&
+      rwhva()->GetFocusedFrame()->get_authenticator()) {
+    rwhva()->GetFocusedFrame()->get_authenticator()->SetWauthnLinkedDevice(
+        linkDevice);
+  }
+}
+
+void RWHVAuraCommonHelperEfl::ClearWauthnLinkedDevice() {
+  if (rwhva()->GetFocusedFrame() &&
+      rwhva()->GetFocusedFrame()->get_authenticator()) {
+    rwhva()->GetFocusedFrame()->get_authenticator()->ClearWauthnLinkedDevice();
+  }
+}
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
 void RWHVAuraCommonHelperEfl::BackgroundColorReceived(
index cce5cd7c4945d1221bcae6f3baefad79e4b3a80f..29ffa3e69837a865f95f5a3c11923aa341e80e94 100644 (file)
@@ -219,7 +219,12 @@ class CONTENT_EXPORT RWHVAuraCommonHelperEfl {
 #if defined(TIZEN_PASSKEY_SUPPORT)
   void DisplayQRCode(std::string contents);
   void CloseQRCode();
+  void DisplayWauthnDialog();
+  void UpdateLinkedData(const void* linked_data);
+  void UpdateWAuthnResult(int result);
   void CancelAuthentication();
+  void SetWauthnLinkedDevice(void* linkDevice);
+  void ClearWauthnLinkedDevice();
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
  protected:
index dc4edffd9db3528b412e4c26222a8384ecddbc75..abc49f4d5c77a7b79325832f02715b109e3ef9b7 100644 (file)
@@ -1,6 +1,7 @@
 #include "content/browser/webauthn/authenticator_common_tizen.h"
 
 #include <cstring>
+#include <iomanip>
 #include <iostream>
 #include <mutex>
 #include <optional>
@@ -47,7 +48,9 @@
 #include "device/fido/public_key_credential_descriptor.h"
 #include "device/fido/public_key_credential_params.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/boringssl/src/include/openssl/sha.h"
+#include "tizen_src/ewk/efl_integration/common/content_switches_efl.h"
 
 namespace content {
 
@@ -83,6 +86,7 @@ using ReportCallback = blink::mojom::Authenticator::ReportCallback;
 
 namespace {
 
+bool supportLinkedDevice = false;
 WebAuthenticationDelegate* GetWebAuthenticationDelegate() {
   return GetContentClient()->browser()->GetWebAuthenticationDelegate();
 }
@@ -532,7 +536,7 @@ std::string WauthnErrorToString(wauthn_error_e result) {
 
 void AuthenticatorCommonTizen::DisplayQRCallback(const char* qr_contents,
                                                  void* data) {
-  LOG(INFO) << __FUNCTION__;
+  LOG(INFO) << "[Passkey] DisplayQRCallback invoked";
 
   AuthenticatorCommonTizen* thiz =
       reinterpret_cast<AuthenticatorCommonTizen*>(data);
@@ -540,6 +544,11 @@ void AuthenticatorCommonTizen::DisplayQRCallback(const char* qr_contents,
       static_cast<RenderFrameHostImpl*>(thiz->GetRenderFrameHost());
   std::string encoded(qr_contents);
 
+  if (thiz->mc_options_.linked_device || thiz->ga_options_.linked_device) {
+    LOG(INFO) << "[Passkey] linked_device is not null, do not display QR code";
+    return;
+  }
+
   if (encoded.empty()) {
     LOG(ERROR) << __FUNCTION__ << " terminated with empty QR URL";
     thiz->CompleteMakeCredentialRequest(
@@ -557,20 +566,48 @@ void AuthenticatorCommonTizen::MakeCredentialResponseCallback(
     const wauthn_pubkey_credential_attestation_s* pubkey_cred,
     wauthn_error_e result,
     void* data) {
-  LOG(INFO) << __FUNCTION__;
-
-  if (result == WAUTHN_ERROR_CANCELED) {
-    return;
-  }
+  LOG(INFO) << "[Passkey] MCCallback invoked, result : " << (int)result;
 
   AuthenticatorCommonTizen* thiz =
       reinterpret_cast<AuthenticatorCommonTizen*>(data);
   RenderFrameHostImpl* const render_frame_host_impl =
       static_cast<RenderFrameHostImpl*>(thiz->GetRenderFrameHost());
-
   content::GetUIThreadTaskRunner({})->PostTask(
-      FROM_HERE, base::BindOnce(&RenderFrameHostImpl::CloseQRCode,
-                                base::Unretained(render_frame_host_impl)));
+      FROM_HERE,
+      base::BindOnce(&RenderFrameHostImpl::UpdateWAuthnResult,
+                     base::Unretained(render_frame_host_impl), result));
+
+  if (result == WAUTHN_ERROR_CANCELED) {
+    LOG(INFO) << "[Passkey] WAUTHN_ERROR_CANCELED, MC failed.";
+    return;
+  }
+  if (result != WAUTHN_ERROR_NONE) {
+    LOG(INFO) << "[Passkey] result is not WAUTHN_ERROR_NONE, MC failed.";
+    return;
+  }
+
+  if (thiz->mc_options_.linked_device) {
+    LOG(INFO) << "[Passkey] Do not need to close QR code";
+  } else {
+    LOG(INFO) << "[Passkey] Need to close QR code";
+    RenderFrameHostImpl* const render_frame_host_impl =
+        static_cast<RenderFrameHostImpl*>(thiz->GetRenderFrameHost());
+
+    content::GetUIThreadTaskRunner({})->PostTask(
+        FROM_HERE, base::BindOnce(&RenderFrameHostImpl::CloseQRCode,
+                                  base::Unretained(render_frame_host_impl)));
+  }
+
+  if (!pubkey_cred || pubkey_cred->linked_device == nullptr) {
+    LOG(INFO) << "[Passkey] response without linked_device";
+  } else {
+    LOG(INFO) << "[Passkey] response with linked_device, supportLinkedDevice : "
+              << (int)supportLinkedDevice;
+    if (supportLinkedDevice) {
+      LOG(INFO) << "[Passkey] response with linked_device, update info";
+      thiz->UpdateLinkedData(pubkey_cred->linked_device);
+    }
+  }
 
   if (!pubkey_cred || result != WAUTHN_ERROR_NONE) {
     LOG(ERROR) << __FUNCTION__ << " terminated with error status: "
@@ -592,20 +629,47 @@ void AuthenticatorCommonTizen::GetAssertionResponseCallback(
     const wauthn_pubkey_credential_assertion_s* pubkey_cred,
     wauthn_error_e result,
     void* data) {
-  LOG(INFO) << __FUNCTION__;
+  LOG(INFO) << "[Passkey] GACallback invoked, result : " << (int)result;
+  AuthenticatorCommonTizen* thiz =
+      reinterpret_cast<AuthenticatorCommonTizen*>(data);
+  RenderFrameHostImpl* const render_frame_host_impl =
+      static_cast<RenderFrameHostImpl*>(thiz->GetRenderFrameHost());
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(&RenderFrameHostImpl::UpdateWAuthnResult,
+                     base::Unretained(render_frame_host_impl), result));
 
   if (result == WAUTHN_ERROR_CANCELED) {
+    LOG(INFO) << "[Passkey] WAUTHN_ERROR_CANCELED, GA failed.";
+    return;
+  }
+  if (result != WAUTHN_ERROR_NONE) {
+    LOG(INFO) << "[Passkey] result is not WAUTHN_ERROR_NONE, GA failed.";
     return;
   }
 
-  AuthenticatorCommonTizen* thiz =
-      reinterpret_cast<AuthenticatorCommonTizen*>(data);
-  RenderFrameHostImpl* const render_frame_host_impl =
-      static_cast<RenderFrameHostImpl*>(thiz->GetRenderFrameHost());
+  if (thiz->ga_options_.linked_device) {
+    LOG(INFO) << "[Passkey] Do not need to close QR code";
+  } else {
+    LOG(INFO) << "[Passkey] Need to close QR code";
+    RenderFrameHostImpl* const render_frame_host_impl =
+        static_cast<RenderFrameHostImpl*>(thiz->GetRenderFrameHost());
 
-  content::GetUIThreadTaskRunner({})->PostTask(
-      FROM_HERE, base::BindOnce(&RenderFrameHostImpl::CloseQRCode,
-                                base::Unretained(render_frame_host_impl)));
+    content::GetUIThreadTaskRunner({})->PostTask(
+        FROM_HERE, base::BindOnce(&RenderFrameHostImpl::CloseQRCode,
+                                  base::Unretained(render_frame_host_impl)));
+  }
+
+  if (!pubkey_cred || pubkey_cred->linked_device == nullptr) {
+    LOG(INFO) << "[Passkey] response without linked_device";
+  } else {
+    LOG(INFO) << "[Passkey] response with linked_device, supportLinkedDevice : "
+              << (int)supportLinkedDevice;
+    if (supportLinkedDevice) {
+      LOG(INFO) << "[Passkey] response with linked_device, update info";
+      thiz->UpdateLinkedData(pubkey_cred->linked_device);
+    }
+  }
 
   if (!pubkey_cred || result != WAUTHN_ERROR_NONE) {
     LOG(ERROR) << __FUNCTION__ << " terminated with error status: "
@@ -623,14 +687,46 @@ void AuthenticatorCommonTizen::MakeCredentialUpdateLinkedDataCallback(
     const wauthn_hybrid_linked_data_s* linked_data,
     wauthn_error_e result,
     void* data) {
-  NOTIMPLEMENTED();
+  LOG(INFO) << "[Passkey] MCUpdateLinkedDataCallback invoked, result : "
+            << (int)result;
+  /* When WAUTHN_ERROR_NONE_AND_WAIT arrived, should keep waiting.
+     Also not update linked device info under other error circumstances*/
+  if (result != WAUTHN_ERROR_NONE) {
+    return;
+  }
+  LOG(INFO) << "[Passkey] update linked info";
+  if (supportLinkedDevice) {
+    AuthenticatorCommonTizen* thiz =
+        reinterpret_cast<AuthenticatorCommonTizen*>(data);
+    if (linked_data == nullptr) {
+      LOG(INFO) << "[Passkey] linked data is NULL, do not update";
+      return;
+    }
+    thiz->UpdateLinkedData(linked_data);
+  }
 }
 
 void AuthenticatorCommonTizen::GetAssertionUpdateLinkedDataCallback(
     const wauthn_hybrid_linked_data_s* linked_data,
     wauthn_error_e result,
     void* data) {
-  NOTIMPLEMENTED();
+  LOG(INFO) << "[Passkey] GAUpdateLinkedDataCallback invoked, result : "
+            << (int)result;
+  /* When WAUTHN_ERROR_NONE_AND_WAIT arrived, should keep waiting.
+     Also not update linked device info under other error circumstances*/
+  if (result != WAUTHN_ERROR_NONE) {
+    return;
+  }
+  LOG(INFO) << "[Passkey] update linked info";
+  if (supportLinkedDevice) {
+    AuthenticatorCommonTizen* thiz =
+        reinterpret_cast<AuthenticatorCommonTizen*>(data);
+   if (linked_data == nullptr) {
+      LOG(INFO) << "[Passkey] linked data is NULL, do not update";
+      return;
+    }
+    thiz->UpdateLinkedData(linked_data);
+  }
 }
 
 // RequestState contains all state that is specific to a single WebAuthn call.
@@ -696,6 +792,14 @@ struct AuthenticatorCommonTizen::RequestState {
       remote_rp_id_validation;
 };
 
+std::string to_hex_string(const unsigned char* data, int length) {
+    std::stringstream ss;
+    for (int i = 0; i < length; ++i) {
+        ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(data[i]);
+    }
+    return ss.str();
+}
+
 // static
 std::unique_ptr<AuthenticatorCommon> AuthenticatorCommon::Create(
     RenderFrameHost* render_frame_host) {
@@ -716,6 +820,14 @@ AuthenticatorCommonTizen::AuthenticatorCommonTizen(
       render_frame_host,
       BackForwardCacheDisable::DisabledReason(
           BackForwardCacheDisable::DisabledReasonId::kWebAuthenticationAPI));
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTizenWAuthnLinkedDeviceEnable)) {
+    LOG(INFO) << "[Passkey] browser support LinkedDevice scenario";
+    supportLinkedDevice = true;
+  } else {
+    LOG(INFO) << "[Passkey] browser not support LinkedDevice scenario";
+  }
 }
 
 AuthenticatorCommonTizen::~AuthenticatorCommonTizen() = default;
@@ -749,8 +861,70 @@ bool AuthenticatorCommonTizen::IsFocused() const {
              WebContents::FromRenderFrameHost(GetRenderFrameHost()));
 }
 
+void AuthenticatorCommonTizen::DisplayWauthnDialog() {
+  LOG(INFO) << "[Passkey] DisplayWauthnDialog";
+  RenderFrameHostImpl* const render_frame_host_impl =
+      static_cast<RenderFrameHostImpl*>(this->GetRenderFrameHost());
+
+  LOG(INFO) << "[Passkey] TO render_frame_host_impl->DisplayWauthnDialog";
+  render_frame_host_impl->DisplayWauthnDialog();
+}
+
+void AuthenticatorCommonTizen::UpdateLinkedData(
+    const wauthn_hybrid_linked_data_s* linked_data) {
+  RenderFrameHostImpl* const render_frame_host_impl =
+      static_cast<RenderFrameHostImpl*>(this->GetRenderFrameHost());
+
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(&RenderFrameHostImpl::UpdateLinkedData,
+                     base::Unretained(render_frame_host_impl), (const void*)linked_data));
+
+  LOG(INFO) << "[Passkey] contact_id size           : "
+            << linked_data->contact_id->size;
+  LOG(INFO) << "[Passkey] contact_id                : "
+              << to_hex_string(linked_data->contact_id->data, linked_data->contact_id->size);
+
+  LOG(INFO) << "[Passkey] link_id size              : "
+            << linked_data->link_id->size;
+  LOG(INFO) << "[Passkey] link_id                   : "
+              << to_hex_string(linked_data->link_id->data, linked_data->link_id->size);
+
+  LOG(INFO) << "[Passkey] link_secret size          : "
+            << linked_data->link_secret->size;
+  LOG(INFO) << "[Passkey] link_secret               : "
+              << to_hex_string(linked_data->link_secret->data, linked_data->link_secret->size);
+
+  LOG(INFO) << "[Passkey] authenticator_pubkey size : "
+            << linked_data->authenticator_pubkey->size;
+  LOG(INFO) << "[Passkey] authenticator_pubkey      : "
+              << to_hex_string(linked_data->authenticator_pubkey->data, linked_data->authenticator_pubkey->size);
+
+  LOG(INFO) << "[Passkey] authenticator_name size   : "
+            << linked_data->authenticator_name->size;
+  LOG(INFO) << "[Passkey] authenticator_name        : "
+              << to_hex_string(linked_data->authenticator_name->data, linked_data->authenticator_name->size);
+
+  LOG(INFO) << "[Passkey] signature size            : "
+            << linked_data->signature->size;
+  LOG(INFO) << "[Passkey] signature                 : "
+              << to_hex_string(linked_data->signature->data, linked_data->signature->size);
+
+  LOG(INFO) << "[Passkey] tunnel_server_domain size : "
+            << linked_data->tunnel_server_domain->size;
+  LOG(INFO) << "[Passkey] tunnel_server_domain      : "
+              << to_hex_string(linked_data->tunnel_server_domain->data, linked_data->tunnel_server_domain->size);
+
+  LOG(INFO) << "[Passkey] identity_key size         : "
+            << linked_data->identity_key->size;
+  LOG(INFO) << "[Passkey] identity_key              : "
+              << to_hex_string(linked_data->identity_key->data, linked_data->identity_key->size);
+
+}
+
 void AuthenticatorCommonTizen::PopulateMakeCredentialWebAuthnArguments(
     blink::mojom::PublicKeyCredentialCreationOptionsPtr options) {
+  LOG(INFO) << "[Passkey] PopulateMCWebauthnArgs";
   std::memset(&mc_options_, 0, sizeof(mc_options_));
 
   // Client data
@@ -1052,6 +1226,7 @@ void AuthenticatorCommonTizen::PopulateMakeCredentialWebAuthnArguments(
 
 void AuthenticatorCommonTizen::PopulateGetAssertionWebAuthnArguments(
     blink::mojom::PublicKeyCredentialRequestOptionsPtr options) {
+  LOG(INFO) << "[Passkey] PopulateGAWebauthnArgs";
   std::memset(&ga_options_, 0, sizeof(ga_options_));
 
   // Client data
@@ -1204,9 +1379,10 @@ void AuthenticatorCommonTizen::MakeCredential(
     url::Origin caller_origin,
     blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
     MakeCredentialCallback callback) {
-  LOG(INFO) << __FUNCTION__;
+  LOG(INFO) << "[Passkey] AuthenticatorCommonTizen::MakeCredential";
 
   if (req_state_) {
+    LOG(INFO) << "[Passkey] req_state_ not NULL";
     std::move(callback).Run(blink::mojom::AuthenticatorStatus::PENDING_REQUEST,
                             nullptr, nullptr);
     return;
@@ -1234,6 +1410,7 @@ void AuthenticatorCommonTizen::MakeCredential(
       security_checker_->ValidateAncestorOrigins(caller_origin, request_type,
                                                  &is_cross_origin_iframe);
   if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
+    LOG(INFO) << "[Passkey] ValidateAncestorOrigins status : " << (int)status;
     req_state_->request_outcome = MakeCredentialOutcome::kSecurityError;
     CompleteMakeCredentialRequest(status);
     return;
@@ -1245,6 +1422,7 @@ void AuthenticatorCommonTizen::MakeCredential(
     req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
     CompleteMakeCredentialRequest(
         blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
+    LOG(INFO) << "[Passkey] invalid exclude_credentials length";
     return;
   }
 
@@ -1280,12 +1458,15 @@ void AuthenticatorCommonTizen::ContinueMakeCredentialAfterRpIdCheck(
 
   if (rp_id_validation_result != blink::mojom::AuthenticatorStatus::SUCCESS) {
     req_state_->request_outcome = MakeCredentialOutcome::kSecurityError;
+    LOG(INFO) << "[Passkey] ValidateDomainAndRelyingPartyID rp_id_validation_result : "
+              << (int)rp_id_validation_result;
     CompleteMakeCredentialRequest(rp_id_validation_result);
     return;
   }
 
   req_state_->request_delegate = MaybeCreateRequestDelegate();
   if (!req_state_->request_delegate) {
+    LOG(INFO) << "[Passkey] request_delegate is NULL";
     req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
     CompleteMakeCredentialRequest(
         blink::mojom::AuthenticatorStatus::PENDING_REQUEST);
@@ -1296,6 +1477,7 @@ void AuthenticatorCommonTizen::ContinueMakeCredentialAfterRpIdCheck(
       !disable_tls_check_ &&
       !GetContentClient()->browser()->IsSecurityLevelAcceptableForWebAuthn(
           GetRenderFrameHost(), caller_origin)) {
+    LOG(INFO) << "[Passkey] CERTIFICATE_ERROR";
     req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
     CompleteMakeCredentialRequest(
         blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR);
@@ -1312,6 +1494,8 @@ void AuthenticatorCommonTizen::ContinueMakeCredentialAfterRpIdCheck(
         *options->appid_exclude, caller_origin,
         options->remote_desktop_client_override, &appid_exclude.value());
     if (add_id_status != blink::mojom::AuthenticatorStatus::SUCCESS) {
+      LOG(INFO) << "[Passkey] ValidateAppIdExtension add_id_status : "
+                << (int)add_id_status;
       req_state_->request_outcome = MakeCredentialOutcome::kSecurityError;
       CompleteMakeCredentialRequest(add_id_status);
       return;
@@ -1328,11 +1512,13 @@ void AuthenticatorCommonTizen::ContinueMakeCredentialAfterRpIdCheck(
   if (proxy) {
     if (options->remote_desktop_client_override) {
       // Don't allow proxying of an already proxied request.
+      LOG(INFO) << "[Passkey] NOT_ALLOWED_ERROR";
       req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
       CompleteMakeCredentialRequest(
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
     }
+    LOG(INFO) << "[Passkey] proxy not NULL";
     options->remote_desktop_client_override =
         blink::mojom::RemoteDesktopClientOverride::New(
             /*origin=*/req_state_->caller_origin,
@@ -1369,6 +1555,7 @@ void AuthenticatorCommonTizen::ContinueMakeCredentialAfterRpIdCheck(
       // This will be handled by the request handler.
       break;
     case device::fido_filter::Action::BLOCK:
+      LOG(INFO) << "[Passkey] fido_filter::Action::BLOCK";
       req_state_->request_outcome = MakeCredentialOutcome::kFilterBlock;
       CompleteMakeCredentialRequest(
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
@@ -1376,6 +1563,7 @@ void AuthenticatorCommonTizen::ContinueMakeCredentialAfterRpIdCheck(
   }
 
   if (!IsFocused()) {
+    LOG(INFO) << "[Passkey] Not focused";
     req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
     CompleteMakeCredentialRequest(
         blink::mojom::AuthenticatorStatus::NOT_FOCUSED);
@@ -1432,6 +1620,7 @@ void AuthenticatorCommonTizen::ContinueMakeCredentialAfterRpIdCheck(
            blink::mojom::ProtectionPolicy::UV_REQUIRED &&
        authenticator_selection_criteria.user_verification_requirement !=
            device::UserVerificationRequirement::kRequired)) {
+    LOG(INFO) << "[Passkey] PROTECTION_POLICY_INCONSISTENT";
     req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
     CompleteMakeCredentialRequest(
         blink::mojom::AuthenticatorStatus::PROTECTION_POLICY_INCONSISTENT);
@@ -1480,6 +1669,7 @@ void AuthenticatorCommonTizen::ContinueMakeCredentialAfterRpIdCheck(
       std::optional<device::PRFInput> prf_input =
           ParsePRFInputForMakeCredential(options->prf_input);
       if (!prf_input) {
+        LOG(INFO) << "[Passkey] invalid PRF inputs, NOT_ALLOWED_ERROR";
         mojo::ReportBadMessage("invalid PRF inputs");
         req_state_->request_outcome = MakeCredentialOutcome::kOtherFailure;
         CompleteMakeCredentialRequest(
@@ -1528,6 +1718,17 @@ void AuthenticatorCommonTizen::ContinueMakeCredentialAfterRpIdCheck(
   ctap_make_credential_request->attestation_preference = attestation;
 
   PopulateMakeCredentialWebAuthnArguments(std::move(options));
+  if (supportLinkedDevice) {
+    DisplayWauthnDialog();
+  } else {
+    ContinueMakeCredential();
+  }
+}
+
+void AuthenticatorCommonTizen::ContinueMakeCredential() {
+  LOG(INFO) << "[Passkey] TO wauthn_make_credential, authenticator : " << this;
+  LOG(INFO) << "[Passkey] TO wauthn_make_credential with linked_device : "
+            << ((mc_options_.linked_device) ? "yes" : "no");
   int ret = wauthn_make_credential(&client_data_, &mc_options_, &mc_callbacks_);
   if (ret != WAUTHN_ERROR_NONE) {
     LOG(ERROR) << __FUNCTION__ << " call returned error status: "
@@ -1541,9 +1742,10 @@ void AuthenticatorCommonTizen::GetAssertion(
     blink::mojom::PublicKeyCredentialRequestOptionsPtr options,
     blink::mojom::PaymentOptionsPtr payment_options,
     GetAssertionCallback callback) {
-  LOG(INFO) << __FUNCTION__;
+  LOG(INFO) << "[Passkey] AuthenticatorCommonTizen::GetAssertion";
 
   if (req_state_) {
+    LOG(INFO) << "[Passkey] req_state_ not NULL";
     std::move(callback).Run(blink::mojom::AuthenticatorStatus::PENDING_REQUEST,
                             nullptr, nullptr);
     return;
@@ -1589,6 +1791,7 @@ void AuthenticatorCommonTizen::GetAssertion(
           : WebAuthRequestSecurityChecker::RequestType::
                 kGetPaymentCredentialAssertion;
   if (!payment_options.is_null() && options->allow_credentials.empty()) {
+    LOG(INFO) << "[Passkey] NOT_ALLOWED_ERROR";
     req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
     CompleteGetAssertionRequest(
         blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
@@ -1600,6 +1803,7 @@ void AuthenticatorCommonTizen::GetAssertion(
       security_checker_->ValidateAncestorOrigins(caller_origin, request_type,
                                                  &is_cross_origin_iframe);
   if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
+    LOG(INFO) << "[Passkey] ValidateAncestorOrigins status : " << (int)status;
     req_state_->request_outcome = GetAssertionOutcome::kSecurityError;
     CompleteGetAssertionRequest(status);
     return;
@@ -1609,6 +1813,8 @@ void AuthenticatorCommonTizen::GetAssertion(
           &options->allow_credentials)) {
     mojo::ReportBadMessage("invalid allow_credentials length");
     req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
+    LOG(INFO)
+        << "[Passkey] invalid allow_credentials length, NOT_ALLOWED_ERROR";
     CompleteGetAssertionRequest(
         blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
     return;
@@ -1642,19 +1848,24 @@ void AuthenticatorCommonTizen::ContinueGetAssertionAfterRpIdCheck(
     blink::mojom::PaymentOptionsPtr payment_options,
     bool is_cross_origin_iframe,
     blink::mojom::AuthenticatorStatus rp_id_validation_result) {
+  LOG(INFO) << "[Passkey] ContinueGetAssertionAfterRpIdCheck";
   if (!CheckRequestKey(request_key)) {
+    LOG(INFO) << "[Passkey] CheckRequestKey failed";
     return;
   }
   req_state_->remote_rp_id_validation.reset();
 
   if (rp_id_validation_result != blink::mojom::AuthenticatorStatus::SUCCESS) {
     req_state_->request_outcome = GetAssertionOutcome::kSecurityError;
+    LOG(INFO) << "[Passkey] ValidateDomainAndRelyingPartyID rp_id_validation_result : "
+              << (int)rp_id_validation_result;
     CompleteGetAssertionRequest(rp_id_validation_result);
     return;
   }
 
   req_state_->request_delegate = MaybeCreateRequestDelegate();
   if (!req_state_->request_delegate) {
+    LOG(INFO) << "[Passkey] request_delegate is NULL";
     req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
     CompleteGetAssertionRequest(
         blink::mojom::AuthenticatorStatus::PENDING_REQUEST);
@@ -1664,6 +1875,7 @@ void AuthenticatorCommonTizen::ContinueGetAssertionAfterRpIdCheck(
       !disable_tls_check_ &&
       !GetContentClient()->browser()->IsSecurityLevelAcceptableForWebAuthn(
           GetRenderFrameHost(), caller_origin)) {
+    LOG(INFO) << "[Passkey] CERTIFICATE_ERROR";
     req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
     CompleteGetAssertionRequest(
         blink::mojom::AuthenticatorStatus::CERTIFICATE_ERROR);
@@ -1680,6 +1892,8 @@ void AuthenticatorCommonTizen::ContinueGetAssertionAfterRpIdCheck(
         *options->extensions->appid, caller_origin,
         options->extensions->remote_desktop_client_override, &app_id);
     if (add_id_status != blink::mojom::AuthenticatorStatus::SUCCESS) {
+      LOG(INFO) << "[Passkey] ValidateAppIdExtension add_id_status : "
+                << (int)add_id_status;
       req_state_->request_outcome = GetAssertionOutcome::kSecurityError;
       CompleteGetAssertionRequest(add_id_status);
       return;
@@ -1696,6 +1910,7 @@ void AuthenticatorCommonTizen::ContinueGetAssertionAfterRpIdCheck(
     if (options->is_conditional ||
         (options->extensions->remote_desktop_client_override)) {
       // Don't allow proxying of an already proxied or conditional request.
+      LOG(INFO) << "[Passkey] NOT_ALLOWED_ERROR";
       req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
       CompleteGetAssertionRequest(
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
@@ -1709,6 +1924,7 @@ void AuthenticatorCommonTizen::ContinueGetAssertionAfterRpIdCheck(
         options,
         base::BindOnce(&AuthenticatorCommonTizen::OnGetAssertionProxyResponse,
                        weak_factory_.GetWeakPtr(), GetRequestKey()));
+    LOG(INFO) << "[Passkey] proxy is not NULL";
     return;
   }
 
@@ -1748,6 +1964,7 @@ void AuthenticatorCommonTizen::ContinueGetAssertionAfterRpIdCheck(
           req_state_->relying_party_id,
           /*device=*/std::nullopt,
           /*id=*/std::nullopt) == device::fido_filter::Action::BLOCK) {
+    LOG(INFO) << "[Passkey] fido_filter::Action::BLOCK, NOT_ALLOWED_ERROR";
     req_state_->request_outcome = GetAssertionOutcome::kFilterBlock;
     CompleteGetAssertionRequest(
         blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
@@ -1786,6 +2003,7 @@ void AuthenticatorCommonTizen::ContinueGetAssertionAfterRpIdCheck(
 
   if (options->extensions->large_blob_read &&
       options->extensions->large_blob_write) {
+    LOG(INFO) << "[Passkey] CANNOT_READ_AND_WRITE_LARGE_BLOB";
     req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
     CompleteGetAssertionRequest(
         blink::mojom::AuthenticatorStatus::CANNOT_READ_AND_WRITE_LARGE_BLOB);
@@ -1796,6 +2014,7 @@ void AuthenticatorCommonTizen::ContinueGetAssertionAfterRpIdCheck(
     req_state_->requested_extensions.insert(RequestExtension::kLargeBlobRead);
   } else if (options->extensions->large_blob_write) {
     if (options->allow_credentials.size() != 1) {
+      LOG(INFO) << "[Passkey] INVALID_ALLOW_CREDENTIALS_FOR_LARGE_BLOB";
       req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
       CompleteGetAssertionRequest(blink::mojom::AuthenticatorStatus::
                                       INVALID_ALLOW_CREDENTIALS_FOR_LARGE_BLOB);
@@ -1828,6 +2047,7 @@ void AuthenticatorCommonTizen::ContinueGetAssertionAfterRpIdCheck(
     // authenticator support on Android.
     if (!prf_inputs || options->extensions->prf_inputs_hashed) {
       mojo::ReportBadMessage("invalid PRF inputs");
+      LOG(INFO) << "[Passkey] invalid PRF inputs, NOT_ALLOWED_ERROR";
       req_state_->request_outcome = GetAssertionOutcome::kOtherFailure;
       CompleteGetAssertionRequest(
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
@@ -1860,6 +2080,7 @@ void AuthenticatorCommonTizen::
     ContinueGetAssertionAfterBrowserPasskeysAvailabilityCheck(
         RequestKey request_key,
         bool available) {
+  LOG(INFO) << "[Passkey] ContinueGetAssertionAfterBrowserPasskeysAvailabilityCheck";
   if (!CheckRequestKey(request_key)) {
     return;
   }
@@ -1875,11 +2096,23 @@ void AuthenticatorCommonTizen::
 void AuthenticatorCommonTizen::ContinueGetAssertionAfterIsUvpaaOverrideCheck(
     RequestKey request_key,
     std::optional<bool> is_uvpaa_override) {
+  LOG(INFO) << "[Passkey] ContinueGetAssertionAfterIsUvpaaOverrideCheck";
   if (!CheckRequestKey(request_key)) {
     return;
   }
   is_uvpaa_override_ = is_uvpaa_override;
 
+  if (supportLinkedDevice) {
+    DisplayWauthnDialog();
+  } else {
+    ContinueGetAssertion();
+  }
+}
+
+void AuthenticatorCommonTizen::ContinueGetAssertion() {
+  LOG(INFO) << "[Passkey] TO wauthn_get_assertion, authenticator : " << this;
+  LOG(INFO) << "[Passkey] TO wauthn_get_assertion with linked_device : "
+            << ((mc_options_.linked_device) ? "yes" : "no");
   int ret = wauthn_get_assertion(&client_data_, &ga_options_, &ga_callbacks_);
   if (ret != WAUTHN_ERROR_NONE) {
     LOG(ERROR) << __FUNCTION__ << " call returned error status: "
@@ -2040,6 +2273,73 @@ void AuthenticatorCommonTizen::
 #endif
 }
 
+void AuthenticatorCommonTizen::SetWauthnLinkedDevice(void* linkDevice) {
+  LOG(INFO) << "[Passkey] Set linkDevice, authenticator : " << this;
+  wauthn_hybrid_linked_data_s *deviceInfo = (wauthn_hybrid_linked_data_s *)linkDevice;
+  LOG(INFO) << "[Passkey] contact_id size           : "
+            << deviceInfo->contact_id->size;
+  LOG(INFO) << "[Passkey] contact_id                : "
+            << to_hex_string(deviceInfo->contact_id->data, deviceInfo->contact_id->size);
+
+  LOG(INFO) << "[Passkey] link_id size              : "
+            << deviceInfo->link_id->size;
+  LOG(INFO) << "[Passkey] link_id                   : "
+            << to_hex_string(deviceInfo->link_id->data, deviceInfo->link_id->size);
+
+  LOG(INFO) << "[Passkey] link_secret size          : "
+            << deviceInfo->link_secret->size;
+  LOG(INFO) << "[Passkey] link_secret               : "
+            << to_hex_string(deviceInfo->link_secret->data, deviceInfo->link_secret->size);
+
+  LOG(INFO) << "[Passkey] authenticator_pubkey size : "
+            << deviceInfo->authenticator_pubkey->size;
+  LOG(INFO) << "[Passkey] authenticator_pubkey      : "
+            << to_hex_string(deviceInfo->authenticator_pubkey->data, deviceInfo->authenticator_pubkey->size);
+
+  LOG(INFO) << "[Passkey] authenticator_name size   : "
+            << deviceInfo->authenticator_name->size;
+  LOG(INFO) << "[Passkey] authenticator_name        : "
+            << to_hex_string(deviceInfo->authenticator_name->data, deviceInfo->authenticator_name->size);
+
+  LOG(INFO) << "[Passkey] signature size            : "
+            << deviceInfo->signature->size;
+  LOG(INFO) << "[Passkey] signature                 : "
+            << to_hex_string(deviceInfo->signature->data, deviceInfo->signature->size);
+
+  LOG(INFO) << "[Passkey] tunnel_server_domain size : "
+            << deviceInfo->tunnel_server_domain->size;
+  LOG(INFO) << "[Passkey] tunnel_server_domain      : "
+            << to_hex_string(deviceInfo->tunnel_server_domain->data, deviceInfo->tunnel_server_domain->size);
+
+  LOG(INFO) << "[Passkey] identity_key size         : "
+            << deviceInfo->identity_key->size;
+  LOG(INFO) << "[Passkey] identity_key              : "
+            << to_hex_string(deviceInfo->identity_key->data, deviceInfo->identity_key->size);
+
+  if (absl::holds_alternative<MakeCredentialCallback>(req_state_.get()->response_callback)) {
+    mc_options_.linked_device = (wauthn_hybrid_linked_data_s*)linkDevice;
+    ContinueMakeCredential();
+  } else if (absl::holds_alternative<GetAssertionCallback>(req_state_.get()->response_callback)) {
+    ga_options_.linked_device = (wauthn_hybrid_linked_data_s*)linkDevice;
+    ContinueGetAssertion();
+  } else {
+    LOG(INFO) << "[Passkey] invalid req_state_";
+  }
+}
+
+void AuthenticatorCommonTizen::ClearWauthnLinkedDevice() {
+  LOG(INFO) << "[Passkey] Clear linkDevice, authenticator : " << this;
+  if (absl::holds_alternative<MakeCredentialCallback>(req_state_.get()->response_callback)) {
+    mc_options_.linked_device = nullptr;
+    ContinueMakeCredential();
+  } else if (absl::holds_alternative<GetAssertionCallback>(req_state_.get()->response_callback)) {
+    ga_options_.linked_device = nullptr;
+    ContinueGetAssertion();
+  } else {
+    LOG(INFO) << "[Passkey] invalid req_state_";
+  }
+}
+
 void AuthenticatorCommonTizen::Report(
     url::Origin caller_origin,
     blink::mojom::PublicKeyCredentialReportOptionsPtr options,
@@ -2161,6 +2461,8 @@ void AuthenticatorCommonTizen::SignalFailureToRequestDelegate(
 
 void AuthenticatorCommonTizen::BeginRequestTimeout(
     std::optional<base::TimeDelta> timeout) {
+  LOG(INFO) << "[Passkey] timeout(microseconds)  : "
+            << timeout->InMicroseconds();
   req_state_->timer->Start(FROM_HERE,
                            AdjustTimeout(timeout, GetRenderFrameHost()),
                            base::BindOnce(&AuthenticatorCommonTizen::OnTimeout,
@@ -2170,6 +2472,14 @@ void AuthenticatorCommonTizen::BeginRequestTimeout(
 // TODO(crbug.com/41371792): Add web tests to verify timeouts are
 // indistinguishable from NOT_ALLOWED_ERROR cases.
 void AuthenticatorCommonTizen::OnTimeout() {
+  LOG(INFO) << "[Passkey] Timeout";
+  RenderFrameHostImpl* const render_frame_host_impl =
+      static_cast<RenderFrameHostImpl*>(this->GetRenderFrameHost());
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(&RenderFrameHostImpl::UpdateWAuthnResult,
+                                base::Unretained(render_frame_host_impl),
+                                WAUTHN_ERROR_TIMED_OUT));
+
   if (!req_state_->request_delegate) {
     // If no UI has been shown yet (likely because we timed out waiting for RP
     // ID validation) then simply cancel the request.
@@ -2315,6 +2625,8 @@ void AuthenticatorCommonTizen::CompleteMakeCredentialRequest(
     blink::mojom::MakeCredentialAuthenticatorResponsePtr response,
     blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details,
     Focus check_focus) {
+  LOG(INFO) << "[Passkey] CompleteMakeCredentialRequest, status : "
+            << (int)status << ", check_focus : " << (int)check_focus;
   DCHECK(absl::holds_alternative<MakeCredentialCallback>(
              req_state_->response_callback) &&
          absl::get<MakeCredentialCallback>(req_state_->response_callback));
@@ -2339,9 +2651,11 @@ void AuthenticatorCommonTizen::CompleteMakeCredentialRequest(
 
   if (check_focus != Focus::kDontCheck &&
       !(req_state_->request_delegate && IsFocused())) {
+    LOG(INFO) << "NOT_FOCUSED, response with nullptr";
     std::move(make_credential_response_callback)
         .Run(blink::mojom::AuthenticatorStatus::NOT_FOCUSED, nullptr, nullptr);
   } else {
+    LOG(INFO) << "FOCUSED, response with detailed info";
     std::move(make_credential_response_callback)
         .Run(status, std::move(response), std::move(dom_exception_details));
   }
index 754707a09236f765ea1a7bcb3e33f4aac396fb83..f4238b409e208f187e734990fab0510704422dde 100644 (file)
@@ -189,6 +189,8 @@ class CONTENT_EXPORT AuthenticatorCommonTizen : public AuthenticatorCommon {
   void Report(url::Origin caller_origin,
               blink::mojom::PublicKeyCredentialReportOptionsPtr options,
               blink::mojom::Authenticator::ReportCallback callback);
+  void SetWauthnLinkedDevice(void* linkDevice);
+  void ClearWauthnLinkedDevice();
 
  protected:
   // MaybeCreateRequestDelegate returns the embedder-provided implementation of
@@ -269,6 +271,9 @@ class CONTENT_EXPORT AuthenticatorCommonTizen : public AuthenticatorCommon {
           IsUserVerifyingPlatformAuthenticatorAvailableCallback callback,
       bool is_get_client_capabilities_call);
 
+  void DisplayWauthnDialog();
+  void UpdateLinkedData(const wauthn_hybrid_linked_data_s* linked_data);
+
   void DispatchGetAssertionRequest(
       const std::string& authenticator_id,
       absl::optional<std::vector<uint8_t>> credential_id);
@@ -345,6 +350,8 @@ class CONTENT_EXPORT AuthenticatorCommonTizen : public AuthenticatorCommon {
       blink::mojom::PublicKeyCredentialCreationOptionsPtr options);
   void PopulateGetAssertionWebAuthnArguments(
       blink::mojom::PublicKeyCredentialRequestOptionsPtr options);
+  void ContinueMakeCredential();
+  void ContinueGetAssertion();
   static void DisplayQRCallback(const char* qr_contents, void* data);
   static void MakeCredentialResponseCallback(
       const wauthn_pubkey_credential_attestation_s* pubkey_cred,
index 29d32e37be6ed472cccaf218976036e100b855ae..f0979314526b82a2eeb4e15324bf22fe12c5c19a 100644 (file)
@@ -41,6 +41,7 @@ const char kEnableTizenSocketsWebApi[] = "enable-tizen-sockets-webapi";
 // Widget Info
 const char kTizenAppId[] = "widget-id";
 const char kTizenAppVersion[] = "app-version";
+const char kTizenWAuthnLinkedDeviceEnable[] = "enable-linked-device";
 const char kWidgetScale[] = "widget-scale";
 const char kWidgetTheme[] = "widget-theme";
 const char kWidgetEncodedBundle[] = "widget-encoded-bundle";
index 5d63287dd18c5e0e733035bb136c055cb95a9743..dedbf23aa40ea2f1b615c272e4ac53b4ae69a475 100644 (file)
@@ -43,6 +43,7 @@ CONTENT_EXPORT extern const char kEnableTizenSocketsWebApi[];
 
 CONTENT_EXPORT extern const char kTizenAppId[];
 CONTENT_EXPORT extern const char kTizenAppVersion[];
+CONTENT_EXPORT extern const char kTizenWAuthnLinkedDeviceEnable[];
 CONTENT_EXPORT extern const char kWidgetScale[];
 CONTENT_EXPORT extern const char kWidgetTheme[];
 CONTENT_EXPORT extern const char kWidgetEncodedBundle[];
index 9fdfe6c2958eb2395297154b23cc97c4500c0bac..45ae0b38be1d2261d7a6d596cdcfc58f0b6f5097 100644 (file)
@@ -3512,6 +3512,21 @@ void EWebView::CancelAuthentication() {
   }
   rwhva()->aura_efl_helper()->CancelAuthentication();
 }
+
+void EWebView::SetWauthnMethod(bool showQRCode, void* linkDevice) {
+  LOG(INFO) << "[Passkey] showQRCode : " << (int)showQRCode
+            << ", linkDevice : " << ((linkDevice) ? linkDevice : 0);
+  if (!rwhva() || !rwhva()->aura_efl_helper()) {
+    return;
+  }
+  if (showQRCode) {
+    rwhva()->aura_efl_helper()->ClearWauthnLinkedDevice();
+  } else {
+    if (!linkDevice)
+      return;
+    rwhva()->aura_efl_helper()->SetWauthnLinkedDevice(linkDevice);
+  }
+}
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
 void EWebView::SetDidChangeThemeColorCallback(
index d1124949c60e2c3ae5458746e94c9bd0af14ab4d..cbecaa69d36ac4db205bb08269bdae03100243f9 100644 (file)
@@ -919,6 +919,7 @@ class EWebView {
 
 #if defined(TIZEN_PASSKEY_SUPPORT)
   void CancelAuthentication();
+  void SetWauthnMethod(bool showQRCode, void* linkDevice);
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
   void SetDidChangeThemeColorCallback(
index 7b968f4b1d88bcd533b01d6e35259749cab5b940..d434bcced30441aa96ac7c51fcbdc2aa8e52578a 100644 (file)
@@ -37,6 +37,8 @@
 #include "private/ewk_autofill_profile_private.h"
 #include "private/ewk_file_chooser_request_private.h"
 #include "public/ewk_view_internal.h"
+#endif
+#if BUILDFLAG(IS_TIZEN_TV) || defined(TIZEN_PASSKEY_SUPPORT)
 #include "public/ewk_view_product.h"
 #endif
 
@@ -199,7 +201,10 @@ enum CallbackType {
 #if defined(TIZEN_PASSKEY_SUPPORT)
   FocusOut,
   DisplayQRCode,
-  CloseQRCode
+  CloseQRCode,
+  DisplayWebAuthnDialog,
+  UpdateLinkedData,
+  UpdateWAuthnResult
 #else
   FocusOut
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
@@ -323,6 +328,15 @@ DECLARE_EWK_VIEW_CALLBACK(EdgeRight, "edge,right", void);
 #if defined(TIZEN_PASSKEY_SUPPORT)
 DECLARE_EWK_VIEW_CALLBACK(DisplayQRCode, "webauthn,display,qr", const char*);
 DECLARE_EWK_VIEW_CALLBACK(CloseQRCode, "webauthn,response", void);
+DECLARE_EWK_VIEW_CALLBACK(DisplayWebAuthnDialog,
+                          "webauth,display,dialog",
+                          void);
+DECLARE_EWK_VIEW_CALLBACK(UpdateLinkedData,
+                          "webauth,update,linked,data",
+                          Ewk_Wauthn_Hybrid_Linked_Data*);
+DECLARE_EWK_VIEW_CALLBACK(UpdateWAuthnResult,
+                          "webauth,update,result",
+                          Ewk_Wauthn_Error_E*);
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 #if BUILDFLAG(IS_TIZEN_TV)
 DECLARE_EWK_VIEW_CALLBACK(DownloadableFontInfo, "on,downloadable,font", void*);
index ffdea3a316b2e19081b5bcf463dd6fd8d6bbb427..4a71f174a26be85a695d33802c7b55e0536916e9 100644 (file)
@@ -2152,7 +2152,13 @@ Eina_Bool ewk_view_mouse_pointer_support_get(const Evas_Object *view)
 }
 
 void ewk_view_wauthn_method_set(const Evas_Object* view, const Ewk_Wauthn_Method *method) {
+  LOG(INFO) << "[Passkey] Set wauthn method";
+#if defined(TIZEN_PASSKEY_SUPPORT)
+  EWK_VIEW_IMPL_GET_OR_RETURN(view, impl);
+  impl->SetWauthnMethod(method->showQRCode, (void *)method->linkDevice);
+#else
   LOG_EWK_API_MOCKUP("Only for Tizen");
+#endif
 }
 
 Eina_Bool ewk_view_ime_position_align_set(const Evas_Object* view,
@@ -2170,6 +2176,7 @@ Eina_Bool ewk_view_ime_position_align_set(const Evas_Object* view,
 }
 
 void ewk_view_webauthn_cancel(const Evas_Object* view) {
+  LOG(INFO) << "[Passkey] Cancel wauthn";
 #if defined(TIZEN_PASSKEY_SUPPORT)
   EWK_VIEW_IMPL_GET_OR_RETURN(view, impl);
   impl->CancelAuthentication();
index 34e987e2bd1d7c05ac06209dece3fc541c20481b..c9c33edc0bd87b94c8e5aceb74b862591a93cef6 100644 (file)
@@ -1089,6 +1089,7 @@ void WebContentsDelegateEfl::DisplayQRCode(std::string contents) {
     LOG(ERROR) << "web_view_ is null";
     return;
   }
+  LOG(INFO) << "[Passkey] Invoke DisplayQRCode smart callback";
   web_view_->SmartCallback<EWebViewCallbacks::DisplayQRCode>().call(
       contents.c_str());
 }
@@ -1098,7 +1099,35 @@ void WebContentsDelegateEfl::CloseQRCode() {
     LOG(ERROR) << "web_view_ is null";
     return;
   }
+  LOG(INFO) << "[Passkey] Invoke CloseQRCode smart callback";
   web_view_->SmartCallback<EWebViewCallbacks::CloseQRCode>().call();
 }
+
+void WebContentsDelegateEfl::DisplayWauthnDialog() {
+  if (!web_view_) {
+    return;
+  }
+  LOG(INFO) << "[Passkey] Invoke DisplayWebAuthnDialog smart callback";
+  web_view_->SmartCallback<EWebViewCallbacks::DisplayWebAuthnDialog>().call();
+}
+
+void WebContentsDelegateEfl::UpdateLinkedData(const void* linked_data) {
+  if (!web_view_) {
+    return;
+  }
+  LOG(INFO) << "[Passkey] Invoke UpdateLinkedData smart callback";
+
+  web_view_->SmartCallback<EWebViewCallbacks::UpdateLinkedData>().call(
+      (Ewk_Wauthn_Hybrid_Linked_Data*)linked_data);
+}
+
+void WebContentsDelegateEfl::UpdateWAuthnResult(int result) {
+  if (!web_view_) {
+    return;
+  }
+  LOG(INFO) << "[Passkey] Invoke UpdateWAuthnResult smart callback";
+  web_view_->SmartCallback<EWebViewCallbacks::UpdateWAuthnResult>().call(
+      (Ewk_Wauthn_Error_E*)(&result));
+}
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 }  // namespace content
index 8029cbbe8a9e6278450c667e429c7c74928dd15f..596553f0bbf22f5bf29d5158f611d1b555c3fdef 100644 (file)
@@ -108,6 +108,9 @@ class WebContentsDelegateEfl : public WebContentsDelegate {
 #if defined(TIZEN_PASSKEY_SUPPORT)
   void DisplayQRCode(std::string contents) override;
   void CloseQRCode() override;
+  void DisplayWauthnDialog() override;
+  void UpdateLinkedData(const void* linked_data) override;
+  void UpdateWAuthnResult(int result) override;
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
   void RequestCertificateConfirm(
       WebContents* web_contents,
index e6ad770d5aec6b790a327ebb32f1c9b051d04720..e5f4c60e3cdd310dddc45e42c6606fcfc129be7d 100644 (file)
@@ -9,6 +9,7 @@
 #include <EWebKit_product.h>
 #include <Elementary.h>
 #include <assert.h>
+#include <fstream>
 
 #include <vector>
 
@@ -43,6 +44,10 @@ static Ewk_Application_Type application_type = EWK_APPLICATION_TYPE_OTHER;
 #endif
 #endif
 
+#if defined(TIZEN_PASSKEY_SUPPORT)
+#include "tizen_src/ewk/efl_integration/common/content_switches_efl.h"
+#endif
+
 // Parses tizen version string (2.4 | 2.3.1 | 3.0 etc).
 // Output std::vector where
 //   std::vector[0] = major,
@@ -179,6 +184,12 @@ Window::Window(Browser& browser, int width, int height, bool incognito)
                                  &Window::DisplayQRCode, this);
   evas_object_smart_callback_add(web_view_, "webauthn,response",
                                  &Window::CloseQRCode, this);
+  evas_object_smart_callback_add(web_view_, "webauth,display,dialog",
+                                 &Window::DisplayWAuthnDiaglog, this);
+  evas_object_smart_callback_add(web_view_, "webauth,update,linked,data",
+                                 &Window::SaveLinkedData, this);
+  evas_object_smart_callback_add(web_view_, "webauth,update,result",
+                                 &Window::GetWAuthnResult, this);
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
 #if BUILDFLAG(IS_TIZEN)
@@ -683,6 +694,7 @@ void Window::DisplayQRCode(void* data, Evas_Object*, void* contents) {
 }
 
 void Window::CloseQRCode(void* data, Evas_Object*, void*) {
+  log_trace("%s", __PRETTY_FUNCTION__);
   Window* thiz = static_cast<Window*>(data);
   evas_object_del(thiz->qr_img_);
   evas_object_del(thiz->qr_window_);
@@ -693,15 +705,241 @@ void Window::CloseQRCode(void* data, Evas_Object*, void*) {
 }
 
 void Window::CancelAuthentication(void* data, Evas_Object*, void*) {
+  log_trace("%s", __PRETTY_FUNCTION__);
   Window* thiz = static_cast<Window*>(data);
   ewk_view_webauthn_cancel(thiz->web_view_);
   evas_object_del(thiz->qr_img_);
   evas_object_del(thiz->qr_window_);
   evas_object_del(thiz->layout_);
+  evas_object_del(thiz->diag_window_);
   thiz->qr_img_ = NULL;
   thiz->qr_window_ = NULL;
   thiz->layout_ = NULL;
+  thiz->diag_window_ = NULL;
 }
+
+void Window::DisplayWAuthnDiaglog(void* data, Evas_Object*, void* contents) {
+  log_trace("%s", __PRETTY_FUNCTION__);
+
+  Window* thiz = static_cast<Window*>(data);
+  thiz->layout_ = elm_layout_add(thiz->window_);
+    
+  if (!thiz->layout_) {
+    log_info("no layout!");
+    return;
+  }
+
+  elm_layout_theme_set(thiz->layout_, "layout", "application", "default");
+  evas_object_size_hint_weight_set(thiz->layout_, EVAS_HINT_EXPAND,
+                                   EVAS_HINT_EXPAND);
+  evas_object_show(thiz->layout_);
+
+  thiz->diag_window_ = elm_popup_add(thiz->layout_);
+  if (!thiz->diag_window_) {
+    return;
+  }
+
+  evas_object_move(thiz->diag_window_, 350, 300);
+  evas_object_resize(thiz->diag_window_, 600, 600);
+  evas_object_show(thiz->diag_window_);
+
+  /* Cancel Button */
+  Evas_Object* cancel_btn = elm_button_add(thiz->diag_window_);
+  elm_object_text_set(cancel_btn, "Cancel");
+  elm_object_style_set(cancel_btn, "bottom");
+  evas_object_resize(cancel_btn, 100, 30);
+  evas_object_move(cancel_btn, 800, 150);
+  evas_object_show(cancel_btn);
+  elm_object_part_content_set(thiz->layout_, "button1", cancel_btn);
+  evas_object_smart_callback_add(cancel_btn, "clicked", CancelAuthentication,
+                                 thiz);
+
+  /* Show QR code Button */
+  Evas_Object* show_qr_btn = elm_button_add(thiz->diag_window_);
+  elm_object_text_set(show_qr_btn, "QR code");
+  elm_object_style_set(show_qr_btn, "bottom");
+  evas_object_resize(show_qr_btn, 100, 30);
+  evas_object_move(show_qr_btn, 800, 230);
+  evas_object_show(show_qr_btn);
+  elm_object_part_content_set(thiz->layout_, "button2", show_qr_btn);
+  evas_object_smart_callback_add(show_qr_btn, "clicked", Window::GetQRCode,
+                                 thiz);
+
+  /* Show Linked Device Button */
+  Evas_Object* linked_device_btn = elm_button_add(thiz->diag_window_);
+  elm_object_text_set(linked_device_btn, "Device");
+  elm_object_style_set(linked_device_btn, "bottom");
+  evas_object_resize(linked_device_btn, 100, 30);
+  evas_object_move(linked_device_btn, 800, 310);
+  evas_object_show(linked_device_btn);
+  elm_object_part_content_set(thiz->layout_, "button3", linked_device_btn);
+  evas_object_smart_callback_add(linked_device_btn, "clicked", Window::SetLinkedDevice,
+                                 thiz);
+
+}
+
+void Window::GetQRCode(void* data, Evas_Object*, void*) {
+  log_trace("%s", __PRETTY_FUNCTION__);
+  Ewk_Wauthn_Method method;
+  method.showQRCode = true;
+  Window* thiz = static_cast<Window*>(data);
+  ewk_view_wauthn_method_set(thiz->web_view_, &method);
+  evas_object_del(thiz->diag_window_);
+  evas_object_del(thiz->layout_);
+  thiz->diag_window_ = NULL;
+  thiz->layout_ = NULL;
+}
+
+void Window::SetLinkedDevice(void* data, Evas_Object*, void*) {
+  log_trace("%s", __PRETTY_FUNCTION__);
+  Window* thiz = static_cast<Window*>(data);
+
+  Ewk_Wauthn_Const_Buffer contact_id;
+  Ewk_Wauthn_Const_Buffer link_id;
+  Ewk_Wauthn_Const_Buffer link_secret;
+  Ewk_Wauthn_Const_Buffer authenticator_pubkey;
+  Ewk_Wauthn_Const_Buffer authenticator_name;
+  Ewk_Wauthn_Const_Buffer signature;
+  Ewk_Wauthn_Const_Buffer tunnel_server_domain;
+  Ewk_Wauthn_Const_Buffer identity_key;
+
+  std::ifstream inFile("/tmp/device-webauthn.bin", std::ios::binary);
+  std::vector<unsigned char> contact_id_data;
+  std::vector<unsigned char> link_id_data;
+  std::vector<unsigned char> link_secret_data;
+  std::vector<unsigned char> authenticator_pubkey_data;
+  std::vector<unsigned char> authenticator_name_data;
+  std::vector<unsigned char> signature_data;
+  std::vector<unsigned char> tunnel_server_domain_data;
+  std::vector<unsigned char> identity_key_data;
+  if (inFile) {
+    log_trace("/tmp/device-webauthn.bin openned, to read data");
+
+    inFile.read(reinterpret_cast<char *>(&contact_id.size), sizeof(size_t));
+    log_trace("read contact_id.size : %d", contact_id.size);
+    contact_id_data.resize(contact_id.size);
+    inFile.read(reinterpret_cast<char *>(contact_id_data.data()), contact_id.size);
+    contact_id.data = contact_id_data.data();
+    log_trace("read contact_id finished");
+
+    inFile.read(reinterpret_cast<char *>(&link_id.size), sizeof(size_t));
+    log_trace("read link_id.size : %d", link_id.size);
+    link_id_data.resize(link_id.size);
+    inFile.read(reinterpret_cast<char *>(link_id_data.data()), link_id.size);
+    link_id.data = link_id_data.data();
+    log_trace("read link_id finished");
+
+    inFile.read(reinterpret_cast<char *>(&link_secret.size), sizeof(size_t));
+    log_trace("read link_secret.size : %d", link_secret.size);
+    link_secret_data.resize(link_secret.size);
+    inFile.read(reinterpret_cast<char *>(link_secret_data.data()), link_secret.size);
+    link_secret.data = link_secret_data.data();
+    log_trace("read link_secret finished");
+
+    inFile.read(reinterpret_cast<char *>(&authenticator_pubkey.size), sizeof(size_t));
+    log_trace("read authenticator_pubkey.size : %d", authenticator_pubkey.size);
+    authenticator_pubkey_data.resize(authenticator_pubkey.size);
+    inFile.read(reinterpret_cast<char *>(authenticator_pubkey_data.data()), authenticator_pubkey.size);
+    authenticator_pubkey.data = authenticator_pubkey_data.data();
+    log_trace("read authenticator_pubkey finished");
+
+    inFile.read(reinterpret_cast<char *>(&authenticator_name.size), sizeof(size_t));
+    log_trace("read authenticator_name.size : %d", authenticator_name.size);
+    authenticator_name_data.resize(authenticator_name.size);
+    inFile.read(reinterpret_cast<char *>(authenticator_name_data.data()), authenticator_name.size);
+    authenticator_name.data = authenticator_name_data.data();
+    log_trace("read authenticator_name finished");
+
+    inFile.read(reinterpret_cast<char *>(&signature.size), sizeof(size_t));
+    log_trace("read signature.size : %d", signature.size);
+    signature_data.resize(signature.size);
+    inFile.read(reinterpret_cast<char *>(signature_data.data()), signature.size);
+    signature.data = signature_data.data();
+    log_trace("read signature finished");
+
+    inFile.read(reinterpret_cast<char *>(&tunnel_server_domain.size), sizeof(size_t));
+    log_trace("read tunnel_server_domain.size : %d", tunnel_server_domain.size);
+    tunnel_server_domain_data.resize(tunnel_server_domain.size);
+    inFile.read(reinterpret_cast<char *>(tunnel_server_domain_data.data()), tunnel_server_domain.size);
+    tunnel_server_domain.data = tunnel_server_domain_data.data();
+    log_trace("read tunnel_server_domain finished");
+
+    inFile.read(reinterpret_cast<char *>(&identity_key.size), sizeof(size_t));
+    log_trace("read identity_key.size : %d", identity_key.size);
+    identity_key_data.resize(identity_key.size);
+    inFile.read(reinterpret_cast<char *>(identity_key_data.data()), identity_key.size);
+    identity_key.data = identity_key_data.data();
+    log_trace("read identity_key finished");
+  } else {
+    log_trace("/tmp/device-webauthn.bin open failed, cancel operation");
+    Window::CancelAuthentication(data, NULL, NULL);
+    return;
+  }
+
+  Ewk_Wauthn_Hybrid_Linked_Data linkData;
+  linkData.contact_id           = &contact_id;
+  linkData.link_id              = &link_id;
+  linkData.link_secret          = &link_secret;
+  linkData.authenticator_pubkey = &authenticator_pubkey;
+  linkData.authenticator_name   = &authenticator_name;
+  linkData.signature            = &signature;
+  linkData.tunnel_server_domain = &tunnel_server_domain;
+  linkData.tunnel_server_domain = &tunnel_server_domain;
+  linkData.identity_key         = &identity_key;
+
+  Ewk_Wauthn_Method method;
+  method.showQRCode = false;
+  method.linkDevice = &linkData;
+
+  ewk_view_wauthn_method_set(thiz->web_view_, &method);
+  evas_object_del(thiz->diag_window_);
+  evas_object_del(thiz->layout_);
+  thiz->diag_window_ = NULL;
+  thiz->layout_ = NULL;
+}
+
+void Window::SaveLinkedData(void* data, Evas_Object*, void* LinkedDevice) {
+  log_trace("%s", __PRETTY_FUNCTION__);
+  Window* thiz = static_cast<Window*>(data);
+  Ewk_Wauthn_Hybrid_Linked_Data *deviceInfo = (Ewk_Wauthn_Hybrid_Linked_Data *)LinkedDevice;
+
+  std::ofstream outFile("/tmp/device-webauthn.bin", std::ios::binary);
+  if (outFile) {
+    outFile.write(reinterpret_cast<const char*>(&deviceInfo->contact_id->size), sizeof(size_t));
+    outFile.write(reinterpret_cast<const char*>(deviceInfo->contact_id->data), deviceInfo->contact_id->size);
+
+    outFile.write(reinterpret_cast<const char*>(&deviceInfo->link_id->size), sizeof(size_t));
+    outFile.write(reinterpret_cast<const char*>(deviceInfo->link_id->data), deviceInfo->link_id->size);
+
+    outFile.write(reinterpret_cast<const char*>(&deviceInfo->link_secret->size), sizeof(size_t));
+    outFile.write(reinterpret_cast<const char*>(deviceInfo->link_secret->data), deviceInfo->link_secret->size);
+
+    outFile.write(reinterpret_cast<const char*>(&deviceInfo->authenticator_pubkey->size), sizeof(size_t));
+    outFile.write(reinterpret_cast<const char*>(deviceInfo->authenticator_pubkey->data), deviceInfo->authenticator_pubkey->size);
+
+    outFile.write(reinterpret_cast<const char*>(&deviceInfo->authenticator_name->size), sizeof(size_t));
+    outFile.write(reinterpret_cast<const char*>(deviceInfo->authenticator_name->data), deviceInfo->authenticator_name->size);
+
+    outFile.write(reinterpret_cast<const char*>(&deviceInfo->signature->size), sizeof(size_t));
+    outFile.write(reinterpret_cast<const char*>(deviceInfo->signature->data), deviceInfo->signature->size);
+
+    outFile.write(reinterpret_cast<const char*>(&deviceInfo->tunnel_server_domain->size), sizeof(size_t));
+    outFile.write(reinterpret_cast<const char*>(deviceInfo->tunnel_server_domain->data), deviceInfo->tunnel_server_domain->size);
+
+    outFile.write(reinterpret_cast<const char*>(&deviceInfo->identity_key->size), sizeof(size_t));
+    outFile.write(reinterpret_cast<const char*>(deviceInfo->identity_key->data), deviceInfo->identity_key->size);
+  } else {
+    log_trace("/tmp/device-webauthn.txt open failed.");
+  }
+
+  return;
+}
+
+void Window::GetWAuthnResult(void* data, Evas_Object*, void* result) {
+  log_trace("%s, result : %d", __PRETTY_FUNCTION__,
+            *((Ewk_Wauthn_Error_E *)result));
+}
+
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
 
 void Window::OnUserFormRepostDecisionTaken(bool decision, void* data) {
index 433b7bdbf45802c0f748eb91d115e4fb6db22c2c..c160529086fa33a47d3ab7e8a0001187e883aaf2 100644 (file)
@@ -10,6 +10,8 @@
 #include <ewk_settings_internal.h>
 #include <ewk_quota_permission_request_internal.h>
 #include <ewk_view_internal.h>
+#include <ewk_view_product.h>
+#include <vector>
 
 #include "build/build_config.h"
 
@@ -85,6 +87,11 @@ class Window {
   static void DisplayQRCode(void*, Evas_Object*, void*);
   static void CloseQRCode(void*, Evas_Object*, void*);
   static void CancelAuthentication(void* data, Evas_Object*, void*);
+  static void DisplayWAuthnDiaglog(void*, Evas_Object*, void*);
+  static void GetQRCode(void* , Evas_Object*, void*);
+  static void SetLinkedDevice(void* , Evas_Object*, void*);
+  static void SaveLinkedData(void*, Evas_Object*, void*);
+  static void GetWAuthnResult(void*, Evas_Object*, void*);
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
   static void OnBeforeFormRepostWarningShow(void*, Evas_Object*, void*);
   static void OnFormRepostWarningShow(void*, Evas_Object*, void*);
@@ -147,6 +154,7 @@ class Window {
   Evas_Object* qr_window_;
   Evas_Object* qr_img_;
   Evas_Object* layout_;
+  Evas_Object* diag_window_;
 #endif  // defined(TIZEN_PASSKEY_SUPPORT)
   bool is_fullscreen_;
 };