18F/identity-idp

View on GitHub
app/controllers/users/totp_setup_controller.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
98%
# frozen_string_literal: true

module Users
  class TotpSetupController < ApplicationController
    include TwoFactorAuthenticatableMethods
    include MfaSetupConcern
    include SecureHeadersConcern
    include ReauthenticationRequiredConcern

    before_action :authenticate_user!
    before_action :confirm_user_authenticated_for_2fa_setup
    before_action :set_totp_setup_presenter
    before_action :apply_secure_headers_override
    before_action :cap_auth_app_count, only: %i[new confirm]
    before_action :confirm_recently_authenticated_2fa

    helper_method :in_multi_mfa_selection_flow?

    def new
      store_totp_secret_in_session
      track_event

      @code = new_totp_secret
      @qrcode = current_user.qrcode(new_totp_secret)
    end

    def confirm
      result = totp_setup_form.submit

      properties = result.to_h.merge(analytics_properties)
      analytics.multi_factor_auth_setup(**properties)

      if result.success?
        process_valid_code
      else
        process_invalid_code
      end
    end

    private

    def totp_setup_form
      @totp_setup_form ||= TotpSetupForm.new(
        current_user,
        new_totp_secret,
        params[:code].to_s.strip,
        params[:name].to_s.strip,
      )
    end

    def set_totp_setup_presenter
      @presenter = SetupPresenter.new(
        current_user: current_user,
        user_fully_authenticated: user_fully_authenticated?,
        user_opted_remember_device_cookie: user_opted_remember_device_cookie,
        remember_device_default: remember_device_default,
      )
    end

    def track_event
      mfa_user = MfaContext.new(current_user)
      analytics.totp_setup_visit(
        user_signed_up: MfaPolicy.new(current_user).two_factor_enabled?,
        totp_secret_present: new_totp_secret.present?,
        enabled_mfa_methods_count: mfa_user.enabled_mfa_methods_count,
        in_account_creation_flow: in_account_creation_flow?,
      )
    end

    def store_totp_secret_in_session
      user_session[:new_totp_secret] = current_user.generate_totp_secret if new_totp_secret.nil?
    end

    def process_valid_code
      create_events
      handle_valid_verification_for_confirmation_context(
        auth_method: TwoFactorAuthenticatable::AuthMethod::TOTP,
      )
      handle_remember_device_preference(params[:remember_device])
      flash[:success] = t('notices.totp_configured')
      user_session.delete(:new_totp_secret)
      redirect_to next_setup_path || after_mfa_setup_path
    end

    def create_events
      create_user_event(:authenticator_enabled)
      mfa_user = MfaContext.new(current_user)
      analytics.multi_factor_auth_added_totp(
        enabled_mfa_methods_count: mfa_user.enabled_mfa_methods_count,
        in_account_creation_flow: in_account_creation_flow?,
      )
      Funnel::Registration::AddMfa.call(current_user.id, 'auth_app', analytics)
    end

    def process_invalid_code
      flash[:error] = if totp_setup_form.name_taken
                        t('errors.piv_cac_setup.unique_name')
                      else
                        t('errors.invalid_totp')
                      end
      redirect_to authenticator_setup_url
    end

    def new_totp_secret
      user_session[:new_totp_secret]
    end

    def cap_auth_app_count
      return unless IdentityConfig.store.max_auth_apps_per_account <= current_auth_app_count
      redirect_to account_two_factor_authentication_path
    end

    def current_auth_app_count
      current_user.auth_app_configurations.count
    end

    def analytics_properties
      {
        in_account_creation_flow: in_account_creation_flow?,
        pii_like_keypaths: [[:mfa_method_counts, :phone]],
      }
    end
  end
end