Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ def after_sign_in_path_for(_user)
return authentication_methods_setup_url if user_needs_sp_auth_method_setup?
return fix_broken_personal_key_url if current_user.broken_personal_key?
return user_session.delete(:stored_location) if user_session.key?(:stored_location)
return setup_piv_cac_url if user_session[:add_piv_cac_after_2fa]
return login_add_piv_cac_prompt_url if session[:needs_to_setup_piv_cac_after_sign_in].present?
return reactivate_account_url if user_needs_to_reactivate_account?
return login_piv_cac_recommended_path if user_recommended_for_piv_cac?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,6 @@ def invalid_otp_error(type)
t('two_factor_authentication.invalid_otp')
when 'personal_key'
t('two_factor_authentication.invalid_personal_key')
when 'piv_cac'
t('two_factor_authentication.invalid_piv_cac')
else
raise "Unsupported otp method: #{type}"
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def two_factor_options_presenter
service_provider: current_sp,
phishing_resistant_required: service_provider_mfa_policy.phishing_resistant_required?,
piv_cac_required: service_provider_mfa_policy.piv_cac_required?,
add_piv_cac_after_2fa: user_session[:add_piv_cac_after_2fa].present?,
)
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

module TwoFactorAuthentication
class PivCacMismatchController < ApplicationController
include TwoFactorAuthenticatable

def show
analytics.piv_cac_mismatch_visited(
piv_cac_required: piv_cac_required?,
has_other_authentication_methods: has_other_authentication_methods?,
)

@piv_cac_required = piv_cac_required?
@has_other_authentication_methods = has_other_authentication_methods?
end

def create
analytics.piv_cac_mismatch_submitted(add_piv_cac_after_2fa: add_piv_cac_after_2fa?)
user_session[:add_piv_cac_after_2fa] = add_piv_cac_after_2fa?
redirect_to login_two_factor_options_url
end

private

def add_piv_cac_after_2fa?
params[:add_piv_cac_after_2fa] == 'true'
end

def piv_cac_required?
service_provider_mfa_policy.piv_cac_required?
end

def has_other_authentication_methods?
return @has_other_authentication_methods if defined?(@has_other_authentication_methods)
@has_other_authentication_methods = mfa_context.two_factor_configurations.any? do |config|
config.mfa_enabled? && !config.is_a?(PivCacConfiguration)
end
end

def mfa_context
@mfa_context ||= MfaContext.new(current_user)
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,22 @@ def handle_valid_piv_cac

def handle_invalid_piv_cac
clear_piv_cac_information
handle_invalid_otp(type: 'piv_cac')
update_invalid_user

if current_user.locked_out?
handle_second_factor_locked_user(type: 'piv_cac')
elsif redirect_for_piv_cac_mismatch_replacement?
redirect_to login_two_factor_piv_cac_mismatch_url
else
flash[:error] = t('two_factor_authentication.invalid_piv_cac')
redirect_to login_two_factor_piv_cac_url
end
end

# This overrides the method in TwoFactorAuthenticatable so that we
# redirect back to ourselves rather than rendering the :show template.
# This removes the token from the address bar and preserves the error
# in the flash.
def render_show_after_invalid
flash[:error] = flash.now[:error]
redirect_to login_two_factor_piv_cac_url
def redirect_for_piv_cac_mismatch_replacement?
piv_cac_verification_form.error_type == 'user.piv_cac_mismatch' &&
UserSessionContext.authentication_context?(context) &&
current_user.piv_cac_configurations.count < IdentityConfig.store.max_piv_cac_per_account
end

def piv_cac_view_data
Expand Down
14 changes: 12 additions & 2 deletions app/controllers/users/piv_cac_authentication_setup_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class PivCacAuthenticationSetupController < ApplicationController
helper_method :in_multi_mfa_selection_flow?

def new
@piv_cac_required = service_provider_mfa_policy.piv_cac_required?

if params.key?(:token)
process_piv_cac_setup
else
Expand All @@ -35,7 +37,10 @@ def error
end

def submit_new_piv_cac
if good_nickname
if skip?
user_session.delete(:add_piv_cac_after_2fa)
redirect_to after_sign_in_path_for(current_user)
elsif good_nickname?
user_session[:piv_cac_nickname] = params[:name]
create_piv_cac_nonce
redirect_to piv_cac_service_url_with_redirect, allow_other_host: true
Expand Down Expand Up @@ -100,6 +105,7 @@ def process_valid_submission
)
create_user_event(:piv_cac_enabled)
track_mfa_method_added
user_session.delete(:add_piv_cac_after_2fa)
session[:needs_to_setup_piv_cac_after_sign_in] = false
redirect_to next_setup_path || after_sign_in_path_for(current_user)
end
Expand All @@ -119,7 +125,11 @@ def process_invalid_submission
end
end

