Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LG-14189: A/B test for allowing F/T setup and authentication on desktop #11347

Open
wants to merge 68 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
ac5bc0d
add desktop ab test information to form
jmdembe Oct 8, 2024
9b8a7c4
add a/b test configuration
jmdembe Oct 9, 2024
e4040c8
remove anything related to ab test bucket
jmdembe Oct 10, 2024
8fe5a6a
add passkey support on desktop
jmdembe Oct 10, 2024
a45733e
remove device supported check
jmdembe Oct 11, 2024
c208389
show_unsupported_passkey_platform_authentication_setup
jmdembe Oct 15, 2024
913c635
fix associated test
jmdembe Oct 15, 2024
8c459c0
changelog: Upcoming Features, desktop f/t unlock, A/B setup for deskt…
jmdembe Oct 15, 2024
87ac4ff
fix js test
jmdembe Oct 16, 2024
8adbf86
remove `deskton_ab_bucket?`; remove device does not support passkey t…
jmdembe Oct 16, 2024
df57b75
Merge branch 'main' into jd-LG-14189-ft-setup-desktop
jmdembe Oct 18, 2024
ca95f08
note to self
jmdembe Oct 21, 2024
b4f03ef
restore `isWebauthnPaskeySupported`
jmdembe Oct 23, 2024
5097d06
remove desktop qualifying fns, change logic for supported and availab…
jmdembe Oct 23, 2024
ec73d0d
restore `show_unsupported_passkey` functionality
jmdembe Oct 24, 2024
079a41d
lintfixes
jmdembe Oct 24, 2024
ceea8cc
fix tests
jmdembe Oct 24, 2024
7fb13fe
remove `@desktop_ab_test_bucket`
jmdembe Oct 24, 2024
024db55
rename to `desktop_ft_unlock_setup_option_percent_tested`
jmdembe Oct 25, 2024
15ee18b
Merge branch 'main' into jd-LG-14189-ft-setup-desktop
jmdembe Oct 28, 2024
bdf4e11
work on specs for A/B test
jmdembe Oct 28, 2024
ef44862
add tag so that functionality to show/hide can be in place
jmdembe Oct 28, 2024
6dfd842
toggle show based on english language
jmdembe Oct 28, 2024
c0f3bdf
restore conditional to show based on A/B enablement
jmdembe Oct 30, 2024
4143303
add javascript test
jmdembe Oct 31, 2024
a0fddd9
lintfix
jmdembe Oct 31, 2024
7308178
changelog: Upcoming Features, A/B test, create A/B test for desktop F…
jmdembe Oct 31, 2024
c144b2c
fix setup for desktop f/t unlock test
jmdembe Oct 31, 2024
b979abb
lintfixes
jmdembe Oct 31, 2024
eeb8788
track event when user is in a/b test but would not show otherwise
jmdembe Nov 4, 2024
cb8d18c
WIP: show/hide based on bucket
jmdembe Nov 6, 2024
e33c39e
Add component tests for desktop-ft-unlock-option
aduth Nov 6, 2024
264c559
Add controller specs for presenter assigns ab test value
aduth Nov 6, 2024
3e42ff7
Fix syntax error on assignment
aduth Nov 6, 2024
8086b69
Add feature test for A/B test setup on desktop
aduth Nov 6, 2024
7139ad2
fix js code and tests
jmdembe Nov 6, 2024
47b43bb
fix tests and associated logic
jmdembe Nov 7, 2024
bc48f5b
add desktop ft unlock capability on login options
jmdembe Nov 7, 2024
2303a72
set logic for `desktop-ft-unlock-option` class on sign in for test
jmdembe Nov 8, 2024
8a11578
show/hide based on value
jmdembe Nov 8, 2024
c589dab
fix error on line
jmdembe Nov 8, 2024
4848696
remove
jmdembe Nov 8, 2024
3252d7c
Merge branch 'main' into jd-LG-14189-ft-setup-desktop
jmdembe Nov 8, 2024
ae0222a
rename bucket, remove a/b test setup from log in files, pass bucket p…
jmdembe Nov 8, 2024
abb787d
lintfix
jmdembe Nov 8, 2024
d38d7ba
fix js test
jmdembe Nov 12, 2024
431645c
js lintfixes
jmdembe Nov 12, 2024
08396d9
fix F/T unlock show logic
jmdembe Nov 12, 2024
6c9a502
fix for f/t unlock logic?
jmdembe Nov 12, 2024
5371ae7
fix javascript test
jmdembe Nov 12, 2024
d8ffe62
remove unneeded logic from input element, remove `trackEvent`
jmdembe Nov 12, 2024
91612ca
lintfix, change default config number
jmdembe Nov 12, 2024
45b7d5a
more lintfixes
jmdembe Nov 12, 2024
8f7e059
clean up hidden and webauthn-input-element specs
jmdembe Nov 13, 2024
3db4df3
Merge branch 'main' into jd-LG-14189-ft-setup-desktop
jmdembe Nov 13, 2024
4be840a
Update app/components/webauthn_input_component.rb
jmdembe Nov 13, 2024
f63587e
Update app/javascript/packages/webauthn/webauthn-input-element.ts
jmdembe Nov 14, 2024
e2332aa
update test
jmdembe Nov 14, 2024
f232866
delete unused analytics event
jmdembe Nov 14, 2024
1ab1dbd
Merge branch 'main' into jd-LG-14189-ft-setup-desktop
jmdembe Nov 15, 2024
dea11a4
change analytics event, remove duplicate a/b test
jmdembe Nov 15, 2024
50296f6
do check for bucket type
jmdembe Nov 15, 2024
ec5f9a4
set up method if in bucket and test is running
jmdembe Nov 15, 2024
2102310
check for A/B test flag
jmdembe Nov 18, 2024
97575ed
For bucket check, change value type and fix test
jmdembe Nov 18, 2024
c992466
fix js test
jmdembe Nov 18, 2024
880030b
Merge branch 'main' into jd-LG-14189-ft-setup-desktop
jmdembe Nov 18, 2024
7920136
lintfix
jmdembe Nov 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions app/components/webauthn_input_component.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
# frozen_string_literal: true

