diff --git a/app/controllers/two_factor_authentication/otp_verification_controller.rb b/app/controllers/two_factor_authentication/otp_verification_controller.rb index 06c4405112c..d456a705e52 100644 --- a/app/controllers/two_factor_authentication/otp_verification_controller.rb +++ b/app/controllers/two_factor_authentication/otp_verification_controller.rb @@ -124,6 +124,7 @@ def confirm_voice_capability return if params[:otp_delivery_preference] == 'sms' phone_is_confirmed = UserSessionContext.authentication_or_reauthentication_context?(context) + capabilities = PhoneNumberCapabilities.new(phone, phone_confirmed: phone_is_confirmed) return if capabilities.supports_voice? @@ -233,6 +234,7 @@ def update_phone_attributes user: current_user, attributes: { phone_id: user_session[:phone_id], phone: user_session[:unconfirmed_phone], + phone_confirmed_at: Time.zone.now, otp_make_default_number: selected_otp_make_default_number }, ) end diff --git a/app/services/update_user_phone_configuration.rb b/app/services/update_user_phone_configuration.rb index ff11b1730b7..f34c8db29ce 100644 --- a/app/services/update_user_phone_configuration.rb +++ b/app/services/update_user_phone_configuration.rb @@ -14,7 +14,10 @@ def initialize(user:, attributes:) def call result = user.update!( - attributes.except(:phone_id, :phone, :otp_make_default_number), + attributes.except( + :phone_id, :phone, :phone_confirmed_at, + :otp_make_default_number + ), ) manage_phone_configuration result @@ -51,6 +54,7 @@ def duplicate_phone? def phone_attributes @phone_attributes ||= { phone: attributes[:phone], + confirmed_at: attributes[:phone_confirmed_at], delivery_preference: attribute(:otp_delivery_preference), made_default_at: made_default_at_date, }.delete_if { |_, value| value.nil? } diff --git a/lib/data_requests/deployed/create_mfa_configurations_report.rb b/lib/data_requests/deployed/create_mfa_configurations_report.rb index 246d31f7927..d5c827d40ca 100644 --- a/lib/data_requests/deployed/create_mfa_configurations_report.rb +++ b/lib/data_requests/deployed/create_mfa_configurations_report.rb @@ -45,6 +45,7 @@ def phone_configurations_report id: phone_configuration.id, phone: phone_configuration.phone, created_at: phone_configuration.created_at, + confirmed_at: phone_configuration.confirmed_at, } end end diff --git a/lib/tasks/create_test_accounts.rb b/lib/tasks/create_test_accounts.rb index 6e5fb718d5c..aff3634fc56 100644 --- a/lib/tasks/create_test_accounts.rb +++ b/lib/tasks/create_test_accounts.rb @@ -18,6 +18,7 @@ def create_account(email: 'joe.smith@email.com', password: 'salty pickles', mfa_ user.save! MfaContext.new(user).phone_configurations.create( phone: mfa_phone || phone, + confirmed_at: Time.zone.now, delivery_preference: user.otp_delivery_preference ) Event.create(user_id: user.id, event_type: :account_created) diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 4f8bd1d4572..e719bba7b1f 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -237,6 +237,7 @@ namespace :dev do { delivery_preference: user.otp_delivery_preference, phone: format('+1 (415) 555-%04d', args[:num]), + confirmed_at: Time.zone.now, } end end diff --git a/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb b/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb index 306dc23e2ff..e2b6e980a0d 100644 --- a/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb +++ b/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb @@ -607,9 +607,10 @@ expect(subject.user_session[:unconfirmed_phone]).to eq('+1 (703) 555-5555') end - it 'does not update user phone' do + it 'does not update user phone or phone_confirmed_at attributes' do first_configuration = MfaContext.new(subject.current_user).phone_configurations.first expect(first_configuration.phone).to eq('+1 202-555-1212') + expect(first_configuration.confirmed_at).to eq(@previous_phone_confirmed_at) end it 'renders :show' do diff --git a/spec/factories/phone_configurations.rb b/spec/factories/phone_configurations.rb index 0a02480e61c..804dcb09241 100644 --- a/spec/factories/phone_configurations.rb +++ b/spec/factories/phone_configurations.rb @@ -2,6 +2,7 @@ Faker::Config.locale = :en factory :phone_configuration do + confirmed_at { Time.zone.now } phone { '+1 202-555-1212' } mfa_enabled { true } user { association :user } diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 72b2138f6ff..893adf1921d 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -121,7 +121,7 @@ user_id: -1, }.merge( evaluator.with.slice( - :phone, :delivery_preference, :mfa_enabled + :phone, :confirmed_at, :delivery_preference, :mfa_enabled ), ), ) @@ -135,7 +135,7 @@ delivery_preference: user.otp_delivery_preference, }.merge( evaluator.with.slice( - :phone, :delivery_preference, :mfa_enabled + :phone, :confirmed_at, :delivery_preference, :mfa_enabled ), ), ) diff --git a/spec/features/phone/add_phone_spec.rb b/spec/features/phone/add_phone_spec.rb index 55cc5994ddf..a924e341fe9 100644 --- a/spec/features/phone/add_phone_spec.rb +++ b/spec/features/phone/add_phone_spec.rb @@ -17,6 +17,8 @@ expect(page).to have_current_path(account_path) expect(user.reload.phone_configurations.count).to eq(2) + expect(user.phone_configurations[0].confirmed_at).to be_present + expect(user.phone_configurations[1].confirmed_at).to be_present end scenario 'adding a new phone number sends the user an email with a disavowal link' do diff --git a/spec/features/phone/confirmation_spec.rb b/spec/features/phone/confirmation_spec.rb index 76315dfcc85..5839bd42e58 100644 --- a/spec/features/phone/confirmation_spec.rb +++ b/spec/features/phone/confirmation_spec.rb @@ -21,6 +21,7 @@ def expect_successful_otp_confirmation(delivery_method) expect(page).to have_content(t('notices.phone_confirmed')) expect(page).to have_current_path(auth_method_confirmation_path) + expect(phone_configuration.confirmed_at).to_not be_nil expect(phone_configuration.delivery_preference).to eq(delivery_method.to_s) end @@ -76,6 +77,7 @@ def visit_otp_confirmation(delivery_method) def expect_successful_otp_confirmation(delivery_method) expect(page).to have_content(t('notices.phone_confirmed')) expect(page).to have_current_path(account_path) + expect(phone_configuration.confirmed_at).to_not be_nil expect(phone_configuration.delivery_preference).to eq(delivery_method.to_s) end diff --git a/spec/fixtures/data_request.json b/spec/fixtures/data_request.json index 4879fe1efec..71a099d0e2b 100644 --- a/spec/fixtures/data_request.json +++ b/spec/fixtures/data_request.json @@ -15,7 +15,8 @@ { "id": 123456, "phone": "+1 555-555-5555", - "created_at": "2021-10-21T14:53:08.803Z" + "created_at": "2021-10-21T14:53:08.803Z", + "confirmed_at": "2021-10-21T14:53:08.790Z" } ], "auth_app_configurations": [], diff --git a/spec/lib/data_requests/deployed/create_mfa_configurations_report_spec.rb b/spec/lib/data_requests/deployed/create_mfa_configurations_report_spec.rb index 999eb11f933..77b69927273 100644 --- a/spec/lib/data_requests/deployed/create_mfa_configurations_report_spec.rb +++ b/spec/lib/data_requests/deployed/create_mfa_configurations_report_spec.rb @@ -15,6 +15,9 @@ expect(phone_data.first[:created_at]).to be_within(1.second).of( phone_configuration.created_at, ) + expect(phone_data.first[:confirmed_at]).to be_within(1.second).of( + phone_configuration.confirmed_at, + ) end it 'includes an array for authentication apps' do diff --git a/spec/lib/data_requests/local/write_user_info_spec.rb b/spec/lib/data_requests/local/write_user_info_spec.rb index 5666620db99..13acab62f04 100644 --- a/spec/lib/data_requests/local/write_user_info_spec.rb +++ b/spec/lib/data_requests/local/write_user_info_spec.rb @@ -30,12 +30,14 @@ expect(email_row['uuid']).to eq(uuid) expect(email_row['value']).to eq('test@example.com') expect(email_row['created_at']).to be_present + expect(email_row['confirmed_at']).to be_present expect(email_row['internal_id']).to be_nil phone_row = parsed.find { |r| r['type'] == 'Phone configuration' } expect(phone_row['uuid']).to eq(uuid) expect(phone_row['value']).to eq('+1 555-555-5555') expect(phone_row['created_at']).to be_present + expect(phone_row['confirmed_at']).to be_present expect(phone_row['internal_id']).to be_present end