def good_nickname
def skip?
params[:skip] == 'true'
end

def good_nickname?
name = params[:name]
name.present? && !PivCacConfiguration.exists?(user_id: current_user.id, name: name)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,21 @@ def type
:piv_cac
end

def render_in(view_context, &block)
@disabled = view_context.user_session.key?(:add_piv_cac_after_2fa)
view_context.capture(&block)
end

def label
t('two_factor_authentication.login_options.piv_cac')
end

def info
t('two_factor_authentication.login_options.piv_cac_info')
end

def disabled?
@disabled.present?
end
end
end
21 changes: 16 additions & 5 deletions app/presenters/two_factor_login_options_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,33 @@ class TwoFactorLoginOptionsPresenter < TwoFactorAuthCode::GenericDeliveryPresent
include AccountResetConcern
include ActionView::Helpers::TranslationHelper

attr_reader :user, :reauthentication_context, :phishing_resistant_required, :piv_cac_required
attr_reader :user,
:reauthentication_context,
:phishing_resistant_required,
:piv_cac_required,
:add_piv_cac_after_2fa

alias_method :reauthentication_context?, :reauthentication_context
alias_method :phishing_resistant_required?, :phishing_resistant_required
alias_method :piv_cac_required?, :piv_cac_required
alias_method :add_piv_cac_after_2fa?, :add_piv_cac_after_2fa

def initialize(
user:,
view:,
reauthentication_context:,
service_provider:,
phishing_resistant_required:,
piv_cac_required:
piv_cac_required:,
add_piv_cac_after_2fa:
)
@user = user
@view = view
@reauthentication_context = reauthentication_context
@service_provider = service_provider
@phishing_resistant_required = phishing_resistant_required
@piv_cac_required = piv_cac_required
@add_piv_cac_after_2fa = add_piv_cac_after_2fa
end

def title
Expand All @@ -47,7 +54,7 @@ def info
end

def restricted_options_warning_text
return if reauthentication_context?
return if show_all_options?

if piv_cac_required?
t('two_factor_authentication.aal2_request.piv_cac_only_html', sp_name:)
Expand All @@ -60,9 +67,9 @@ def options
return @options if defined?(@options)
mfa = MfaContext.new(user)

if piv_cac_required? && !reauthentication_context?
if piv_cac_required? && !show_all_options?
configurations = mfa.piv_cac_configurations
elsif phishing_resistant_required? && !reauthentication_context?
elsif phishing_resistant_required? && !show_all_options?
configurations = mfa.phishing_resistant_configurations
else
configurations = mfa.two_factor_configurations
Expand Down Expand Up @@ -101,6 +108,10 @@ def first_enabled_option_index

private

def show_all_options?
reauthentication_context? || add_piv_cac_after_2fa?
end

