Module: Booth::Testing::Support::VirtualAuthenticators

Includes:
Logging, Capybara::DSL
Defined in:
lib/booth/testing/support/virtual_authenticators.rb,
lib/booth/testing/support/virtual_authenticators/load.rb,
lib/booth/testing/support/virtual_authenticators/create.rb,
lib/booth/testing/support/virtual_authenticators/enable.rb,
lib/booth/testing/support/virtual_authenticators/destroy.rb

Overview

Factory and operations for VirtualAuthenticator objects. Provides methods to create and import authenticators across browser sessions.

Defined Under Namespace

Classes: Create, Destroy, Enable, Load

Class Method Summary collapse

Class Method Details

.authenticator_options(has_user_verification:) ⇒ Object



84
85
86
87
88
89
90
91
92
93
# File 'lib/booth/testing/support/virtual_authenticators.rb', line 84

def self.authenticator_options(has_user_verification:)
  {
    protocol: 'ctap2',
    transport: 'internal',
    hasResidentKey: true,
    hasUserVerification: has_user_verification,
    isUserVerified: true,
    automaticPresenceSimulation: true
  }
end

.create(has_user_verification: true) ⇒ Object

Creates a new virtual authenticator in the current browser session. Returns a VirtualAuthenticator object that can be used to pull credentials after WebAuthn registration.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/booth/testing/support/virtual_authenticators.rb', line 15

def self.create(has_user_verification: true)
  log { "Creating virtual authenticator in session #{Capybara.session_name}" }

  ::Booth::Testing::Support::VirtualAuthenticators::Enable.call

  result = send_cdp_message(
    'WebAuthn.addVirtualAuthenticator',
    options: authenticator_options(has_user_verification:),
  )

  authenticator_id = result['authenticatorId']

  unless authenticator_id
    raise 'Failed to create virtual authenticator - no authenticatorId in response'
  end

  log { "Created virtual authenticator #{authenticator_id}" }

  VirtualAuthenticator.new(
    authenticator_id:,
    has_user_verification:,
  )
end

.import(virtual_authenticator) ⇒ Object

Imports a VirtualAuthenticator into the current browser session. This creates a new authenticator and adds the credential with the stored private key.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/booth/testing/support/virtual_authenticators.rb', line 41

def self.import(virtual_authenticator)
  unless virtual_authenticator.credential_data
    raise 'VirtualAuthenticator has no credential data to import. Call pull() on the source authenticator first.'
  end

  log { "Importing authenticator into session #{Capybara.session_name}" }

  ::Booth::Testing::Support::VirtualAuthenticators::Enable.call

  result = send_cdp_message(
    'WebAuthn.addVirtualAuthenticator',
    options: authenticator_options(has_user_verification: virtual_authenticator.has_user_verification),
  )

  new_authenticator_id = result['authenticatorId']

  raise 'Failed to create authenticator for import' unless new_authenticator_id

  # Add the credential with stored data
  unless virtual_authenticator.rp_id
    raise 'IMPORT FAILED: No rp_id set on VirtualAuthenticator'
  end

  send_cdp_message(
    'WebAuthn.addCredential',
    authenticatorId: new_authenticator_id,
    credential: virtual_authenticator.credential_data,
    rpId: virtual_authenticator.rp_id,
  )

  log do
    "Imported credential #{virtual_authenticator.credential_id} into new authenticator #{new_authenticator_id}"
  end

  # Register the new authenticator ID for the current browser session
  virtual_authenticator.register_authenticator_id(new_authenticator_id,
                                                  Capybara.session_name.to_s)

  virtual_authenticator
end

.send_cdp_message(method, params) ⇒ Object



95
96
97
98
99
100
101
102
# File 'lib/booth/testing/support/virtual_authenticators.rb', line 95

def self.send_cdp_message(method, params)
  result = nil
  Capybara.current_session.driver.with_playwright_page do |playwright_page|
    cdp_session = playwright_page.context.new_cdp_session(playwright_page)
    result = cdp_session.send_message(method, params:)
  end
  result
end