Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 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
fb1ae8b
remove desktop only check
jmdembe Nov 19, 2024
55e61a6
Merge branch 'main' into jd-LG-14189-ft-setup-desktop
jmdembe Nov 25, 2024
30687e2
Update app/controllers/users/two_factor_authentication_setup_controll…
jmdembe Nov 25, 2024
36e6c74
add test that should not have been deleted
jmdembe Nov 25, 2024
db887a2
change definition name
jmdembe Nov 25, 2024
9bc8cbf
lintfix by changing name of method
jmdembe Nov 25, 2024
239ea00
add `User Registration: 2FA Setup visited` to `DESKTOP_FT_UNLOCK_SETU…
jmdembe Nov 25, 2024
bac7f21
change placement of test
jmdembe Nov 26, 2024
84ca898
remove duplicate test
jmdembe Nov 26, 2024
a663f27
Merge branch 'main' into jd-LG-14189-ft-setup-desktop
jmdembe Nov 26, 2024
067ca97
lintfix
jmdembe Nov 26, 2024
15f9bee
Merge branch 'main' into jd-LG-14189-ft-setup-desktop
jmdembe Nov 27, 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
11 changes: 10 additions & 1 deletion app/components/webauthn_input_component.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
# frozen_string_literal: true

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

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

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

Expand All @@ -26,6 +30,7 @@ def call
**tag_options,
**initial_hidden_tag_options,
'show-unsupported-passkey': show_unsupported_passkey?.presence,
'desktop-ft-unlock-option': show_desktop_ft_unlock_option?.presence,
)
end

Expand All @@ -36,4 +41,8 @@ def initial_hidden_tag_options
{ class: 'js' }
end
end

def show_desktop_ft_unlock_option?
desktop_ft_unlock_option? && I18n.locale == :en
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class TwoFactorAuthenticationSetupController < ApplicationController
include UserAuthenticator
include MfaSetupConcern
include AbTestingConcern
include ApplicationHelper

before_action :authenticate_user
before_action :confirm_user_authenticated_for_2fa_setup
Expand Down Expand Up @@ -68,6 +69,7 @@ def two_factor_options_presenter
show_skip_additional_mfa_link: show_skip_additional_mfa_link?,
after_mfa_setup_path:,
return_to_sp_cancel_path:,
desktop_ft_ab_test: in_ab_test_bucket?,
)
end

Expand All @@ -81,5 +83,9 @@ def two_factor_options_form_params
rescue ActionController::ParameterMissing
ActionController::Parameters.new(selection: [])
end

def in_ab_test_bucket?
ab_test_bucket(:DESKTOP_FT_UNLOCK_SETUP) == (:desktop_ft_unlock_option_shown)
end
end
end
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 @@ -88,7 +88,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 webauthn_auth_method
Expand Down
14 changes: 14 additions & 0 deletions app/javascript/packages/webauthn/webauthn-input-element.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ describe('WebauthnInputElement', () => {
});
});

context('as a part of A/B test', () => {
beforeEach(() => {
isWebauthnPasskeySupported.returns(false);
isWebauthnPlatformAvailable.resolves(true);
document.body.innerHTML = `<lg-webauthn-input desktop-ft-unlock-option hidden></lg-webauthn-input>`;
});

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

await waitFor(() => expect(element.hidden).to.be.false());
});
});

context('unsupported passkey shown', () => {
beforeEach(() => {
isWebauthnPasskeySupported.returns(false);
Expand Down
11 changes: 9 additions & 2 deletions app/javascript/packages/webauthn/webauthn-input-element.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import isWebauthnPasskeySupported from './is-webauthn-passkey-supported';
import isWebauthnPlatformAuthenticatorAvailable from './is-webauthn-platform-authenticator-available';
import isWebauthnPasskeySupported from './is-webauthn-passkey-supported';

export class WebauthnInputElement extends HTMLElement {
connectedCallback() {
this.toggleVisibleIfPasskeySupported();
}

get isOptedInToAbTest(): boolean {
return this.hasAttribute('desktop-ft-unlock-option');
}

get isPlatform(): boolean {
return this.hasAttribute('platform');
}
Expand All @@ -19,7 +23,10 @@ export class WebauthnInputElement extends HTMLElement {
return;
}

if (isWebauthnPasskeySupported() && (await isWebauthnPlatformAuthenticatorAvailable())) {
Comment thread
aduth marked this conversation as resolved.
if (
(isWebauthnPasskeySupported() || this.isOptedInToAbTest) &&
(await isWebauthnPlatformAuthenticatorAvailable())
) {
this.hidden = false;
} else if (this.showUnsupportedPasskey) {
this.hidden = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,26 @@ module TwoFactorAuthentication
class SetUpSelectionPresenter
include ActionView::Helpers::TranslationHelper

attr_reader :user, :piv_cac_required, :phishing_resistant_required, :user_agent
attr_reader :user,
:piv_cac_required,
:phishing_resistant_required,
:user_agent,
:desktop_ft_ab_test
alias_method :piv_cac_required?, :piv_cac_required
alias_method :phishing_resistant_required?, :phishing_resistant_required

def initialize(
user:,
piv_cac_required: false,
phishing_resistant_required: false,
user_agent: nil
user_agent: nil,
desktop_ft_ab_test: nil
)
@user = user
@piv_cac_required = piv_cac_required
@phishing_resistant_required = phishing_resistant_required
@user_agent = user_agent
@desktop_ft_ab_test = desktop_ft_ab_test
end

def render_in(view_context, &block)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def render_in(view_context, &block)
passkey_supported_only: true,
show_unsupported_passkey:
IdentityConfig.store.show_unsupported_passkey_platform_authentication_setup,
desktop_ft_unlock_option: desktop_ft_ab_test,
),
&block
)
Expand Down
8 changes: 6 additions & 2 deletions app/presenters/two_factor_options_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ class TwoFactorOptionsPresenter
:return_to_sp_cancel_path,
:phishing_resistant_required,
:piv_cac_required,
:user_agent
:user_agent,
:desktop_ft_ab_test

delegate :two_factor_enabled?, to: :mfa_policy
def initialize(
Expand All @@ -18,7 +19,8 @@ def initialize(
piv_cac_required: false,
show_skip_additional_mfa_link: true,
after_mfa_setup_path: nil,
return_to_sp_cancel_path: nil
return_to_sp_cancel_path: nil,
desktop_ft_ab_test: false
)
@user_agent = user_agent
@user = user
Expand All @@ -27,6 +29,7 @@ def initialize(
@show_skip_additional_mfa_link = show_skip_additional_mfa_link
@after_mfa_setup_path = after_mfa_setup_path
@return_to_sp_cancel_path = return_to_sp_cancel_path
@desktop_ft_ab_test = desktop_ft_ab_test
end

def options
Expand All @@ -47,6 +50,7 @@ def all_options_sorted
piv_cac_required: piv_cac_required?,
phishing_resistant_required: phishing_resistant_only?,
user_agent:,
desktop_ft_ab_test:,
)
end.
partition(&:recommended?).
Expand Down
2 changes: 2 additions & 0 deletions config/application.yml.default
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ database_worker_jobs_sslmode: 'verify-full'
database_worker_jobs_username: ''
deleted_user_accounts_report_configs: '[]'
deliver_mail_async: false
desktop_ft_unlock_setup_option_percent_tested: 0
development_mailer_deliver_method: letter_opener
disable_email_sending: true
disable_logout_get_request: true
Expand Down Expand Up @@ -457,6 +458,7 @@ development:
compromised_password_randomizer_value: 1
dashboard_api_token: test_token
dashboard_url: http://localhost:3001/api/service_providers
desktop_ft_unlock_setup_option_percent_tested: 100
doc_auth_selfie_desktop_test_mode: true
domain_name: localhost:3000
enable_rate_limiting: false
Expand Down
11 changes: 11 additions & 0 deletions config/initializers/ab_tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,15 @@ def self.all
shadow_mode_enabled: IdentityConfig.store.socure_idplus_shadow_mode_percent,
},
).freeze

DESKTOP_FT_UNLOCK_SETUP = AbTest.new(
experiment_name: 'Desktop F/T unlock setup',
should_log: [
'User Registration: 2FA Setup visited',
:webauthn_setup_submitted,
'Multi-Factor Authentication Setup',
].to_set,
buckets: { desktop_ft_unlock_option_shown:
IdentityConfig.store.desktop_ft_unlock_setup_option_percent_tested },
).freeze
end
1 change: 1 addition & 0 deletions lib/identity_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ def self.store
config.add(:database_worker_jobs_username, type: :string)
config.add(:deleted_user_accounts_report_configs, type: :json)
config.add(:deliver_mail_async, type: :boolean)
config.add(:desktop_ft_unlock_setup_option_percent_tested, type: :integer)
config.add(:development_mailer_deliver_method, type: :symbol, enum: [:file, :letter_opener])
config.add(:disable_email_sending, type: :boolean)
config.add(:disable_logout_get_request, type: :boolean)
Expand Down
22 changes: 20 additions & 2 deletions spec/components/webauthn_input_component_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,26 @@
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)
it 'does not render desktop-ft-unlock-option attribute' do
expect(rendered).to have_css('lg-webauthn-input:not([desktop-ft-unlock-option="false"])')
end

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

it 'does render desktop-ft-unlock-option attribute' do
expect(rendered).to have_css('lg-webauthn-input[desktop-ft-unlock-option="true"]')
end

context 'in a locale other than english' do
before do
I18n.locale = I18n.available_locales.sample
end

it 'does not render desktop-ft-unlock-option attribute' do
expect(rendered).to have_css('lg-webauthn-input:not([desktop-ft-unlock-option="false"])')
end
end
end

context 'with platform option' do
Expand Down
47 changes: 46 additions & 1 deletion spec/config/initializers/ab_tests_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@
end
end

describe '.RECOMMEND_WEBAUTHN_PLATFORM_FOR_SMS_USER' do
describe 'RECOMMEND_WEBAUTHN_PLATFORM_FOR_SMS_USER' do
let(:user) { create(:user) }

subject(:bucket) do
Expand Down Expand Up @@ -301,4 +301,49 @@
end
end
end

describe 'DESKTOP_FT_UNLOCK_SETUP' do
let(:user) { nil }
let(:user_session) { {} }

subject(:bucket) do
AbTests::DESKTOP_FT_UNLOCK_SETUP.bucket(
request: nil,
service_provider: nil,
session: nil,
user:,
user_session:,
)
end

context 'when A/B test is disabled' do
before do
allow(IdentityConfig.store).to receive(:desktop_ft_unlock_setup_option_percent_tested).
and_return(0)
reload_ab_tests
end

context 'when it would otherwise assign a bucket' do
let(:user) { build(:user) }

it 'does not return a bucket' do
expect(bucket).to be_nil
end
end
end

context 'when A/B test is enabled' do
before do
allow(IdentityConfig.store).to receive(:desktop_ft_unlock_setup_option_percent_tested).
and_return(100)
reload_ab_tests
end

let(:user) { build(:user) }

it 'returns a bucket' do
expect(bucket).not_to be_nil
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
describe 'GET index' do
let(:user) { create(:user) }

subject(:response) { get :index }

before do
stub_sign_in_before_2fa(user) if user
stub_analytics
Expand All @@ -19,6 +21,12 @@
)
end

it 'initializes presenter with false ab test bucket value' do
response

expect(assigns(:presenter).desktop_ft_ab_test).to be false
end

context 'with user having gov or mil email' do
let!(:federal_domain) { create(:federal_email_domain, name: 'gsa.gov') }
let(:user) do
Expand Down Expand Up @@ -101,6 +109,20 @@
expect(response).to redirect_to(user_two_factor_authentication_url)
end
end

context 'with user opted in to desktop ft unlock setup ab test' do
before do
allow(controller).to receive(:ab_test_bucket).with(
:DESKTOP_FT_UNLOCK_SETUP,
).and_return(:desktop_ft_unlock_option_shown)
end

it 'initializes presenter with ab test bucket value' do
response

expect(assigns(:presenter).desktop_ft_ab_test).to eq(true)
end
end
end

describe '#create' do
Expand Down
Loading