class WebauthnInputComponent < BaseComponent
attr_reader :platform, :passkey_supported_only, :show_unsupported_passkey, :tag_options
attr_reader :platform, :passkey_supported_only, :tag_options

alias_method :platform?, :platform
alias_method :passkey_supported_only?, :passkey_supported_only
alias_method :show_unsupported_passkey?, :show_unsupported_passkey

def initialize(
platform: false,
passkey_supported_only: false,
show_unsupported_passkey: false,
**tag_options
)
@platform = platform
@passkey_supported_only = passkey_supported_only
@show_unsupported_passkey = show_unsupported_passkey
@tag_options = tag_options
end

Expand All @@ -25,7 +22,6 @@ def call
content,
**tag_options,
**initial_hidden_tag_options,
'show-unsupported-passkey': show_unsupported_passkey?.presence,
)
end

Expand Down
3 changes: 0 additions & 3 deletions app/components/webauthn_input_component.scss

This file was deleted.

2 changes: 1 addition & 1 deletion app/controllers/users/webauthn_setup_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def validate_existing_platform_authenticator
if platform_authenticator? && in_account_creation_flow? &&
current_user.webauthn_configurations.platform_authenticators.present?
redirect_to authentication_methods_setup_path
end
end
end

def platform_authenticator?
Expand Down
2 changes: 1 addition & 1 deletion app/javascript/packages/webauthn/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export { default as extractCredentials } from './extract-credentials';
export { default as verifyWebauthnDevice } from './verify-webauthn-device';
export { default as isExpectedWebauthnError } from './is-expected-error';
export { default as isWebauthnPlatformAuthenticatorAvailable } from './is-webauthn-platform-authenticator-available';
export { default as isWebauthnPasskeySupported } from './is-webauthn-passkey-supported';

export * from './converters';

export type { VerifyCredentialDescriptor } from './verify-webauthn-device';

This file was deleted.

