diff --git a/.rubocop_todo/gitlab/feature_flag_without_actor.yml b/.rubocop_todo/gitlab/feature_flag_without_actor.yml index a6a6e49b78abaf580f8078452dd5844b2236b0cc..8e916214d9ed8f648893e558cbdf15dd2f25d83d 100644 --- a/.rubocop_todo/gitlab/feature_flag_without_actor.yml +++ b/.rubocop_todo/gitlab/feature_flag_without_actor.yml @@ -61,7 +61,6 @@ Gitlab/FeatureFlagWithoutActor: - 'app/views/admin/application_settings/_invitation_flow_enforcement.html.haml' - 'app/views/admin/application_settings/general.html.haml' - 'app/views/admin/projects/show.html.haml' - - 'app/views/authentication/_register.html.haml' - 'app/views/devise/shared/_language_switcher.html.haml' - 'app/views/explore/projects/_projects.html.haml' - 'app/views/layouts/notify.html.haml' diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index 996e25559384b12b3331497c734b8411c50fe351..cf947d53ccc3b8ec8efa580fa1f9dea3d612cd33 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -19,7 +19,7 @@ def create ::Users::ValidateManualOtpService.new(current_user).execute(params[:pin_code]) validated = (otp_validation_result[:status] == :success) - if validated && current_user.otp_backup_codes? && Feature.enabled?(:webauthn_without_totp) + if validated && current_user.otp_backup_codes? ActiveSession.destroy_all_but_current(current_user, session) Users::UpdateService.new(current_user, user: current_user, otp_required_for_login: true).execute! redirect_to profile_two_factor_auth_path, notice: _("Your Time-based OTP device was registered!") @@ -50,21 +50,16 @@ def create_webauthn if @webauthn_registration.persisted? session.delete(:challenge) - if Feature.enabled?(:webauthn_without_totp) - - if current_user.otp_backup_codes? - redirect_to profile_two_factor_auth_path, notice: notice - else + if current_user.otp_backup_codes? + redirect_to profile_two_factor_auth_path, notice: notice + else - Users::UpdateService.new(current_user, user: current_user).execute! do |user| - @codes = current_user.generate_otp_backup_codes! - end - helpers.dismiss_two_factor_auth_recovery_settings_check - flash[:notice] = notice - render 'create' + Users::UpdateService.new(current_user, user: current_user).execute! do |user| + @codes = current_user.generate_otp_backup_codes! end - else - redirect_to profile_two_factor_auth_path, notice: notice + helpers.dismiss_two_factor_auth_recovery_settings_check + flash[:notice] = notice + render 'create' end else @qr_code = build_qr_code @@ -131,7 +126,6 @@ def update_current_user_otp! end def validate_current_password - return if Feature.disabled?(:webauthn_without_totp) && params[:action] == 'create_webauthn' return if current_user.valid_password?(params[:current_password]) current_user.increment_failed_attempts! diff --git a/app/views/authentication/_register.html.haml b/app/views/authentication/_register.html.haml deleted file mode 100644 index f8a03f085ff3e877411c30dad09503e372e6e7ca..0000000000000000000000000000000000000000 --- a/app/views/authentication/_register.html.haml +++ /dev/null @@ -1,50 +0,0 @@ -- if Feature.enabled?(:webauthn_without_totp) - #js-device-registration{ data: device_registration_data(current_password_required: current_password_required?, target_path: target_path, webauthn_error: @webauthn_error) } -- else - #js-register-token-2fa - - -# haml-lint:disable InlineJavaScript - %script#js-register-2fa-message{ type: "text/template" } - %p <%= message %> - - -# haml-lint:disable InlineJavaScript - %script#js-register-token-2fa-setup{ type: "text/template" } - - if current_user.two_factor_otp_enabled? - .row.gl-mb-3 - .col-md-5 - = render Pajamas::ButtonComponent.new(variant: :confirm, - button_options: { id: 'js-setup-token-2fa-device' }) do - = _("Set up new device") - .col-md-7 - %p= _("Your device needs to be set up. Plug it in (if needed) and click the button on the left.") - - else - .row.gl-mb-3 - .col-md-4 - = render Pajamas::ButtonComponent.new(variant: :confirm, - disabled: true, - button_options: { id: 'js-setup-token-2fa-device' }) do - = _("Set up new device") - .col-md-8 - %p= _("You need to register a two-factor authentication app before you can set up a device.") - - -# haml-lint:disable InlineJavaScript - %script#js-register-token-2fa-error{ type: "text/template" } - %div - %p - %span <%= error_message %> (<%= error_name %>) - = render Pajamas::ButtonComponent.new(button_options: { id: 'js-token-2fa-try-again' }) do - = _("Try again?") - - -# haml-lint:disable InlineJavaScript - %script#js-register-token-2fa-registered{ type: "text/template" } - .row.gl-mb-3 - .col-md-12 - %p= _("Your device was successfully set up! Give it a name and register it with the GitLab server.") - = form_tag(target_path, method: :post) do - .row.gl-mb-3 - .col-md-3 - = text_field_tag 'device_registration[name]', nil, class: 'form-control', placeholder: _("Pick a name") - .col-md-3 - = hidden_field_tag 'device_registration[device_response]', nil, class: 'form-control', required: true, id: "js-device-response" - = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm) do - = _("Register device") diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 0b7b27bfdd892979d3f40aa915bd37c6d900fcbe..836e452c007cfc4aad60156c042fb16062942669 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -77,14 +77,11 @@ %p = _('Set up a hardware device to enable two-factor authentication (2FA).') %p - - if Feature.enabled?(:webauthn_without_totp) - = _("Not all browsers support WebAuthn. You must save your recovery codes after you first register a two-factor authenticator to be able to sign in, even from an unsupported browser.") - - else - = _("Not all browsers support WebAuthn. Therefore, we require that you set up a two-factor authentication app first. That way you'll always be able to sign in, even from an unsupported browser.") + = _("Not all browsers support WebAuthn. You must save your recovery codes after you first register a two-factor authenticator to be able to sign in, even from an unsupported browser.") .col-lg-8 - if @webauthn_registration.errors.present? = form_errors(@webauthn_registration) - = render "authentication/register", target_path: create_webauthn_profile_two_factor_auth_path + #js-device-registration{ data: device_registration_data(current_password_required: current_password_required?, target_path: create_webauthn_profile_two_factor_auth_path, webauthn_error: @webauthn_error) } %hr diff --git a/config/feature_flags/development/webauthn_without_totp.yml b/config/feature_flags/development/webauthn_without_totp.yml deleted file mode 100644 index c4174e7f7c0033ca5613a2d100ee00cb48b087db..0000000000000000000000000000000000000000 --- a/config/feature_flags/development/webauthn_without_totp.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: webauthn_without_totp -introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107438" -rollout_issue_url: "https://gitlab.com/gitlab-org/gitlab/-/issues/386270" -milestone: '15.9' -type: development -group: group::authentication -default_enabled: false diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md index 5cabd6cca0466cc95443e7f3f125179ab45a3330..23cb0e71e81cf1fc109c9991d3c19f19d1a78db0 100644 --- a/doc/user/profile/account/two_factor_authentication.md +++ b/doc/user/profile/account/two_factor_authentication.md @@ -277,12 +277,6 @@ Configure FortiToken Cloud in GitLab. On your GitLab server: ### Set up a WebAuthn device -> - Optional one-time password authentication for WebAuthn devices [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/378844) in GitLab 15.10 [with a flag](../../../administration/feature_flags.md) named `webauthn_without_totp`. [Enabled on GitLab.com and self-managed by default](https://gitlab.com/gitlab-org/gitlab/-/issues/232671). - -FLAG: -On self-managed GitLab, by default, optional one-time password authentication for WebAuthn devices is not available. To enable the feature, an administrator can [enable the feature flag](../../../administration/feature_flags.md) named `webauthn_without_totp`. -On GitLab.com, this feature is available. On GitLab Dedicated, this feature is not available. - WebAuthn is [supported by](https://caniuse.com/#search=webauthn) the following: - Desktop browsers: diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 9e9e071d7e9011506557dae88a3d591133682310..f2f98883421a0cbf973d2c103a832dce96b4d61a 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -36445,9 +36445,6 @@ msgstr "" msgid "Not adopted" msgstr "" -msgid "Not all browsers support WebAuthn. Therefore, we require that you set up a two-factor authentication app first. That way you'll always be able to sign in, even from an unsupported browser." -msgstr "" - msgid "Not all browsers support WebAuthn. You must save your recovery codes after you first register a two-factor authenticator to be able to sign in, even from an unsupported browser." msgstr "" @@ -39803,9 +39800,6 @@ msgstr "" msgid "Pick a group or project" msgstr "" -msgid "Pick a name" -msgstr "" - msgid "Pipeline" msgstr "" @@ -63539,9 +63533,6 @@ msgstr "" msgid "You need permission." msgstr "" -msgid "You need to register a two-factor authentication app before you can set up a device." -msgstr "" - msgid "You need to set terms to be enforced" msgstr "" diff --git a/spec/controllers/profiles/two_factor_auths_controller_spec.rb b/spec/controllers/profiles/two_factor_auths_controller_spec.rb index 3ff71fc12171e6d3af06e68e462e7583ed0ebbfc..046a5f5959ee1999e9e2ec40480f6795214b1436 100644 --- a/spec/controllers/profiles/two_factor_auths_controller_spec.rb +++ b/spec/controllers/profiles/two_factor_auths_controller_spec.rb @@ -202,51 +202,6 @@ def go expect(ActiveSession).to receive(:destroy_all_but_current) go end - - context 'when webauthn_without_totp flag is disabled' do - before do - stub_feature_flags(webauthn_without_totp: false) - expect(user).to receive(:validate_and_consume_otp!).with(pin).and_return(true) - end - - it 'enables 2fa for the user' do - go - - user.reload - expect(user).to be_two_factor_enabled - end - - it 'presents plaintext codes for the user to save' do - expect(user).to receive(:generate_otp_backup_codes!).and_return(%w[a b c]) - - go - - expect(assigns[:codes]).to match_array %w[a b c] - end - - it 'calls to delete other sessions' do - expect(ActiveSession).to receive(:destroy_all_but_current) - - go - end - - it 'dismisses the `TWO_FACTOR_AUTH_RECOVERY_SETTINGS_CHECK` callout' do - expect(controller.helpers).to receive(:dismiss_two_factor_auth_recovery_settings_check) - - go - end - - it 'renders create' do - go - expect(response).to render_template(:create) - end - - it 'renders create even if backup code already exists' do - expect(user).to receive(:otp_backup_codes?).and_return(true) - go - expect(response).to render_template(:create) - end - end end context 'with invalid pin' do @@ -364,22 +319,6 @@ def device_response expect(flash[:notice]).to match(/Your WebAuthn device was registered!/) end end - - context "when the feature flag 'webauthn_without_totp' is disabled" do - before do - stub_feature_flags(webauthn_without_totp: false) - session[:challenge] = challenge - end - - let(:params) { { device_registration: { name: 'touch id', device_response: device_response } } } # rubocop:disable Rails/SaveBang - - it "does not validate the current_password" do - go - - expect(flash[:notice]).to match(/Your WebAuthn device was registered!/) - expect(response).to redirect_to(profile_two_factor_auth_path) - end - end end describe 'DELETE destroy' do diff --git a/spec/features/webauthn_spec.rb b/spec/features/webauthn_spec.rb index 72463a0b9ab06c29c18098d5e01cc9e90bd173a3..16fb71417db2a69b9084726c1c4c53c668b0b054 100644 --- a/spec/features/webauthn_spec.rb +++ b/spec/features/webauthn_spec.rb @@ -10,67 +10,65 @@ WebAuthn.configuration.origin = app_id end - context 'when the webauth_without_totp feature flag is enabled' do - # Some of the shared tests don't apply. After removing U2F support and the `webauthn_without_totp` feature flag, refactor the shared tests. - # TODO: it_behaves_like 'hardware device for 2fa', 'WebAuthn' + it_behaves_like 'hardware device for 2fa', 'WebAuthn' - describe 'registration' do - let(:user) { create(:user) } + describe 'registration' do + let(:user) { create(:user) } - before do - gitlab_sign_in(user) - end - - it 'shows an error when using a wrong password' do - visit profile_account_path - - # First device - enable_two_factor_authentication - webauthn_device_registration(password: 'fake') - expect(page).to have_content(_('You must provide a valid current password.')) - end + before do + gitlab_sign_in(user) + end - it 'allows registering more than one device' do - visit profile_account_path + it 'shows an error when using a wrong password' do + visit profile_account_path - # First device - enable_two_factor_authentication - first_device = webauthn_device_registration(password: user.password) - expect(page).to have_content('Your WebAuthn device was registered!') - copy_recovery_codes - manage_two_factor_authentication + # First device + enable_two_factor_authentication + webauthn_device_registration(password: 'fake') + expect(page).to have_content(_('You must provide a valid current password.')) + end - # Second device - second_device = webauthn_device_registration(name: 'My other device', password: user.password) - expect(page).to have_content('Your WebAuthn device was registered!') + it 'allows registering more than one device' do + visit profile_account_path - expect(page).to have_content(first_device.name) - expect(page).to have_content(second_device.name) - expect(WebauthnRegistration.count).to eq(2) - end + # First device + enable_two_factor_authentication + first_device = webauthn_device_registration(password: user.password) + expect(page).to have_content('Your WebAuthn device was registered!') + copy_recovery_codes + manage_two_factor_authentication - it 'allows the same device to be registered for multiple users' do - # First user - visit profile_account_path - enable_two_factor_authentication - webauthn_device = webauthn_device_registration(password: user.password) - expect(page).to have_content('Your WebAuthn device was registered!') - gitlab_sign_out + # Second device + second_device = webauthn_device_registration(name: 'My other device', password: user.password) + expect(page).to have_content('Your WebAuthn device was registered!') - # Second user - user = create(:user) - gitlab_sign_in(user) - visit profile_account_path - enable_two_factor_authentication - webauthn_device_registration(webauthn_device: webauthn_device, name: 'My other device', password: user.password) - expect(page).to have_content('Your WebAuthn device was registered!') + expect(page).to have_content(first_device.name) + expect(page).to have_content(second_device.name) + expect(WebauthnRegistration.count).to eq(2) + end - expect(WebauthnRegistration.count).to eq(2) - end + it 'allows the same device to be registered for multiple users' do + # First user + visit profile_account_path + enable_two_factor_authentication + webauthn_device = webauthn_device_registration(password: user.password) + expect(page).to have_content('Your WebAuthn device was registered!') + gitlab_sign_out + + # Second user + user = create(:user) + gitlab_sign_in(user) + visit profile_account_path + enable_two_factor_authentication + webauthn_device_registration(webauthn_device: webauthn_device, name: 'My other device', password: user.password) + expect(page).to have_content('Your WebAuthn device was registered!') + + expect(WebauthnRegistration.count).to eq(2) + end - context 'when there are form errors' do - let(:mock_register_js) do - <<~JS + context 'when there are form errors' do + let(:mock_register_js) do + <<~JS const mockResponse = { type: 'public-key', id: '', @@ -82,145 +80,37 @@ getClientExtensionResults: () => {}, }; navigator.credentials.create = () => Promise.resolve(mockResponse); - JS - end - - it "doesn't register the device if there are errors" do - visit profile_account_path - enable_two_factor_authentication - - # Have the "webauthn device" respond with bad data - page.execute_script(mock_register_js) - click_on _('Set up new device') - webauthn_fill_form_and_submit(password: user.password) - expect(page).to have_content(_('Your WebAuthn device did not send a valid JSON response.')) - - expect(WebauthnRegistration.count).to eq(0) - end - - it 'allows retrying registration' do - visit profile_account_path - enable_two_factor_authentication - - # Failed registration - page.execute_script(mock_register_js) - click_on _('Set up new device') - webauthn_fill_form_and_submit(password: user.password) - expect(page).to have_content(_('Your WebAuthn device did not send a valid JSON response.')) - - # Successful registration - webauthn_device_registration(password: user.password) - - expect(page).to have_content('Your WebAuthn device was registered!') - expect(WebauthnRegistration.count).to eq(1) - end + JS end - end - end - context 'when the webauth_without_totp feature flag is disabled' do - before do - stub_feature_flags(webauthn_without_totp: false) - end - - it_behaves_like 'hardware device for 2fa', 'WebAuthn' - - describe 'registration' do - let(:user) { create(:user) } - - before do - gitlab_sign_in(user) - user.update_attribute(:otp_required_for_login, true) - end - - describe 'when 2FA via OTP is enabled' do - it 'allows registering more than one device' do - visit profile_account_path - - # First device - manage_two_factor_authentication - first_device = register_webauthn_device - expect(page).to have_content('Your WebAuthn device was registered') - - # Second device - second_device = register_webauthn_device(name: 'My other device') - expect(page).to have_content('Your WebAuthn device was registered') - - expect(page).to have_content(first_device.name) - expect(page).to have_content(second_device.name) - expect(WebauthnRegistration.count).to eq(2) - end - end - - it 'allows the same device to be registered for multiple users' do - # First user + it "doesn't register the device if there are errors" do visit profile_account_path - manage_two_factor_authentication - webauthn_device = register_webauthn_device - expect(page).to have_content('Your WebAuthn device was registered') - gitlab_sign_out + enable_two_factor_authentication - # Second user - user = create(:user) - gitlab_sign_in(user) - user.update_attribute(:otp_required_for_login, true) - visit profile_account_path - manage_two_factor_authentication - register_webauthn_device(webauthn_device, name: 'My other device') - expect(page).to have_content('Your WebAuthn device was registered') + # Have the "webauthn device" respond with bad data + page.execute_script(mock_register_js) + click_on _('Set up new device') + webauthn_fill_form_and_submit(password: user.password) + expect(page).to have_content(_('Your WebAuthn device did not send a valid JSON response.')) - expect(WebauthnRegistration.count).to eq(2) + expect(WebauthnRegistration.count).to eq(0) end - context 'when there are form errors' do - let(:mock_register_js) do - <<~JS - const mockResponse = { - type: 'public-key', - id: '', - rawId: '', - response: { - clientDataJSON: '', - attestationObject: '', - }, - getClientExtensionResults: () => {}, - }; - navigator.credentials.create = function(_) {return Promise.resolve(mockResponse);} - JS - end - - it "doesn't register the device if there are errors" do - visit profile_account_path - manage_two_factor_authentication - - # Have the "webauthn device" respond with bad data - page.execute_script(mock_register_js) - click_on 'Set up new device' - expect(page).to have_content('Your device was successfully set up') - click_on 'Register device' - - expect(WebauthnRegistration.count).to eq(0) - expect(page).to have_content('The form contains the following error') - expect(page).to have_content('did not send a valid JSON response') - end - - it 'allows retrying registration' do - visit profile_account_path - manage_two_factor_authentication + it 'allows retrying registration' do + visit profile_account_path + enable_two_factor_authentication - # Failed registration - page.execute_script(mock_register_js) - click_on 'Set up new device' - expect(page).to have_content('Your device was successfully set up') - click_on 'Register device' - expect(page).to have_content('The form contains the following error') + # Failed registration + page.execute_script(mock_register_js) + click_on _('Set up new device') + webauthn_fill_form_and_submit(password: user.password) + expect(page).to have_content(_('Your WebAuthn device did not send a valid JSON response.')) - # Successful registration - register_webauthn_device + # Successful registration + webauthn_device_registration(password: user.password) - expect(page).to have_content('Your WebAuthn device was registered') - expect(WebauthnRegistration.count).to eq(1) - end + expect(page).to have_content('Your WebAuthn device was registered!') + expect(WebauthnRegistration.count).to eq(1) end end end @@ -280,8 +170,9 @@ current_user = create(:user) gitlab_sign_in(current_user) visit profile_account_path - manage_two_factor_authentication - register_webauthn_device(webauthn_device) + enable_two_factor_authentication + webauthn_device_registration(webauthn_device: webauthn_device, password: current_user.password) + copy_recovery_codes gitlab_sign_out # Try authenticating user with the same WebAuthn device @@ -311,7 +202,7 @@ # Authenticate as both devices [first_device, second_device].each do |device| gitlab_sign_in(user) - # register_webauthn_device(device) + device.respond_to_webauthn_authentication expect(page).to have_css('.sign-out-link', visible: false) diff --git a/spec/frontend/authentication/webauthn/register_spec.js b/spec/frontend/authentication/webauthn/register_spec.js deleted file mode 100644 index 5f0691782a7d99126ca4c8c15a66b5d1e719e0f7..0000000000000000000000000000000000000000 --- a/spec/frontend/authentication/webauthn/register_spec.js +++ /dev/null @@ -1,135 +0,0 @@ -import $ from 'jquery'; -import htmlWebauthnRegister from 'test_fixtures/webauthn/register.html'; -import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; -import { trimText } from 'helpers/text_helper'; -import setWindowLocation from 'helpers/set_window_location_helper'; -import waitForPromises from 'helpers/wait_for_promises'; -import WebAuthnRegister from '~/authentication/webauthn/register'; -import MockWebAuthnDevice from './mock_webauthn_device'; -import { useMockNavigatorCredentials } from './util'; - -describe('WebAuthnRegister', () => { - useMockNavigatorCredentials(); - - const mockResponse = { - type: 'public-key', - id: '', - rawId: '', - response: { - clientDataJSON: '', - attestationObject: '', - }, - getClientExtensionResults: () => {}, - }; - let webAuthnDevice; - let container; - let component; - - beforeEach(() => { - setHTMLFixture(htmlWebauthnRegister); - webAuthnDevice = new MockWebAuthnDevice(); - container = $('#js-register-token-2fa'); - component = new WebAuthnRegister(container, { - options: { - rp: '', - user: { - id: '', - name: '', - displayName: '', - }, - challenge: '', - pubKeyCredParams: '', - }, - }); - component.start(); - }); - - afterEach(() => { - resetHTMLFixture(); - }); - - const findSetupButton = () => container.find('#js-setup-token-2fa-device'); - const findMessage = () => container.find('p'); - const findDeviceResponse = () => container.find('#js-device-response'); - const findRetryButton = () => container.find('#js-token-2fa-try-again'); - - it('shows setup button', () => { - expect(trimText(findSetupButton().text())).toBe('Set up new device'); - }); - - describe('when unsupported', () => { - const { PublicKeyCredential } = window; - - beforeEach(() => { - delete window.credentials; - window.PublicKeyCredential = undefined; - }); - - afterEach(() => { - window.PublicKeyCredential = PublicKeyCredential; - }); - - it.each` - httpsEnabled | expectedText - ${false} | ${'WebAuthn only works with HTTPS-enabled websites'} - ${true} | ${'Please use a supported browser, e.g. Chrome (67+) or Firefox'} - `('when https is $httpsEnabled', ({ httpsEnabled, expectedText }) => { - setWindowLocation(`${httpsEnabled ? 'https:' : 'http:'}//localhost`); - component.start(); - - expect(findMessage().text()).toContain(expectedText); - }); - }); - - describe('when setup', () => { - beforeEach(() => { - findSetupButton().trigger('click'); - }); - - it('shows in progress message', () => { - expect(findMessage().text()).toContain('Trying to communicate with your device'); - }); - - it('registers device', () => { - webAuthnDevice.respondToRegisterRequest(mockResponse); - - return waitForPromises().then(() => { - expect(findMessage().text()).toContain('Your device was successfully set up!'); - expect(findDeviceResponse().val()).toBe(JSON.stringify(mockResponse)); - }); - }); - - it.each` - errorName | expectedText - ${'NotSupportedError'} | ${'Your device is not compatible with GitLab'} - ${'NotAllowedError'} | ${'There was a problem communicating with your device'} - `('when fails with $errorName', ({ errorName, expectedText }) => { - webAuthnDevice.rejectRegisterRequest(new DOMException('', errorName)); - - return waitForPromises().then(() => { - expect(findMessage().text()).toContain(expectedText); - expect(findRetryButton().length).toBe(1); - }); - }); - - it('can retry', () => { - webAuthnDevice.respondToRegisterRequest({ - errorCode: 'error!', - }); - - return waitForPromises() - .then(() => { - findRetryButton().click(); - - expect(findMessage().text()).toContain('Trying to communicate with your device'); - - webAuthnDevice.respondToRegisterRequest(mockResponse); - return waitForPromises(); - }) - .then(() => { - expect(findMessage().text()).toContain('Your device was successfully set up!'); - expect(findDeviceResponse().val()).toBe(JSON.stringify(mockResponse)); - }); - }); - }); -}); diff --git a/spec/frontend/fixtures/webauthn.rb b/spec/frontend/fixtures/webauthn.rb index ed6180118f008ff9502755fec3c11b6483393420..77927c7310aca2209c36958c75e16508c3fe0336 100644 --- a/spec/frontend/fixtures/webauthn.rb +++ b/spec/frontend/fixtures/webauthn.rb @@ -23,22 +23,4 @@ expect(response).to be_successful end end - - describe Profiles::TwoFactorAuthsController, '(JavaScript fixtures)', type: :controller do - render_views - - before do - sign_in(user) - allow_next_instance_of(Profiles::TwoFactorAuthsController) do |instance| - allow(instance).to receive(:build_qr_code).and_return('qrcode:blackandwhitesquares') - end - stub_feature_flags(webauthn_without_totp: false) - end - - it 'webauthn/register.html' do - get :show - - expect(response).to be_successful - end - end end diff --git a/spec/support/helpers/features/two_factor_helpers.rb b/spec/support/helpers/features/two_factor_helpers.rb index e0469091d96cfd648433d4458bb96ac316fd3404..6cdd29be8821cb3a7dfa3d7001960973f3695911 100644 --- a/spec/support/helpers/features/two_factor_helpers.rb +++ b/spec/support/helpers/features/two_factor_helpers.rb @@ -29,17 +29,6 @@ def manage_two_factor_authentication end # Registers webauthn device via UI - # Remove after `webauthn_without_totp` feature flag is deleted. - def register_webauthn_device(webauthn_device = nil, name: 'My device') - webauthn_device ||= FakeWebauthnDevice.new(page, name) - webauthn_device.respond_to_webauthn_registration - click_on 'Set up new device' - expect(page).to have_content('Your device was successfully set up') - fill_in 'Pick a name', with: name - click_on 'Register device' - webauthn_device - end - def webauthn_device_registration(webauthn_device: nil, name: 'My device', password: 'fake') webauthn_device ||= FakeWebauthnDevice.new(page, name) webauthn_device.respond_to_webauthn_registration diff --git a/spec/support/shared_examples/features/2fa_shared_examples.rb b/spec/support/shared_examples/features/2fa_shared_examples.rb index 7c46dd2aba2ab980b59fdf960b90ef5f017989ad..1fa4b69e2a90fc92a03513bb4a0bc92d2f666bb1 100644 --- a/spec/support/shared_examples/features/2fa_shared_examples.rb +++ b/spec/support/shared_examples/features/2fa_shared_examples.rb @@ -7,7 +7,7 @@ def register_device(device_type, **kwargs) case device_type when 'WebAuthn' - register_webauthn_device(**kwargs) + webauthn_device_registration(**kwargs) else raise "Unknown device type #{device_type}" end @@ -26,11 +26,16 @@ def register_device(device_type, **kwargs) user.update_attribute(:otp_required_for_login, false) end - it 'does not allow registering a new device' do + it 'allows registering a new device' do visit profile_account_path - click_on 'Enable two-factor authentication' + click_on _('Enable two-factor authentication') - expect(page).to have_button("Set up new device", disabled: true) + device = register_device(device_type, password: user.password) + expect(page).to have_content("Your #{device_type} device was registered") + copy_recovery_codes + manage_two_factor_authentication + + expect(page).to have_content(device.name) end end @@ -40,10 +45,12 @@ def register_device(device_type, **kwargs) manage_two_factor_authentication expect(page).to have_content(_("You've already enabled two-factor authentication using a one-time password authenticator. In order to register a different device, you must first delete this authenticator.")) - device = register_device(device_type) + device = register_device(device_type, password: user.password) + expect(page).to have_content("Your #{device_type} device was registered") + copy_recovery_codes + manage_two_factor_authentication expect(page).to have_content(device.name) - expect(page).to have_content("Your #{device_type} device was registered") end it 'allows deleting a device' do @@ -51,8 +58,10 @@ def register_device(device_type, **kwargs) manage_two_factor_authentication expect(page).to have_content(_("You've already enabled two-factor authentication using a one-time password authenticator. In order to register a different device, you must first delete this authenticator.")) - first_device = register_device(device_type) - second_device = register_device(device_type, name: 'My other device') + first_device = register_device(device_type, password: user.password) + copy_recovery_codes + manage_two_factor_authentication + second_device = register_device(device_type, name: 'My other device', password: user.password) expect(page).to have_content(first_device.name) expect(page).to have_content(second_device.name) @@ -95,7 +104,7 @@ def register_device(device_type, **kwargs) describe 'when a device is registered' do before do manage_two_factor_authentication - register_device(device_type) + register_device(device_type, password: user.password) gitlab_sign_out gitlab_sign_in(user) end