Skip to content

Commit

Permalink
Merge pull request #11848 from 18F/stages/rc-2025-02-06
Browse files Browse the repository at this point in the history
Deploy RC 450 to Production
  • Loading branch information
amirbey authored Feb 6, 2025
2 parents ec142ef + dd91c3d commit 4e5f992
Show file tree
Hide file tree
Showing 92 changed files with 1,333 additions and 1,304 deletions.
1 change: 0 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
},
{
"files": [
"app/javascript/packages/address-search/components/address-input.tsx",
"app/javascript/packages/address-search/components/full-address-search-input.tsx",
"app/javascript/packages/components/hooks/use-focus-trap.ts",
"app/javascript/packages/components/hooks/use-toggle-body-class-by-presence.ts",
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ GEM
bigdecimal
rexml
crass (1.0.6)
css_parser (1.19.1)
css_parser (1.21.0)
addressable
cssbundling-rails (1.4.0)
railties (>= 6.0.0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ def update
end

def destroy
result = ::TwoFactorAuthentication::WebauthnDeleteForm.new(
form = ::TwoFactorAuthentication::WebauthnDeleteForm.new(
user: current_user,
configuration_id: params[:id],
).submit
)
result = form.submit

analytics.webauthn_delete_submitted(**result)

if result.success?
handle_successful_mfa_deletion(event_type: :webauthn_key_removed)
handle_successful_mfa_deletion(event_type: form.event_type)
render json: { success: true }
else
render json: { success: false, error: result.first_error_message }, status: :bad_request
Expand Down
7 changes: 1 addition & 6 deletions app/controllers/concerns/idv/verify_info_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,7 @@ def shared_update
threatmetrix_session_id: idv_session.threatmetrix_session_id,
request_ip: request.remote_ip,
ipp_enrollment_in_progress: ipp_enrollment_in_progress?,
proofing_components: ProofingComponents.new(
user: current_user,
idv_session:,
session:,
user_session:,
),
proofing_components: ProofingComponents.new(idv_session:),
)

return true
Expand Down
20 changes: 19 additions & 1 deletion app/controllers/concerns/rate_limit_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module RateLimitConcern

def confirm_not_rate_limited(rate_limiters = ALL_IDV_RATE_LIMITERS)
exceeded_rate_limits = check_for_exceeded_rate_limits(rate_limiters)
if exceeded_rate_limits.any?
if exceeded_rate_limits.any? && !final_hybrid_submission_passed?
rate_limit_redirect!(exceeded_rate_limits.first)
return true
end
Expand All @@ -28,6 +28,24 @@ def confirm_not_rate_limited_for_phone_address_verification

private

def final_hybrid_submission_passed?
doc_session_idv = user_session.to_h['idv']
return false if doc_session_idv.blank?

doc_session_uuid = doc_session_idv['document_capture_session_uuid']
return false if doc_session_uuid.blank?

flow_path = doc_session_idv['flow_path']
return false if flow_path.blank?

return false if flow_path != 'hybrid'

document_capture_session = DocumentCaptureSession.find_by(uuid: doc_session_uuid)
return false if document_capture_session.nil?

document_capture_session.last_doc_auth_result == 'Passed'
end

def confirm_not_rate_limited_for_phone_and_letter_address_verification
if idv_attempter_rate_limited?(:proof_address) && gpo_verify_by_mail_policy.rate_limited?
rate_limit_redirect!(:proof_address)
Expand Down
17 changes: 16 additions & 1 deletion app/controllers/concerns/two_factor_authenticatable_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,40 @@ def auth_methods_session

def handle_verification_for_authentication_context(result:, auth_method:, extra_analytics: nil)
increment_mfa_selection_attempt_count(auth_method)
recaptcha_annotation = annotate_recaptcha(
result.success? ? RecaptchaAnnotator::AnnotationReasons::PASSED_TWO_FACTOR
: RecaptchaAnnotator::AnnotationReasons::FAILED_TWO_FACTOR,
)
analytics.multi_factor_auth(
**result,
multi_factor_auth_method: auth_method,
enabled_mfa_methods_count: mfa_context.enabled_mfa_methods_count,
new_device: new_device?,
**extra_analytics.to_h,
attempts: mfa_attempts_count,
recaptcha_annotation:,
)

if result.success?
handle_valid_verification_for_authentication_context(auth_method:)
user_session.delete(:mfa_attempts)
session.delete(:sign_in_recaptcha_assessment_id) if sign_in_recaptcha_annotation_enabled?
else
handle_invalid_verification_for_authentication_context
end
end

def annotate_recaptcha(reason)
if sign_in_recaptcha_annotation_enabled?
RecaptchaAnnotator.annotate(assessment_id: session[:sign_in_recaptcha_assessment_id], reason:)
end
end

private

def sign_in_recaptcha_annotation_enabled?
IdentityConfig.store.sign_in_recaptcha_annotation_enabled
end

def handle_valid_verification_for_authentication_context(auth_method:)
mark_user_session_authenticated(auth_method:, authentication_type: :valid_2fa)
disavowal_event, disavowal_token = create_user_event_with_disavowal(:sign_in_after_2fa)
Expand Down
7 changes: 1 addition & 6 deletions app/controllers/idv/enter_password_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,7 @@ def init_profile
profile = idv_session.create_profile_from_applicant_with_password(
password,
is_enhanced_ipp: resolved_authn_context_result.enhanced_ipp?,
proofing_components: ProofingComponents.new(
user: current_user,
idv_session:,
session:,
user_session:,
).to_h,
proofing_components: ProofingComponents.new(idv_session:).to_h,
)

if profile.gpo_verification_pending?
Expand Down
8 changes: 5 additions & 3 deletions app/controllers/idv/in_person/state_id_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ def update
pii_from_user[attr] = flow_params[attr]
end

analytics.idv_in_person_proofing_state_id_submitted(
**analytics_arguments.merge(**form_result),
)
# Accept Date of Birth from both memorable date and input date components
formatted_dob = MemorableDateComponent.extract_date_param flow_params&.[](:dob)
pii_from_user[:dob] = formatted_dob if formatted_dob
Expand All @@ -57,6 +54,11 @@ def update
end

idv_session.doc_auth_vendor = Idp::Constants::Vendors::USPS

analytics.idv_in_person_proofing_state_id_submitted(
**analytics_arguments.merge(**form_result),
)

redirect_to redirect_url
else
render :show, locals: extra_view_variables
Expand Down
10 changes: 10 additions & 0 deletions app/controllers/idv/link_sent_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ def update
analytics.idv_doc_auth_link_sent_submitted(**analytics_arguments)

return render_document_capture_cancelled if document_capture_session&.cancelled_at

# If the user opted into in-person proofing in the hybrid session,
# we should be able to find an establishing IPP enrollment
if current_user.has_establishing_in_person_enrollment?
redirect_to idv_in_person_url
return
end

# Otherwise, we assume the user is still in the remote doc auth flow.

return render_step_incomplete_error unless take_photo_with_phone_successful?

# The doc capture flow will have fetched the results already. We need
Expand Down
13 changes: 11 additions & 2 deletions app/controllers/idv/link_sent_poll_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def status
:unauthorized
elsif document_capture_session.cancelled_at
:gone
elsif rate_limiter.limited?
elsif rate_limiter.limited? && !session_result_passed?
:too_many_requests
elsif confirmed_barcode_attention_result? || user_has_establishing_in_person_enrollment?
:ok
Expand All @@ -46,7 +46,7 @@ def status
def redirect_url
return unless document_capture_session

if rate_limiter.limited?
if rate_limiter.limited? && !session_result_passed?
idv_session_errors_rate_limited_url
elsif user_has_establishing_in_person_enrollment?
idv_in_person_url
Expand All @@ -58,6 +58,15 @@ def session_result
@session_result = document_capture_session.load_result
end

def session_result_passed?
return @session_success if defined?(@session_success)
@session_success = false
if session_result
@session_success = session_result.success?
end
@session_success
end

def document_capture_session
return @document_capture_session if defined?(@document_capture_session)
@document_capture_session = DocumentCaptureSession.find_by uuid: document_capture_session_uuid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ class BackupCodeVerificationController < ApplicationController
prepend_before_action :authenticate_user

def show
analytics.multi_factor_auth_enter_backup_code_visit(context: context)
recaptcha_annotation = annotate_recaptcha(
RecaptchaAnnotator::AnnotationReasons::INITIATED_TWO_FACTOR,
)
analytics.multi_factor_auth_enter_backup_code_visit(context: context, recaptcha_annotation:)
@presenter = TwoFactorAuthCode::BackupCodePresenter.new(
view: view_context,
data: { current_user: current_user },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ class OtpVerificationController < ApplicationController
helper_method :in_multi_mfa_selection_flow?

def show
analytics.multi_factor_auth_enter_otp_visit(**analytics_properties)
recaptcha_annotation = annotate_recaptcha(
RecaptchaAnnotator::AnnotationReasons::INITIATED_TWO_FACTOR,
)
analytics.multi_factor_auth_enter_otp_visit(recaptcha_annotation:, **analytics_properties)

@landline_alert = landline_warning?
@presenter = presenter_for_two_factor_authentication_method
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ class PersonalKeyVerificationController < ApplicationController
before_action :check_personal_key_enabled

def show
analytics.multi_factor_auth_enter_personal_key_visit(context: context)
recaptcha_annotation = annotate_recaptcha(
RecaptchaAnnotator::AnnotationReasons::INITIATED_TWO_FACTOR,
)
analytics.multi_factor_auth_enter_personal_key_visit(context: context, recaptcha_annotation:)
@presenter = TwoFactorAuthCode::PersonalKeyPresenter.new
@personal_key_form = PersonalKeyForm.new(current_user)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ def show
if params[:token]
process_token
else
analytics.multi_factor_auth_enter_piv_cac(**analytics_properties)
recaptcha_annotation = annotate_recaptcha(
RecaptchaAnnotator::AnnotationReasons::INITIATED_TWO_FACTOR,
)
analytics.multi_factor_auth_enter_piv_cac(**analytics_properties, recaptcha_annotation:)
@presenter = presenter_for_two_factor_authentication_method
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ class TotpVerificationController < ApplicationController
before_action :confirm_totp_enabled

def show
analytics.multi_factor_auth_enter_totp_visit(context: context)
recaptcha_annotation = annotate_recaptcha(
RecaptchaAnnotator::AnnotationReasons::INITIATED_TWO_FACTOR,
)
analytics.multi_factor_auth_enter_totp_visit(context: context, recaptcha_annotation:)

@presenter = presenter_for_two_factor_authentication_method
return unless FeatureManagement.prefill_otp_codes?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ class WebauthnVerificationController < ApplicationController

def show
save_challenge_in_session
analytics.multi_factor_auth_enter_webauthn_visit(**analytics_properties)
recaptcha_annotation = annotate_recaptcha(
RecaptchaAnnotator::AnnotationReasons::INITIATED_TWO_FACTOR,
)
analytics.multi_factor_auth_enter_webauthn_visit(
**analytics_properties,
recaptcha_annotation:,
)
@presenter = presenter_for_two_factor_authentication_method
end

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/users/email_confirmations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def process_successful_confirmation(email_address)
confirm_and_notify(email_address)
if current_user
flash[:success] = t('devise.confirmations.confirmed')
if params[:request_id]
if params[:request_id] && IdentityConfig.store.feature_select_email_to_share_enabled
redirect_to sign_up_select_email_url
else
redirect_to account_url
Expand Down
5 changes: 5 additions & 0 deletions app/controllers/users/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ def recaptcha_response
)
end

def recaptcha_assessment_id
recaptcha_form.assessment_id
end

def recaptcha_form
@recaptcha_form ||= SignInRecaptchaForm.new(
email: auth_params[:email],
Expand Down Expand Up @@ -217,6 +221,7 @@ def track_authentication_attempt
success = current_user.present? &&
!user_locked_out?(user) &&
(recaptcha_response.success? || log_captcha_failures_only?)
session[:sign_in_recaptcha_assessment_id] = recaptcha_assessment_id if recaptcha_assessment_id

analytics.email_and_password_auth(
**recaptcha_response,
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/users/webauthn_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def destroy

if result.success?
flash[:success] = presenter.delete_success_alert_text
handle_successful_mfa_deletion(event_type: :webauthn_key_removed)
handle_successful_mfa_deletion(event_type: form.event_type)
redirect_to account_path
else
flash[:error] = result.first_error_message
Expand Down
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 @@ -127,7 +127,7 @@ def save_challenge_in_session
end

def process_valid_webauthn(form)
create_user_event(:webauthn_key_added)
create_user_event(form.event_type)
analytics.webauthn_setup_submitted(
platform_authenticator: form.platform_authenticator?,
in_account_creation_flow: user_session[:in_account_creation_flow] || false,
Expand Down
7 changes: 4 additions & 3 deletions app/controllers/users/webauthn_setup_mismatch_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,17 @@ def update
end

def destroy
result = ::TwoFactorAuthentication::WebauthnDeleteForm.new(
form = ::TwoFactorAuthentication::WebauthnDeleteForm.new(
user: current_user,
configuration_id: webauthn_mismatch_id,
skip_multiple_mfa_validation: in_multi_mfa_selection_flow?,
).submit
)
result = form.submit

analytics.webauthn_setup_mismatch_submitted(**result.to_h, confirmed_mismatch: false)

if result.success?
handle_successful_mfa_deletion(event_type: :webauthn_key_removed)
handle_successful_mfa_deletion(event_type: form.event_type)
redirect_to retry_setup_url
else
flash.now[:error] = result.first_error_message
Expand Down
1 change: 1 addition & 0 deletions app/decorators/event_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

EventDecorator = Struct.new(:event) do
def event_type
return if event.event_type.blank?
I18n.t("event_types.#{event.event_type}", app_name: APP_NAME)
end

Expand Down
5 changes: 3 additions & 2 deletions app/forms/sign_in_recaptcha_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ class SignInRecaptchaForm

RECAPTCHA_ACTION = 'sign_in'

attr_reader :form_class, :form_args, :email, :recaptcha_token, :device_cookie, :ab_test_bucket
attr_reader :form_class, :form_args, :email, :recaptcha_token, :device_cookie, :ab_test_bucket,
:assessment_id

validate :validate_recaptcha_result

Expand Down Expand Up @@ -39,7 +40,7 @@ def exempt?
private

def validate_recaptcha_result
recaptcha_response, _assessment_id = recaptcha_form.submit(recaptcha_token)
recaptcha_response, @assessment_id = recaptcha_form.submit(recaptcha_token)
errors.merge!(recaptcha_form) if !recaptcha_response.success?
end

Expand Down
Loading

0 comments on commit 4e5f992

Please sign in to comment.