diff --git a/app/controllers/concerns/saml_idp_auth_concern.rb b/app/controllers/concerns/saml_idp_auth_concern.rb index 946a27580f3..6b7616d1e82 100644 --- a/app/controllers/concerns/saml_idp_auth_concern.rb +++ b/app/controllers/concerns/saml_idp_auth_concern.rb @@ -42,6 +42,7 @@ def add_sp_metadata_to_session loa3: loa3_requested?, request_id: @request_id, request_url: request.original_url, + requested_attributes: requested_attributes, } end diff --git a/app/controllers/openid_connect/authorization_controller.rb b/app/controllers/openid_connect/authorization_controller.rb index c7d68f5247d..923a439e8ca 100644 --- a/app/controllers/openid_connect/authorization_controller.rb +++ b/app/controllers/openid_connect/authorization_controller.rb @@ -122,7 +122,7 @@ def store_request sp_request.issuer = @authorize_form.client_id sp_request.loa = @authorize_form.acr_values.sort.max sp_request.url = request.original_url - sp_request.requested_attributes = @authorize_decorator.requested_attributes + sp_request.requested_attributes = requested_attributes end end @@ -134,7 +134,12 @@ def add_sp_metadata_to_session loa3: @authorize_form.loa3_requested?, request_id: @request_id, request_url: request.original_url, + requested_attributes: requested_attributes, } end + + def requested_attributes + @_attributes ||= @authorize_decorator.requested_attributes + end end end diff --git a/app/controllers/users/verify_account_controller.rb b/app/controllers/users/verify_account_controller.rb index 4fdecb4a3ef..6595c399ffb 100644 --- a/app/controllers/users/verify_account_controller.rb +++ b/app/controllers/users/verify_account_controller.rb @@ -9,9 +9,10 @@ def index def create @verify_account_form = build_verify_account_form + if @verify_account_form.submit flash[:success] = t('account.index.verification.success') - redirect_to after_sign_in_path_for(current_user) + redirect_to sign_up_completed_url else render :index end diff --git a/app/forms/verify_account_form.rb b/app/forms/verify_account_form.rb index 31671329da0..c1f8bed3b1d 100644 --- a/app/forms/verify_account_form.rb +++ b/app/forms/verify_account_form.rb @@ -36,7 +36,7 @@ def validate_pending_profile def validate_otp return if valid_otp? - errors.add :otp, :otp_incorrect + errors.add :otp, :confirmation_code_incorrect end def valid_otp? diff --git a/app/inputs/inline_input.rb b/app/inputs/inline_input.rb new file mode 100644 index 00000000000..34cec13f070 --- /dev/null +++ b/app/inputs/inline_input.rb @@ -0,0 +1,17 @@ +class InlineInput < SimpleForm::Inputs::StringInput + def input(_wrapper_options) + input_html_classes.push('col-10 field monospace') + template.content_tag( + :div, builder.text_field(attribute_name, input_html_options), + class: 'col col-12 sm-col-4 mb4 sm-mb0' + ) + end + + def input_type + :text + end + + def builder + @builder ||= :builder + end +end diff --git a/app/views/users/verify_account/index.html.slim b/app/views/users/verify_account/index.html.slim index 75204f1b40a..61a96bd022e 100644 --- a/app/views/users/verify_account/index.html.slim +++ b/app/views/users/verify_account/index.html.slim @@ -2,8 +2,11 @@ h1.h3.my0 = t('forms.verify_profile.title') p.mt-tiny.mb0 = t('forms.verify_profile.instructions') + = simple_form_for(@verify_account_form, url: verify_account_path, html: { autocomplete: 'off', method: :post, role: 'form' }) do |f| = f.error :base - = f.input :otp, required: true, label: 'Secret code' - = f.button :submit, t('forms.verify_profile.submit'), class: 'mb1' + = f.input :otp, required: true, label: t('forms.verify_profile.name'), wrapper: :inline_form do + = f.input_field :otp, as: :inline, autofocus: true, type: 'text', maxlength: '10' + = f.button :submit, t('forms.verify_profile.submit') + end diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb index 9c9faea8d77..bc0e444d12c 100644 --- a/config/initializers/simple_form.rb +++ b/config/initializers/simple_form.rb @@ -1,9 +1,11 @@ +# rubocop:disable Metrics/BlockLength SimpleForm.setup do |config| config.button_class = 'btn btn-primary' config.boolean_label_class = nil config.default_form_class = 'mt3 sm-mt4' config.error_notification_tag = :div config.error_notification_class = 'alert alert-error' + config.wrapper_mappings = { inline: :append } config.wrappers :base do |b| b.use :html5 @@ -23,5 +25,19 @@ b.use :error, wrap_with: { tag: 'div', class: 'mt-tiny h6 red error-message' } end + config.wrappers :inline_form, tag: 'div' do |b| + b.use :label, class: 'bold block' + b.wrapper tag: 'div', class: 'col-12 clearfix' do |ba| + ba.use :input + end + + b.wrapper tag: 'div' do |bb| + bb.use :error, wrap_with: { tag: 'span', class: 'mt-tiny h6 red error-message' } + end + + b.optional :maxlength + end + config.default_wrapper = :vertical_form end +# rubocop:enable Metrics/BlockLength diff --git a/config/locales/errors/en.yml b/config/locales/errors/en.yml index 67c85bd45d7..4e23242c298 100644 --- a/config/locales/errors/en.yml +++ b/config/locales/errors/en.yml @@ -9,12 +9,13 @@ en: messages: already_confirmed: was already confirmed, please try signing in blank: Please fill in this field. + confirmation_code_incorrect: Incorrect code. Did you type it in correctly? confirmation_invalid_token: > - Invalid confirmation link. Either the link expired or you - already confirmed your account. + Invalid confirmation link. Either the link expired or you + already confirmed your account. confirmation_period_expired: > Expired confirmation link. - You can click "Resend confirmation instructions" to get another one. + You can click "Resend confirmation instructions" to get another one. expired: has expired, please request a new one format_mismatch: Please match the requested format. improbable_phone: Invalid phone number. Please make sure you enter a 10-digit phone number. diff --git a/config/locales/errors/es.yml b/config/locales/errors/es.yml index 87579ff8937..f3e3e2d66fc 100644 --- a/config/locales/errors/es.yml +++ b/config/locales/errors/es.yml @@ -10,6 +10,7 @@ es: messages: already_confirmed: Ya está confirmado, por favor intenta iniciar sesión. blank: Por favor, rellenar este campo. + confirmation_code_incorrect: NOT TRANSLATED YET confirmation_invalid_token: El enlace de confirmación en el que ha hecho clic ya no es válido. Esto puede haber sido causado al hacer clic en un enlace antiguo de su correo electrónico, o puede ser que ya haya diff --git a/config/locales/forms/en.yml b/config/locales/forms/en.yml index 29e5edee75f..fe6a908efc5 100644 --- a/config/locales/forms/en.yml +++ b/config/locales/forms/en.yml @@ -44,6 +44,7 @@ en: personal_key: Personal key try_again: Use another phone number verify_profile: - title: Verify your identity - submit: Submit - instructions: Enter your secret code + name: Confirmation code + instructions: Enter the ten-character code in the letter we sent you. + submit: Confirm account + title: Confirm your account diff --git a/config/locales/forms/es.yml b/config/locales/forms/es.yml index 12121218922..3320f316d6c 100644 --- a/config/locales/forms/es.yml +++ b/config/locales/forms/es.yml @@ -43,6 +43,7 @@ es: personal_key: Código de recuperación try_again: Inténtalo de nuevo verify_profile: + name: NOT TRANSLATED YET title: NOT TRANSLATED YET submit: NOT TRANSLATED YET instructions: NOT TRANSLATED YET diff --git a/config/locales/idv/en.yml b/config/locales/idv/en.yml index cdef2991ae0..dcc1adba4bb 100644 --- a/config/locales/idv/en.yml +++ b/config/locales/idv/en.yml @@ -154,7 +154,8 @@ en: no_pii: Do not use real personal information (demo purposes only) usps: bad_address: I can't get mail at this address - byline: To activate by mail, we will mail a letter with a confirmation code to your street address. + byline: We will mail a letter with a confirmation code to your verified + address on file. success: It should arrive in 5 to 10 business days. personal_details_verified: Personal details verified! modal: diff --git a/spec/controllers/openid_connect/authorization_controller_spec.rb b/spec/controllers/openid_connect/authorization_controller_spec.rb index 857b9ffbd05..713955cb3a8 100644 --- a/spec/controllers/openid_connect/authorization_controller_spec.rb +++ b/spec/controllers/openid_connect/authorization_controller_spec.rb @@ -113,7 +113,8 @@ loa3: false, issuer: 'urn:gov:gsa:openidconnect:test', request_id: sp_request_id, - request_url: request.original_url + request_url: request.original_url, + requested_attributes: %w[given_name family_name birthdate] ) end end diff --git a/spec/controllers/saml_idp_controller_spec.rb b/spec/controllers/saml_idp_controller_spec.rb index 0620e179452..a08b2c16d98 100644 --- a/spec/controllers/saml_idp_controller_spec.rb +++ b/spec/controllers/saml_idp_controller_spec.rb @@ -295,7 +295,8 @@ loa3: false, issuer: saml_settings.issuer, request_id: sp_request_id, - request_url: @saml_request.request.original_url + request_url: @saml_request.request.original_url, + requested_attributes: [:email] ) end diff --git a/spec/controllers/users/verify_account_controller_spec.rb b/spec/controllers/users/verify_account_controller_spec.rb index 3526d76cee4..89e757124bf 100644 --- a/spec/controllers/users/verify_account_controller_spec.rb +++ b/spec/controllers/users/verify_account_controller_spec.rb @@ -54,10 +54,12 @@ end context 'with a valid form' do - it 'redirects to the account verification page' do + let(:success) { true } + + it 'redirects to the sign_up/completions page' do action - expect(response).to redirect_to(verify_account_path) + expect(response).to redirect_to(sign_up_completed_url) end end diff --git a/spec/features/idv/flow_spec.rb b/spec/features/idv/flow_spec.rb index 30ef0851447..3530bb75ce9 100644 --- a/spec/features/idv/flow_spec.rb +++ b/spec/features/idv/flow_spec.rb @@ -346,29 +346,6 @@ end end - scenario 'pick USPS address verification' do - sign_in_and_2fa_user - visit verify_session_path - - fill_out_idv_form_ok - click_idv_continue - fill_out_financial_form_ok - click_idv_continue - click_idv_address_choose_usps - click_on t('idv.buttons.send_letter') - - expect(current_path).to eq verify_review_path - - fill_in :user_password, with: user_password - click_submit_default - - expect(current_url).to eq verify_confirmations_url - click_acknowledge_personal_key - - expect(current_url).to eq(account_url) - expect(page).to have_content(t('account.index.verification.reactivate_button')) - end - context 'cancel from USPS/Phone verification screen' do context 'without js' do it 'returns user to profile path' do diff --git a/spec/features/openid_connect/openid_connect_spec.rb b/spec/features/openid_connect/openid_connect_spec.rb index 9a38daae9e5..72a8f7c2025 100644 --- a/spec/features/openid_connect/openid_connect_spec.rb +++ b/spec/features/openid_connect/openid_connect_spec.rb @@ -285,11 +285,14 @@ sign_in_live_with_2fa(user) - fill_in 'Secret code', with: otp + fill_in t('forms.verify_profile.name'), with: usps_otp_code_for(user) click_button t('forms.verify_profile.submit') - click_button t('openid_connect.authorization.index.allow') + expect(current_path).to eq(sign_up_completed_path) + find('input').click + click_button t('openid_connect.authorization.index.allow') redirect_uri = URI(current_url) + expect(redirect_uri.to_s).to start_with('http://localhost:7654/auth/result') end end diff --git a/spec/features/saml/loa3_sso_spec.rb b/spec/features/saml/loa3_sso_spec.rb index 26f60d7a3b4..536aa776282 100644 --- a/spec/features/saml/loa3_sso_spec.rb +++ b/spec/features/saml/loa3_sso_spec.rb @@ -5,22 +5,23 @@ include IdvHelper context 'First time registration' do - it 'redirects to original SAML Authn Request after IdV is complete', email: true do + let(:email) { 'test@test.com' } + before do allow(FeatureManagement).to receive(:prefill_otp_codes?).and_return(true) - saml_authn_request = auth_request.create(loa3_with_bundle_saml_settings) + @saml_authn_request = auth_request.create(loa3_with_bundle_saml_settings) + end + + it 'redirects to original SAML Authn Request after IdV is complete', email: true do xmldoc = SamlResponseDoc.new('feature', 'response_assertion') - email = 'test@test.com' - visit saml_authn_request - click_link t('sign_up.registrations.create_account') - submit_form_with_valid_email - click_confirmation_link_in_email(email) - submit_form_with_valid_password - set_up_2fa_with_valid_phone - enter_2fa_code + visit @saml_authn_request + + saml_register_loa3_user(email) expect(current_path).to eq verify_path - click_on 'Yes' + + click_idv_begin + user = User.find_with_email(email) complete_idv_profile_ok(user.reload) click_acknowledge_personal_key @@ -41,7 +42,7 @@ end click_on I18n.t('forms.buttons.continue') - expect(current_url).to eq saml_authn_request + expect(current_url).to eq @saml_authn_request user_access_key = user.unlock_user_access_key(Features::SessionHelper::VALID_PASSWORD) profile_phone = user.active_profile.decrypt_pii(user_access_key).phone @@ -49,6 +50,34 @@ expect(xmldoc.phone_number.children.children.to_s).to eq(profile_phone) end + it 'allows the user to select verification via USPS letter', email: true do + visit @saml_authn_request + + saml_register_loa3_user(email) + + click_idv_begin + + fill_out_idv_form_ok + click_idv_continue + fill_out_financial_form_ok + click_idv_continue + + click_idv_address_choose_usps + + click_on t('idv.buttons.send_letter') + + expect(current_path).to eq verify_review_path + + fill_in :user_password, with: user_password + click_submit_default + + expect(current_url).to eq verify_confirmations_url + click_acknowledge_personal_key + + expect(current_url).to eq(account_url) + expect(page).to have_content(t('account.index.verification.reactivate_button')) + end + it 'shows user the start page with accordion' do saml_authn_request = auth_request.create(loa3_with_bundle_saml_settings) sp_content = [ @@ -130,18 +159,19 @@ context 'having previously selected USPS verification' do let(:phone_confirmed) { false } - it 'prompts for OTP at sign in' do + it 'prompts for confirmation code at sign in' do saml_authn_request = auth_request.create(loa3_with_bundle_saml_settings) - visit saml_authn_request - sign_in_live_with_2fa(user) expect(current_path).to eq verify_account_path - fill_in 'Secret code', with: otp + fill_in t('forms.verify_profile.name'), with: usps_otp_code_for(user) click_button t('forms.verify_profile.submit') + expect(current_path).to eq(sign_up_completed_path) + find('input').click + expect(current_url).to eq saml_authn_request end end diff --git a/spec/features/users/verify_profile_spec.rb b/spec/features/users/verify_profile_spec.rb index 5ac5b93b0fc..ed8942ab3b8 100644 --- a/spec/features/users/verify_profile_spec.rb +++ b/spec/features/users/verify_profile_spec.rb @@ -22,7 +22,7 @@ expect(current_path).to eq verify_account_path - fill_in 'Secret code', with: otp + fill_in t('forms.verify_profile.name'), with: otp click_button t('forms.verify_profile.submit') expect(current_path).to eq account_path @@ -35,11 +35,11 @@ scenario 'wrong OTP used' do sign_in_live_with_2fa(user) - fill_in 'Secret code', with: 'the wrong code' + fill_in t('forms.verify_profile.name'), with: 'the wrong code' click_button t('forms.verify_profile.submit') expect(current_path).to eq verify_account_path - expect(page).to have_content(t('errors.messages.otp_incorrect')) + expect(page).to have_content(t('errors.messages.confirmation_code_incorrect')) expect(page.body).to_not match('the wrong code') end end diff --git a/spec/forms/verify_account_form_spec.rb b/spec/forms/verify_account_form_spec.rb index 6149207a127..87c17b8b5fa 100644 --- a/spec/forms/verify_account_form_spec.rb +++ b/spec/forms/verify_account_form_spec.rb @@ -41,7 +41,7 @@ it 'is invalid' do expect(subject).to_not be_valid - expect(subject.errors[:otp]).to eq [t('errors.messages.otp_incorrect')] + expect(subject.errors[:otp]).to eq [t('errors.messages.confirmation_code_incorrect')] end end end diff --git a/spec/support/features/idv_helper.rb b/spec/support/features/idv_helper.rb index 56651c5c798..9944b5ba9bc 100644 --- a/spec/support/features/idv_helper.rb +++ b/spec/support/features/idv_helper.rb @@ -7,6 +7,10 @@ def user_password Features::SessionHelper::VALID_PASSWORD end + def usps_otp_code_for(user) + user.profiles.first.decrypt_pii(user.unlock_user_access_key(user.password))[:otp] + end + def fill_out_idv_form_ok fill_in 'profile_first_name', with: 'José' fill_in 'profile_last_name', with: 'One' @@ -59,6 +63,10 @@ def fill_out_phone_form_fail fill_in :idv_phone_form_phone, with: '(555) 555-5555' end + def click_idv_begin + click_on t('idv.index.continue_link') + end + def click_idv_continue click_button t('forms.buttons.continue') end @@ -89,7 +97,7 @@ def complete_idv_profile_ok(user) click_idv_address_choose_phone fill_out_phone_form_ok(user.phone) click_idv_continue - fill_in :user_password, with: Features::SessionHelper::VALID_PASSWORD + fill_in 'Password', with: Features::SessionHelper::VALID_PASSWORD click_submit_default end end diff --git a/spec/support/features/session_helper.rb b/spec/support/features/session_helper.rb index 2a495bd8bbe..4174dbce389 100644 --- a/spec/support/features/session_helper.rb +++ b/spec/support/features/session_helper.rb @@ -193,11 +193,10 @@ def loa1_sp_session end end - def loa3_sp_session + def loa3_sp_session(request_url: 'http://localhost:3000') Warden.on_next_request do |proxy| session = proxy.env['rack.session'] - sp = ServiceProvider.from_issuer('http://localhost:3000') - session[:sp] = { loa3: true, issuer: sp.issuer } + session[:sp] = { loa3: true, request_url: request_url } end end diff --git a/spec/support/saml_auth_helper.rb b/spec/support/saml_auth_helper.rb index 4f53f848695..fad99ef19ac 100644 --- a/spec/support/saml_auth_helper.rb +++ b/spec/support/saml_auth_helper.rb @@ -161,6 +161,15 @@ def saml_get_auth(settings) get(:auth, SAMLRequest: URI.decode(saml_request(settings))) end + def saml_register_loa3_user(email) + click_link t('sign_up.registrations.create_account') + submit_form_with_valid_email + click_confirmation_link_in_email(email) + submit_form_with_valid_password + set_up_2fa_with_valid_phone + enter_2fa_code + end + private def saml_request(settings)