def account_reset_link
t(
'two_factor_authentication.account_reset.text_html',
Expand Down
18 changes: 18 additions & 0 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5939,6 +5939,24 @@ def piv_cac_login_visited
track_event(:piv_cac_login_visited)
end

# User submits prompt to replace PIV/CAC after failing to authenticate due to mismatched subject
# @param [Boolean] add_piv_cac_after_2fa User chooses to replace PIV/CAC authenticator
def piv_cac_mismatch_submitted(add_piv_cac_after_2fa:, **extra)
track_event(:piv_cac_mismatch_submitted, add_piv_cac_after_2fa:, **extra)
end

# User visits prompt to replace PIV/CAC after failing to authenticate due to mismatched subject
# @param [Boolean] piv_cac_required Partner requires HSPD12 authentication
# @param [Boolean] has_other_authentication_methods User has non-PIV authentication methods
def piv_cac_mismatch_visited(piv_cac_required:, has_other_authentication_methods:, **extra)
track_event(
:piv_cac_mismatch_visited,
piv_cac_required:,
has_other_authentication_methods:,
**extra,
)
end

# @param [String] action what action user made
# Tracks when user submits an action on Piv Cac recommended page
def piv_cac_recommended(action: nil, **extra)
Expand Down
7 changes: 7 additions & 0 deletions app/views/two_factor_authentication/options/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

<%= render(VendorOutageAlertComponent.new(vendors: [:sms, :voice])) %>

<% if @presenter.add_piv_cac_after_2fa? %>
<%= render AlertComponent.new(
type: :info,
class: 'margin-bottom-4',
).with_content(t('two_factor_authentication.piv_cac_mismatch.2fa_before_add')) %>
<% end %>

<%= render PageHeadingComponent.new.with_content(@presenter.heading) %>

<p>
Expand Down
38 changes: 38 additions & 0 deletions app/views/two_factor_authentication/piv_cac_mismatch/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<% self.title = t('two_factor_authentication.piv_cac_mismatch.title') %>

<%= render PageHeadingComponent.new.with_content(t('two_factor_authentication.piv_cac_mismatch.title')) %>

<% if @has_other_authentication_methods %>
<p><%= t('two_factor_authentication.piv_cac_mismatch.instructions') %></p>

<%= render ButtonComponent.new(
url: login_two_factor_piv_cac_mismatch_url,
method: :post,
params: { add_piv_cac_after_2fa: 'true' },
big: true,
wide: true,
class: 'display-block margin-top-5',
).with_content(t('two_factor_authentication.piv_cac_mismatch.cta')) %>

<% if !@piv_cac_required %>
<%= render ButtonComponent.new(
url: login_two_factor_piv_cac_mismatch_url,
method: :post,
unstyled: true,
class: 'display-block margin-top-2',
).with_content(t('two_factor_authentication.piv_cac_mismatch.skip')) %>
<% end %>
<% else %>
<p><%= t('two_factor_authentication.piv_cac_mismatch.instructions_no_other_method', app_name: APP_NAME) %></p>

<%= render ButtonComponent.new(
url: account_reset_recovery_options_url,
big: true,
wide: true,
class: 'display-inline-block margin-top-3',
).with_content(t('two_factor_authentication.piv_cac_mismatch.delete_account')) %>
<% end %>

<%= render PageFooterComponent.new do %>
<%= link_to t('links.cancel'), sign_out_url %>
<% end %>
18 changes: 16 additions & 2 deletions app/views/users/piv_cac_authentication_setup/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,22 @@
<% end %>
<% end %>

<%= f.submit t('forms.piv_cac_setup.submit'), class: 'display-block margin-y-5' %>
<%= f.submit t('forms.piv_cac_setup.submit'), class: 'display-block margin-top-5 margin-bottom-2' %>
<% end %>

<% if user_session[:add_piv_cac_after_2fa] && !@piv_cac_required %>
<%= render ButtonComponent.new(
url: submit_new_piv_cac_url,
method: :post,
params: { skip: 'true' },
unstyled: true,
).with_content(t('mfa.skip')) %>
<% end %>

<%= render 'shared/cancel_or_back_to_options' %>
<% if user_session[:add_piv_cac_after_2fa] %>
<%= render PageFooterComponent.new do %>
<%= link_to t('links.cancel'), sign_out_path %>
<% end %>
<% else %>
<%= render 'shared/cancel_or_back_to_options' %>
<% end %>
7 changes: 7 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1716,6 +1716,13 @@ two_factor_authentication.phone_verification.troubleshooting.code_not_received:
two_factor_authentication.phone.delete.failure: Unable to remove your phone.
two_factor_authentication.phone.delete.success: Your phone has been removed.
two_factor_authentication.piv_cac_header_text: Insert your government employee ID
two_factor_authentication.piv_cac_mismatch.2fa_before_add: You need to authenticate with another method before adding your PIV/CAC.
two_factor_authentication.piv_cac_mismatch.cta: Authenticate and add PIV/CAC
two_factor_authentication.piv_cac_mismatch.delete_account: Delete your account
two_factor_authentication.piv_cac_mismatch.instructions: Click “Authenticate and add PIV/CAC” below to authenticate with another method before adding this PIV/CAC to your account.
two_factor_authentication.piv_cac_mismatch.instructions_no_other_method: If you were reissued your PIV/CAC, you will need to delete your %{app_name} account and create a new account to use your reissued PIV/CAC.
two_factor_authentication.piv_cac_mismatch.skip: Skip adding PIV/CAC
two_factor_authentication.piv_cac_mismatch.title: This government employee ID is not connected to your account
two_factor_authentication.piv_cac_upsell.add_piv: Add PIV/CAC card
two_factor_authentication.piv_cac_upsell.choose_other_method: Choose other methods instead
two_factor_authentication.piv_cac_upsell.explain: This will improve your account security and let you skip entering your email and password when signing in.
Expand Down
7 changes: 7 additions & 0 deletions config/locales/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1728,6 +1728,13 @@ two_factor_authentication.phone_verification.troubleshooting.code_not_received:
two_factor_authentication.phone.delete.failure: No se puede eliminar su teléfono.
two_factor_authentication.phone.delete.success: Su teléfono fue eliminado.
two_factor_authentication.piv_cac_header_text: Inserte su identificación de empleado del gobierno
two_factor_authentication.piv_cac_mismatch.2fa_before_add: Debe realizar la autenticación con otro método antes de añadir su tarjeta PIV o CAC.
two_factor_authentication.piv_cac_mismatch.cta: Autenticar y añadir tarjeta PIV o CAC
two_factor_authentication.piv_cac_mismatch.delete_account: Eliminar su cuenta
two_factor_authentication.piv_cac_mismatch.instructions: Haga clic en “Autenticar y añadir tarjeta PIV o CAC” más abajo para autenticar con otro método antes de añadir esta tarjeta PIV o CAC a su cuenta.
two_factor_authentication.piv_cac_mismatch.instructions_no_other_method: Si se le emitió una nueva tarjeta PIV o CAC, para poder usarla, deberá eliminar su cuenta de %{app_name} y crear una cuenta nueva.
two_factor_authentication.piv_cac_mismatch.skip: Saltar añadir tarjeta PIV o CAC
two_factor_authentication.piv_cac_mismatch.title: Esta tarjeta de identificación de empleado del gobierno no está conectada a su cuenta
two_factor_authentication.piv_cac_upsell.add_piv: Agregar tarjeta PIV/CAC
two_factor_authentication.piv_cac_upsell.choose_other_method: Elegir otros métodos
two_factor_authentication.piv_cac_upsell.explain: Esto hará que su cuenta sea más segura y no tendrá que ingresar su correo electrónico ni su contraseña cuando inicie sesión.
Expand Down
7 changes: 7 additions & 0 deletions config/locales/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1716,6 +1716,13 @@ two_factor_authentication.phone_verification.troubleshooting.code_not_received:
two_factor_authentication.phone.delete.failure: Impossible de supprimer votre téléphone.
two_factor_authentication.phone.delete.success: Votre téléphone a été supprimé.
two_factor_authentication.piv_cac_header_text: Insérer votre carte d’employé fédéral
two_factor_authentication.piv_cac_mismatch.2fa_before_add: Vous devez vous authentifier à l’aide d’une autre méthode avant d’ajouter votre carte PIV/CAC.
two_factor_authentication.piv_cac_mismatch.cta: S’authentifier et ajouter une carte PIV/CAC
two_factor_authentication.piv_cac_mismatch.delete_account: Supprimer votre compte
two_factor_authentication.piv_cac_mismatch.instructions: Cliquez sur « S’authentifier et ajouter une carte PIV/CAC » ci-dessous pour vous authentifier au moyen d’une autre méthode avant d’ajouter cette carte PIV/CAC à votre compte.
two_factor_authentication.piv_cac_mismatch.instructions_no_other_method: Si l’on vous a à nouveau délivré une carte PIV/CAC, vous devez supprimer votre compte %{app_name} et en créer un nouveau pour l’utiliser.
two_factor_authentication.piv_cac_mismatch.skip: Sauter l’ajout de carte PIV/CAC
two_factor_authentication.piv_cac_mismatch.title: Cette carte d’employé fédéral n’est pas associée à votre compte.
two_factor_authentication.piv_cac_upsell.add_piv: Ajouter une carte PIV/CAC
two_factor_authentication.piv_cac_upsell.choose_other_method: Choisir plutôt d’autres méthodes
two_factor_authentication.piv_cac_upsell.explain: Ceci permettra de renforcer la sécurité de votre compte et de sauter l’étape de saisie de votre e-mail et mot de passe quand vous vous connecterez.
Expand Down
7 changes: 7 additions & 0 deletions config/locales/zh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1729,6 +1729,13 @@ two_factor_authentication.phone_verification.troubleshooting.code_not_received:
two_factor_authentication.phone.delete.failure: 无法去掉你的电话。
two_factor_authentication.phone.delete.success: 你的电话已被去掉。
two_factor_authentication.piv_cac_header_text: 插入您的政府雇员ID
two_factor_authentication.piv_cac_mismatch.2fa_before_add: 添加你的PIV/CAC之前,你需要使用另外一种方法进行身份证实。
two_factor_authentication.piv_cac_mismatch.cta: 进行身份证实并添加PIV/CAC
two_factor_authentication.piv_cac_mismatch.delete_account: 删除你的帐户
two_factor_authentication.piv_cac_mismatch.instructions: 点击下边的“进行身份证实并添加PIV/CAC”,以在将该PIV/CAC添加到你账户之前使用另外一种方法进行身份证实。
two_factor_authentication.piv_cac_mismatch.instructions_no_other_method: 如果你的PIV/CAV是重新颁发的,你需要删除自己的%{app_name}帐户,并使用重新颁发的PIV/CAC 设立一个新账户。
two_factor_authentication.piv_cac_mismatch.skip: 跳过添加 PIV/CAC
two_factor_authentication.piv_cac_mismatch.title: 该政府雇员身份证件与你的账户没有连接起来
two_factor_authentication.piv_cac_upsell.add_piv: 添加 PIV/CAC 卡
two_factor_authentication.piv_cac_upsell.choose_other_method: 选择其他方法
two_factor_authentication.piv_cac_upsell.explain: 这将改善你账户安全,而且你在登录时无需再输入电邮和密码。
Expand Down
Loading