30 changes: 0 additions & 30 deletions app/javascript/packages/webauthn/is-webauthn-passkey-supported.ts

This file was deleted.

40 changes: 0 additions & 40 deletions app/javascript/packages/webauthn/webauthn-input-element.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
import sinon from 'sinon';
import quibble from 'quibble';
import { waitFor } from '@testing-library/dom';
import type { IsWebauthnPasskeySupported } from './is-webauthn-passkey-supported';
import type { IsWebauthnPlatformAvailable } from './is-webauthn-platform-authenticator-available';

describe('WebauthnInputElement', () => {
const isWebauthnPasskeySupported = sinon.stub<
Parameters<IsWebauthnPasskeySupported>,
ReturnType<IsWebauthnPasskeySupported>
>();

const isWebauthnPlatformAvailable = sinon.stub<
Parameters<IsWebauthnPlatformAvailable>,
ReturnType<IsWebauthnPlatformAvailable>
>();

before(async () => {
quibble('./is-webauthn-passkey-supported', isWebauthnPasskeySupported);
quibble('./is-webauthn-platform-authenticator-available', isWebauthnPlatformAvailable);
await import('./webauthn-input-element');
});
Expand All @@ -25,42 +18,10 @@ describe('WebauthnInputElement', () => {
quibble.reset();
});

context('device does not support passkey', () => {
context('unsupported passkey not shown', () => {
beforeEach(() => {
isWebauthnPasskeySupported.returns(false);
isWebauthnPlatformAvailable.resolves(false);
document.body.innerHTML = `<lg-webauthn-input hidden></lg-webauthn-input>`;
});

it('stays hidden', () => {
const element = document.querySelector('lg-webauthn-input')!;

expect(element.hidden).to.be.true();
});
});

context('unsupported passkey shown', () => {
beforeEach(() => {
isWebauthnPasskeySupported.returns(false);
isWebauthnPlatformAvailable.resolves(false);
document.body.innerHTML = `<lg-webauthn-input show-unsupported-passkey hidden></lg-webauthn-input>`;
});

it('becomes visible, with modifier class', () => {
const element = document.querySelector('lg-webauthn-input')!;

expect(element.hidden).to.be.false();
expect(element.classList.contains('webauthn-input--unsupported-passkey')).to.be.true();
});
});
});

context('device supports passkey', () => {
context('unsupported publickeycredential not shown', () => {
beforeEach(() => {
isWebauthnPlatformAvailable.resolves(false);
isWebauthnPasskeySupported.returns(true);
document.body.innerHTML = `<lg-webauthn-input hidden></lg-webauthn-input>`;
});

Expand All @@ -73,7 +34,6 @@ describe('WebauthnInputElement', () => {

context('publickeycredential input is shown', () => {
beforeEach(() => {
isWebauthnPasskeySupported.returns(true);
isWebauthnPlatformAvailable.resolves(true);
document.body.innerHTML = `<lg-webauthn-input hidden></lg-webauthn-input>`;
});
Expand Down
6 changes: 2 additions & 4 deletions app/javascript/packages/webauthn/webauthn-input-element.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import isWebauthnPasskeySupported from './is-webauthn-passkey-supported';
import isWebauthnPlatformAuthenticatorAvailable from './is-webauthn-platform-authenticator-available';

export class WebauthnInputElement extends HTMLElement {
Expand All @@ -19,11 +18,10 @@ export class WebauthnInputElement extends HTMLElement {
return;
}

if (isWebauthnPasskeySupported() && (await isWebauthnPlatformAuthenticatorAvailable())) {
aduth marked this conversation as resolved.
Show resolved Hide resolved
if (await isWebauthnPlatformAuthenticatorAvailable()) {
this.hidden = false;
} else if (this.showUnsupportedPasskey) {
} else {
this.hidden = false;
this.classList.add('webauthn-input--unsupported-passkey');
}
}
}
Expand Down
20 changes: 0 additions & 20 deletions app/javascript/packs/platform-authenticator-available.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ def render_in(view_context, &block)
WebauthnInputComponent.new(
platform: true,
passkey_supported_only: true,
show_unsupported_passkey:
IdentityConfig.store.show_unsupported_passkey_platform_authentication_setup,
),
&block
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ def render_in(view_context, &block)
WebauthnInputComponent.new(
platform: true,
passkey_supported_only: false,
show_unsupported_passkey: false,
),
&block
)
Expand Down
3 changes: 1 addition & 2 deletions config/application.yml.default
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ session_timeout_warning_seconds: 150
session_total_duration_timeout_in_minutes: 720
short_term_phone_otp_max_attempt_window_in_seconds: 10
short_term_phone_otp_max_attempts: 2
show_unsupported_passkey_platform_authentication_setup: false
show_desktop_ft_unlock_setup_option_percent_tested: 0
show_user_attribute_deprecation_warnings: false
sign_in_recaptcha_percent_tested: 0
sign_in_recaptcha_score_threshold: 0.0
Expand Down Expand Up @@ -441,7 +441,6 @@ development:
scrypt_cost: 10000$8$1$
secret_key_base: development_secret_key_base
session_encryption_key: 27bad3c25711099429c1afdfd1890910f3b59f5a4faec1c85e945cb8b02b02f261ba501d99cfbb4fab394e0102de6fecf8ffe260f322f610db3e96b2a775c120
show_unsupported_passkey_platform_authentication_setup: true
sign_in_recaptcha_percent_tested: 100
sign_in_recaptcha_score_threshold: 0.3
skip_encryption_allowed_list: '["urn:gov:gsa:SAML:2.0.profiles:sp:sso:localhost"]'
Expand Down
16 changes: 16 additions & 0 deletions config/initializers/ab_tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,20 @@ def self.all
SecureRandom.alphanumeric(8)
end
end.freeze

DESKTOP_FT_UNLOCK_SETUP = AbTest.new(
experiment_name: 'Desktop F/T unlock setup',
should_log: [
'WebAuthn Setup Visited',
'Multi-Factor Authentication Setup',
].to_set,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation metrics for this test require us to measure how many people successfully set up WebAuthn. I think we'll need to revise this list of events (e.g. "submitted" event rather than "visited").

It'd be a good idea to mock up the CloudWatch queries we expect to run to perform the metrics validation.

buckets: { desktop_ft_unlock:
IdentityConfig.store.show_desktop_ft_unlock_setup_option_percent_tested },
) do |user:, user_session:, **|
if user_session&.[](:platform_authenticator_available) == false
nil
else
user.uuid
end
end.freeze
end
2 changes: 1 addition & 1 deletion lib/identity_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ def self.store
config.add(:session_timeout_in_minutes, type: :integer)
config.add(:session_timeout_warning_seconds, type: :integer)
config.add(:session_total_duration_timeout_in_minutes, type: :integer)
config.add(:show_unsupported_passkey_platform_authentication_setup, type: :boolean)
config.add(:show_desktop_ft_unlock_setup_option_percent_tested, type: :integer)
config.add(:show_user_attribute_deprecation_warnings, type: :boolean)
config.add(:short_term_phone_otp_max_attempts, type: :integer)
config.add(:short_term_phone_otp_max_attempt_window_in_seconds, type: :integer)
Expand Down
15 changes: 0 additions & 15 deletions spec/components/webauthn_input_component_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@
expect(component.passkey_supported_only?).to eq(false)
end

it 'exposes boolean alias for show_unsupported_passkey option' do
expect(component.show_unsupported_passkey?).to eq(false)
end

context 'with platform option' do
context 'with platform option false' do
let(:options) { super().merge(platform: false) }
Expand Down Expand Up @@ -67,17 +63,6 @@
)
end
end

context 'with show_unsupported_passkey option true' do
let(:options) { super().merge(show_unsupported_passkey: true) }

it 'renders with show-unsupported-passkey attribute' do
expect(rendered).to have_css(
'lg-webauthn-input[hidden][show-unsupported-passkey]',
visible: false,
)
end
end
end
end
end
Expand Down