diff --git a/app/controllers/idv/agreement_controller.rb b/app/controllers/idv/agreement_controller.rb
index 64ced91aba2..2ccd9277a11 100644
--- a/app/controllers/idv/agreement_controller.rb
+++ b/app/controllers/idv/agreement_controller.rb
@@ -40,7 +40,11 @@ def update
if IdentityConfig.store.in_person_proofing_opt_in_enabled &&
IdentityConfig.store.in_person_proofing_enabled
- redirect_to idv_how_to_verify_url
+ if params[:skip_hybrid_handoff]
+ redirect_to idv_choose_id_type_url
+ else
+ redirect_to idv_how_to_verify_url
+ end
else
redirect_to idv_hybrid_handoff_url
end
@@ -53,7 +57,7 @@ def self.step_info
Idv::StepInfo.new(
key: :agreement,
controller: self,
- next_steps: [:hybrid_handoff, :document_capture, :how_to_verify],
+ next_steps: [:hybrid_handoff, :choose_id_type, :document_capture, :how_to_verify],
preconditions: ->(idv_session:, user:) { idv_session.welcome_visited },
undo_step: ->(idv_session:, user:) do
idv_session.idv_consent_given_at = nil
diff --git a/app/controllers/idv/choose_id_type_controller.rb b/app/controllers/idv/choose_id_type_controller.rb
new file mode 100644
index 00000000000..75e668624d8
--- /dev/null
+++ b/app/controllers/idv/choose_id_type_controller.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+module Idv
+ class ChooseIdTypeController < ApplicationController
+ include Idv::AvailabilityConcern
+ include IdvStepConcern
+ include StepIndicatorConcern
+
+ before_action :redirect_if_passport_not_available
+
+ def show
+ analytics.idv_doc_auth_choose_id_type_visited(**analytics_arguments)
+ end
+
+ def update
+ clear_future_steps!
+
+ @choose_id_type_form = Idv::ChooseIdTypeForm.new
+
+ result = @choose_id_type_form.submit(choose_id_type_form_params)
+
+ analytics.idv_doc_auth_choose_id_type_submitted(
+ **analytics_arguments.merge(result.to_h)
+ .merge({ chosen_id_type: }),
+ )
+
+ if result.success?
+ set_passport_requested
+ redirect_to next_step
+ else
+ render :show
+ end
+ end
+
+ def self.step_info
+ Idv::StepInfo.new(
+ key: :choose_id_type,
+ controller: self,
+ next_steps: [:document_capture],
+ preconditions: ->(idv_session:, user:) {
+ idv_session.flow_path == 'standard' &&
+ idv_session.passport_allowed == true
+ },
+ undo_step: ->(idv_session:, user:) do
+ idv_session.passport_requested = nil
+ end,
+ )
+ end
+
+ private
+
+ def redirect_if_passport_not_available
+ redirect_to idv_how_to_verify_url if !idv_session.passport_allowed
+ end
+
+ def chosen_id_type
+ choose_id_type_form_params[:choose_id_type_preference]
+ end
+
+ def set_passport_requested
+ if chosen_id_type == 'passport'
+ idv_session.passport_requested = true
+ else
+ idv_session.passport_requested = false
+ end
+ end
+
+ def next_step
+ idv_document_capture_url
+ end
+
+ def choose_id_type_form_params
+ params.require(:doc_auth).permit(:choose_id_type_preference)
+ end
+
+ def analytics_arguments
+ {
+ step: 'choose_id_type',
+ analytics_id: 'Doc Auth',
+ flow_path: idv_session.flow_path,
+ }
+ end
+ end
+end
diff --git a/app/controllers/idv/hybrid_handoff_controller.rb b/app/controllers/idv/hybrid_handoff_controller.rb
index d78224d68ea..37a199b744f 100644
--- a/app/controllers/idv/hybrid_handoff_controller.rb
+++ b/app/controllers/idv/hybrid_handoff_controller.rb
@@ -58,7 +58,7 @@ def self.step_info
Idv::StepInfo.new(
key: :hybrid_handoff,
controller: self,
- next_steps: [:link_sent, :document_capture, :socure_document_capture],
+ next_steps: [:choose_id_type, :link_sent, :document_capture, :socure_document_capture],
preconditions: ->(idv_session:, user:) {
idv_session.idv_consent_given? &&
(self.selected_remote(idv_session: idv_session) || # from opt-in screen
@@ -149,7 +149,7 @@ def update_document_capture_session_requested_at(session_uuid)
def bypass_send_link_steps
idv_session.flow_path = 'standard'
- redirect_to vendor_document_capture_url
+ redirect_to next_step
analytics.idv_doc_auth_hybrid_handoff_submitted(
**analytics_arguments.merge(
@@ -158,6 +158,14 @@ def bypass_send_link_steps
)
end
+ def next_step
+ if idv_session.passport_allowed
+ idv_choose_id_type_url
+ else
+ idv_document_capture_url
+ end
+ end
+
def extra_view_variables
{ idv_phone_form: build_form }
end
diff --git a/app/controllers/idv/welcome_controller.rb b/app/controllers/idv/welcome_controller.rb
index d25415fbca5..b9b5badc77d 100644
--- a/app/controllers/idv/welcome_controller.rb
+++ b/app/controllers/idv/welcome_controller.rb
@@ -11,6 +11,7 @@ class WelcomeController < ApplicationController
def show
idv_session.proofing_started_at ||= Time.zone.now.iso8601
+ idv_session.passport_allowed = IdentityConfig.store.doc_auth_passports_enabled
analytics.idv_doc_auth_welcome_visited(**analytics_arguments)
Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer])
diff --git a/app/forms/idv/choose_id_type_form.rb b/app/forms/idv/choose_id_type_form.rb
new file mode 100644
index 00000000000..1b11407e19f
--- /dev/null
+++ b/app/forms/idv/choose_id_type_form.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Idv
+ class ChooseIdTypeForm
+ include ActiveModel::Model
+
+ validate :chosen_id_type_valid?
+ attr_reader :chosen_id_type
+
+ def initialize(chosen_id_type = nil)
+ @chosen_id_type = chosen_id_type
+ end
+
+ def submit(params)
+ @chosen_id_type = params[:choose_id_type_preference]
+
+ FormResponse.new(success: chosen_id_type_valid?, errors: errors)
+ end
+
+ def chosen_id_type_valid?
+ valid_types = ['passport', 'drivers_license'] # Will remove once pasport added to id slugs
+ return true if valid_types.include? @chosen_id_type
+ errors.add(
+ :chosen_id_type,
+ :invalid,
+ message: "`choose_id_type` #{@chosen_id_type} is invalid, expected one of #{valid_types}",
+ )
+ false
+ end
+ end
+end
diff --git a/app/policies/idv/flow_policy.rb b/app/policies/idv/flow_policy.rb
index a8510c31f09..8385438f1e5 100644
--- a/app/policies/idv/flow_policy.rb
+++ b/app/policies/idv/flow_policy.rb
@@ -18,6 +18,7 @@ class FlowPolicy
agreement: Idv::AgreementController.step_info,
how_to_verify: Idv::HowToVerifyController.step_info,
hybrid_handoff: Idv::HybridHandoffController.step_info,
+ choose_id_type: Idv::ChooseIdTypeController.step_info,
link_sent: Idv::LinkSentController.step_info,
document_capture: Idv::DocumentCaptureController.step_info,
socure_document_capture: Idv::Socure::DocumentCaptureController.step_info,
diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb
index 22c963a4e3f..4c071817c36 100644
--- a/app/services/analytics_events.rb
+++ b/app/services/analytics_events.rb
@@ -1306,6 +1306,51 @@ def idv_doc_auth_capture_complete_visited(
)
end
+ # @param [Boolean] success
+ # @param [String] step Current IdV step
+ # @param [String] analytics_id Current IdV flow identifier
+ # @param ["hybrid","standard"] flow_path Document capture user flow
+ # @param ['drivers_license', 'passport'] chosen_id_type Chosen id type of the user
+ # @param [Hash] error_details
+ def idv_doc_auth_choose_id_type_submitted(
+ success:,
+ step:,
+ analytics_id:,
+ flow_path:,
+ chosen_id_type:,
+ error_details: nil,
+ **extra
+ )
+ track_event(
+ :idv_doc_auth_choose_id_type_submitted,
+ success:,
+ step:,
+ analytics_id:,
+ flow_path:,
+ chosen_id_type:,
+ error_details:,
+ **extra,
+ )
+ end
+
+ # @param [String] step Current IdV step
+ # @param [String] analytics_id Current IdV flow identifier
+ # @param ["hybrid","standard"] flow_path Document capture user flow
+ def idv_doc_auth_choose_id_type_visited(
+ step:,
+ analytics_id:,
+ flow_path:,
+ **extra
+ )
+ track_event(
+ :idv_doc_auth_choose_id_type_visited,
+ step:,
+ analytics_id:,
+ flow_path:,
+ **extra,
+ )
+ end
+
# User returns from Socure document capture, but is waiting on a result to be fetched
# @param ["hybrid","standard"] flow_path Document capture user flow
# @param [String] step Current IdV step
diff --git a/app/services/idv/session.rb b/app/services/idv/session.rb
index 70435fb86c8..f0707baf90a 100644
--- a/app/services/idv/session.rb
+++ b/app/services/idv/session.rb
@@ -16,6 +16,8 @@ module Idv
# @attr idv_phone_step_document_capture_session_uuid [String, nil]
# @attr mail_only_warning_shown [Boolean, nil]
# @attr opted_in_to_in_person_proofing [Boolean, nil]
+ # @attr passport_allowed [Boolean, nil]
+ # @attr passport_requested [Boolean, nil]
# @attr personal_key [String, nil]
# @attr personal_key_acknowledged [Boolean, nil]
# @attr phone_for_mobile_flow [String, nil]
@@ -60,6 +62,8 @@ class Session
idv_phone_step_document_capture_session_uuid
mail_only_warning_shown
opted_in_to_in_person_proofing
+ passport_allowed
+ passport_requested
personal_key
personal_key_acknowledged
phone_for_mobile_flow
diff --git a/app/views/idv/choose_id_type/show.html.erb b/app/views/idv/choose_id_type/show.html.erb
new file mode 100644
index 00000000000..a2e45997035
--- /dev/null
+++ b/app/views/idv/choose_id_type/show.html.erb
@@ -0,0 +1,51 @@
+<% self.title = t('doc_auth.headings.choose_id_type') %>
+
+<% content_for(:pre_flash_content) do %>
+ <%= render StepIndicatorComponent.new(
+ steps: Idv::StepIndicatorConcern::STEP_INDICATOR_STEPS,
+ current_step: :verify_id,
+ locale_scope: 'idv',
+ class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4',
+ ) %>
+<% end %>
+
+<%= render PageHeadingComponent.new do %>
+ <%= t('doc_auth.headings.choose_id_type') %>
+<% end %>
+
+
+ <%= t('doc_auth.info.choose_id_type') %>
+
+
+<%= new_tab_link_to(
+ t('doc_auth.info.id_types_learn_more'),
+ help_center_redirect_url(
+ category: 'verify-your-identity',
+ article: 'accepted-identification-documents',
+ ),
+ )
+%>
+
+<%= simple_form_for(
+ :doc_auth,
+ url: idv_choose_id_type_path,
+ method: :put,
+ ) do |f| %>
+ <%= render ValidatedFieldComponent.new(
+ as: :radio_buttons,
+ collection: [
+ [t('doc_auth.forms.id_type_preference.drivers_license'), :drivers_license],
+ [t('doc_auth.forms.id_type_preference.passport'), :passport],
+ ],
+ form: f,
+ input_html: { class: 'usa-radio__input--tile' },
+ item_label_class: 'usa-radio__label text-bold width-full margin-y-2',
+ name: :choose_id_type_preference,
+ required: true,
+ wrapper: :uswds_radio_buttons,
+ error_messages: { valueMissing: t('doc_auth.errors.choose_id_type_check') },
+ ) %>
+ <%= f.submit t('forms.buttons.continue'), class: 'margin-y-2' %>
+<% end %>
+
+<%= render 'idv/doc_auth/cancel', step: 'choose_id_type' %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 1bfcbb18e25..3bef6fc2858 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -531,6 +531,7 @@ doc_auth.errors.camera.blocked: Your camera is blocked
doc_auth.errors.camera.blocked_detail_html: 'Allow access to your camera to take photos for %{app_name}.Try taking photos again and allowing permission. If that doesn’t work, you may need to check your device settings to allow access.'
doc_auth.errors.camera.failed: Camera failed to start, please try again.
doc_auth.errors.card_type: Try again with your driver’s license or state ID card.
+doc_auth.errors.choose_id_type_check: Select the type of document that you have
doc_auth.errors.consent_form: Before you can continue, you must give us permission. Please check the box below and then click continue.
doc_auth.errors.doc_type_not_supported_heading: We only accept a driver’s license or a state ID
doc_auth.errors.doc.doc_type_check: Your driver’s license or state ID must be issued by a U.S. state or territory. We do not accept other forms of ID, like passports or military IDs.
@@ -581,6 +582,8 @@ doc_auth.errors.upload_error: Sorry, something went wrong on our end.
doc_auth.forms.change_file: Change file
doc_auth.forms.choose_file_html: Drag file here or choose from folder
doc_auth.forms.doc_success: We verified your information
+doc_auth.forms.id_type_preference.drivers_license: U.S. driver’s license or state ID
+doc_auth.forms.id_type_preference.passport: U.S. passport book
doc_auth.forms.selected_file: Selected file
doc_auth.headers.expired_id: Your ID may have expired
doc_auth.headers.general.network_error: We are having technical difficulties
@@ -594,6 +597,7 @@ doc_auth.headings.back: Back of your driver’s license or state ID
doc_auth.headings.capture_complete: We verified your identity document
doc_auth.headings.capture_scan_warning_html: We couldn’t read the barcode on your ID. If the information below is incorrect, please %{link_html} of your state‑issued ID.
doc_auth.headings.capture_scan_warning_link: upload new photos
+doc_auth.headings.choose_id_type: Choose your ID type
doc_auth.headings.document_capture: Add photos of your driver’s license or state ID card
doc_auth.headings.document_capture_back: Back of your ID
doc_auth.headings.document_capture_front: Front of your ID
@@ -635,6 +639,7 @@ doc_auth.info.capture_status_capturing: Capturing
doc_auth.info.capture_status_none: Align
doc_auth.info.capture_status_small_document: Move Closer
doc_auth.info.capture_status_tap_to_capture: Tap to Capture
+doc_auth.info.choose_id_type: Select the type of document that you have. You’ll need to take photos of your ID to verify your identity.
doc_auth.info.exit.with_sp: Exit %{app_name} and return to %{sp_name}
doc_auth.info.exit.without_sp: Exit identity verification and go to your account page
doc_auth.info.getting_started_html: '%{sp_name} needs to make sure you are you — not someone pretending to be you. %{link_html}'
@@ -644,6 +649,7 @@ doc_auth.info.how_to_verify_mobile: You have the option to verify your identity
doc_auth.info.how_to_verify_troubleshooting_options_header: Want to learn more about how to verify your identity?
doc_auth.info.hybrid_handoff: We’ll collect information about you by reading your state‑issued ID.
doc_auth.info.hybrid_handoff_ipp_html: 'Don’t have a mobile phone? You can verify your identity at a United States Post Office instead.'
+doc_auth.info.id_types_learn_more: Learn more about which ID types you can use
doc_auth.info.image_loaded: Image loaded
doc_auth.info.image_loading: Image loading
doc_auth.info.image_updated: Image updated
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 8ad0dddbe84..a470950d733 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -542,6 +542,7 @@ doc_auth.errors.camera.blocked: Su cámara está bloqueada
doc_auth.errors.camera.blocked_detail_html: 'Permita el acceso a su cámara para tomar las fotografías de %{app_name}.Intente tomar las fotografías de nuevo permitiendo el acceso. Si eso no funciona, tal vez necesite revisar la configuración de su dispositivo para permitir el acceso.'
doc_auth.errors.camera.failed: No se pudo activar la cámara; inténtelo de nuevo.
doc_auth.errors.card_type: Inténtelo de nuevo con su licencia de conducir o tarjeta de identificación estatal.
+doc_auth.errors.choose_id_type_check: Seleccione el tipo de documento que tenga
doc_auth.errors.consent_form: Antes de continuar, debe darnos permiso. Marque la casilla a continuación y luego haga clic en continuar.
doc_auth.errors.doc_type_not_supported_heading: Solo aceptamos una licencia de conducir o una identificación estatal.
doc_auth.errors.doc.doc_type_check: Su licencia de conducir o identificación estatal debe ser emitida por un estado o territorio de los EE. UU. No aceptamos otras formas de identificación, como pasaportes o identificaciones militares.
@@ -592,6 +593,8 @@ doc_auth.errors.upload_error: Lo sentimos, algo no funcionó bien.
doc_auth.forms.change_file: Cambiar archivo
doc_auth.forms.choose_file_html: Arrastrar el archivo aquí o seleccionarlo de la carpeta
doc_auth.forms.doc_success: Verificamos su información
+doc_auth.forms.id_type_preference.drivers_license: Licencia de conducir de los EE. UU. o identificación estatal
+doc_auth.forms.id_type_preference.passport: Pasaporte estadounidense
doc_auth.forms.selected_file: Archivo seleccionado
doc_auth.headers.expired_id: Su identificación puede estar vencida
doc_auth.headers.general.network_error: Estamos teniendo problemas técnicos
@@ -605,6 +608,7 @@ doc_auth.headings.back: Reverso de su licencia de conducir o identificación est
doc_auth.headings.capture_complete: Verificamos su documento de identidad
doc_auth.headings.capture_scan_warning_html: No pudimos leer el código de barras en su identificación. Si la información que aparece a continuación es incorrecta, %{link_html} de su identificación emitida por el estado.
doc_auth.headings.capture_scan_warning_link: cargue nuevas fotos
+doc_auth.headings.choose_id_type: Elija el tipo de su identificación
doc_auth.headings.document_capture: Añade fotos de tu licencia de conducir o credencial de identificación oficial
doc_auth.headings.document_capture_back: Reverso de su identificación
doc_auth.headings.document_capture_front: Frente de su identificación
@@ -646,6 +650,7 @@ doc_auth.info.capture_status_capturing: Capturando
doc_auth.info.capture_status_none: Alinear
doc_auth.info.capture_status_small_document: Acercar
doc_auth.info.capture_status_tap_to_capture: Tocar para capturar
+doc_auth.info.choose_id_type: Seleccione el tipo de documento que tenga. Tendrá que tomar fotografías de su identificación para verificar su identidad.
doc_auth.info.exit.with_sp: Salir de %{app_name} y volver a %{sp_name}
doc_auth.info.exit.without_sp: Salga de la verificación de identidad y vaya a la página de su cuenta
doc_auth.info.getting_started_html: '%{sp_name} necesita asegurarse de que se trata de usted y no de alguien que se hace pasar por usted. %{link_html}'
@@ -655,6 +660,7 @@ doc_auth.info.how_to_verify_mobile: Tiene la opción de verificar su identidad e
doc_auth.info.how_to_verify_troubleshooting_options_header: ¿Desea obtener más información sobre cómo verificar su identidad?
doc_auth.info.hybrid_handoff: Recopilaremos información sobre usted leyendo su identificación emitida por el estado.
doc_auth.info.hybrid_handoff_ipp_html: '¿No tiene un teléfono móvil? Puede verificar su identidad en una oficina de correos de los Estados Unidos.'
+doc_auth.info.id_types_learn_more: Obtenga más información acerca de los tipos de identificación que puede usar
doc_auth.info.image_loaded: Imagen cargada
doc_auth.info.image_loading: Cargando la imagen
doc_auth.info.image_updated: Imagen actualizada
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 7c63337f736..b4a43c9e67e 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -531,6 +531,7 @@ doc_auth.errors.camera.blocked: Votre appareil photo est bloqué
doc_auth.errors.camera.blocked_detail_html: 'Autorisez l’accès à votre caméra pour prendre des photos pour %{app_name}.Essayez à nouveau de prendre des photos en autorisant %{app_name} à accéder à votre appareil. Si cela ne marche pas, pensez à vérifier les paramètres de votre appareil en matière d’autorisations d’accès.'
doc_auth.errors.camera.failed: L’appareil photo n’a pas réussi à démarrer, veuillez réessayer.
doc_auth.errors.card_type: Réessayez avec votre permis de conduire ou carte d’identité d’un État.
+doc_auth.errors.choose_id_type_check: Sélectionnez le type de document dont vous disposez
doc_auth.errors.consent_form: Avant de pouvoir continuer, vous devez nous donner la permission. Veuillez cocher la case ci-dessous, puis cliquez sur Suite.
doc_auth.errors.doc_type_not_supported_heading: Nous n’acceptons que les permis de conduire ou les cartes d’identité délivrées par un État
doc_auth.errors.doc.doc_type_check: Votre permis de conduire ou votre carte d’identité doit être délivré par un État ou un territoire des États-Unis. Nous n’acceptons pas d’autres pièces d’identité, comme les passeports ou les cartes d’identité militaires.
@@ -581,6 +582,8 @@ doc_auth.errors.upload_error: Désolé, il y a eu un problème de notre côté.
doc_auth.forms.change_file: Changer de fichier
doc_auth.forms.choose_file_html: Faites glisser le fichier ici ou choisissez dans un dossier
doc_auth.forms.doc_success: Nous avons vérifié vos informations
+doc_auth.forms.id_type_preference.drivers_license: Permis de conduire ou carte d’identité d’un État
+doc_auth.forms.id_type_preference.passport: Passeport américain
doc_auth.forms.selected_file: Fichier sélectionné
doc_auth.headers.expired_id: Votre pièce d’identité est peut-être périmée
doc_auth.headers.general.network_error: Nous rencontrons des difficultés techniques
@@ -594,6 +597,7 @@ doc_auth.headings.back: Verso de votre permis de conduire ou de votre carte d’
doc_auth.headings.capture_complete: Nous avons vérifié votre pièce d’identité
doc_auth.headings.capture_scan_warning_html: Nous n’avons pas pu lire le code-barres de votre pièce d’identité. Si les informations ci-dessous ne sont pas correctes, veuillez %{link_html} de votre carte d’identité délivrée par l’État.
doc_auth.headings.capture_scan_warning_link: télécharger de nouvelles photos
+doc_auth.headings.choose_id_type: Choisir le type de votre pièce d’identité
doc_auth.headings.document_capture: Ajoutez des photos de votre permis de conduire ou de votre carte d’identité nationale
doc_auth.headings.document_capture_back: Verso de votre pièce d’identité
doc_auth.headings.document_capture_front: Recto de votre carte d’identité
@@ -635,6 +639,7 @@ doc_auth.info.capture_status_capturing: Prise de la photo
doc_auth.info.capture_status_none: Alignez
doc_auth.info.capture_status_small_document: Approchez-vous
doc_auth.info.capture_status_tap_to_capture: Appuyez pour prendre la photo
+doc_auth.info.choose_id_type: Sélectionnez le type de document dont vous disposez. Vous devrez prendre des photos de votre pièce d’identité pour confirmer votre identité.
doc_auth.info.exit.with_sp: Quitter %{app_name} et revenir à %{sp_name}
doc_auth.info.exit.without_sp: Quitter la vérification d’identité et accéder à la page de votre compte
doc_auth.info.getting_started_html: '%{sp_name} doit s’assurer qu’il s’agit bien de vous et non de quelqu’un qui se fait passer pour vous. %{link_html}'
@@ -644,6 +649,7 @@ doc_auth.info.how_to_verify_mobile: Vous avez la possibilité de confirmer votre
doc_auth.info.how_to_verify_troubleshooting_options_header: Vous voulez en savoir plus sur la façon de confirmer votre identité?
doc_auth.info.hybrid_handoff: Nous recueillerons des informations vous concernant en lisant votre pièce d’identité délivrée par un État.
doc_auth.info.hybrid_handoff_ipp_html: 'Vous n’avez pas de téléphone portable? Vous pouvez confirmer votre identité dans un bureau de poste américain participant.'
+doc_auth.info.id_types_learn_more: En savoir plus sur les types de pièces d’identité que vous pouvez utiliser
doc_auth.info.image_loaded: Image chargée
doc_auth.info.image_loading: Image en cours de chargement
doc_auth.info.image_updated: Image mise à jour
diff --git a/config/locales/zh.yml b/config/locales/zh.yml
index 997492b7744..6f84c42c6fa 100644
--- a/config/locales/zh.yml
+++ b/config/locales/zh.yml
@@ -542,6 +542,7 @@ doc_auth.errors.camera.blocked: 你的镜头被遮住了。
doc_auth.errors.camera.blocked_detail_html: '允许 %{app_name} 进入你的相机来拍照。尝试再次拍照并给予许可。如果还不行,你可能需要检查自己设备的设置来给予许可。'
doc_auth.errors.camera.failed: 相机未开启,请再试一次。
doc_auth.errors.card_type: 再用你的驾照或州政府颁发的身份证件试一次。
+doc_auth.errors.choose_id_type_check: 选择你具备的身份证件类型
doc_auth.errors.consent_form: 在你能继续之前,你必须授予我们你的同意。请在下面的框打勾然后点击继续。
doc_auth.errors.doc_type_not_supported_heading: 我们只接受驾照或州政府颁发的 ID。
doc_auth.errors.doc.doc_type_check: 你的驾照或身份证件必须是美国一个州或属地颁发的。我们不接受任何其他形式的身份证件,比如护照和军队身份证件。
@@ -592,6 +593,8 @@ doc_auth.errors.upload_error: 抱歉,我们这边出错了。
doc_auth.forms.change_file: 更改文件
doc_auth.forms.choose_file_html: 将文件拖到此处或者从文件夹中选择。
doc_auth.forms.doc_success: 我们验证了你的信息
+doc_auth.forms.id_type_preference.drivers_license: 美国驾照或州身份证件
+doc_auth.forms.id_type_preference.passport: 美国护照本
doc_auth.forms.selected_file: 被选文件
doc_auth.headers.expired_id: 你的身份证件可能已过期
doc_auth.headers.general.network_error: 我们目前遇到技术困难
@@ -605,6 +608,7 @@ doc_auth.headings.back: 驾照或州政府颁发身份证件的背面。
doc_auth.headings.capture_complete: 我们验证了你的身份文件
doc_auth.headings.capture_scan_warning_html: 我们读取不到你身份证件上的条形码。如果以下信息不正确,请将州政府颁发的身份证件%{link_html}。
doc_auth.headings.capture_scan_warning_link: 上传新照片
+doc_auth.headings.choose_id_type: 选择你的身份证件类型
doc_auth.headings.document_capture: 添加你身份证件的照片
doc_auth.headings.document_capture_back: 你身份证件的背面
doc_auth.headings.document_capture_front: 你身份证件的正面
@@ -646,6 +650,7 @@ doc_auth.info.capture_status_capturing: 扫描中
doc_auth.info.capture_status_none: 对齐
doc_auth.info.capture_status_small_document: 靠近一些
doc_auth.info.capture_status_tap_to_capture: 点击来扫描
+doc_auth.info.choose_id_type: 选择你具备的身份证件类型你将需要拍你身份证件的照片来验证身份。
doc_auth.info.exit.with_sp: 退出 %{app_name} 并返回 %{sp_name}
doc_auth.info.exit.without_sp: 退出身份验证并到你的账户页面
doc_auth.info.getting_started_html: '%{sp_name} 需要确保你是你,而不是别人冒充你。 了解更多有关验证你身份的信息 %{link_html}'
@@ -655,6 +660,7 @@ doc_auth.info.how_to_verify_mobile: 你可以选择用你的手机在网上验
doc_auth.info.how_to_verify_troubleshooting_options_header: 想对验证身份获得更多了解吗?
doc_auth.info.hybrid_handoff: 我们将通过读取州政府颁发给你的身份证件来收集你的信息。
doc_auth.info.hybrid_handoff_ipp_html: '没有手机?你可以去一个美国邮局验证身份。'
+doc_auth.info.id_types_learn_more: 了解有关你能使用的身份证件类型的更多信息
doc_auth.info.image_loaded: 图像已加载
doc_auth.info.image_loading: 图像加载中
doc_auth.info.image_updated: 图像已上传
diff --git a/config/routes.rb b/config/routes.rb
index 4201dae14e7..31457b2e174 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -391,6 +391,8 @@
get '/hybrid_mobile/socure/document_capture_errors' => 'hybrid_mobile/socure/errors#show', as: :hybrid_mobile_socure_document_capture_errors
get '/hybrid_handoff' => 'hybrid_handoff#show'
put '/hybrid_handoff' => 'hybrid_handoff#update'
+ get '/choose_id_type' => 'choose_id_type#show'
+ put '/choose_id_type' => 'choose_id_type#update'
get '/link_sent' => 'link_sent#show'
put '/link_sent' => 'link_sent#update'
get '/link_sent/poll' => 'link_sent_poll#show'
diff --git a/spec/controllers/idv/choose_id_type_controller_spec.rb b/spec/controllers/idv/choose_id_type_controller_spec.rb
new file mode 100644
index 00000000000..7f1916055ed
--- /dev/null
+++ b/spec/controllers/idv/choose_id_type_controller_spec.rb
@@ -0,0 +1,133 @@
+require 'rails_helper'
+
+RSpec.describe Idv::ChooseIdTypeController do
+ include FlowPolicyHelper
+
+ let(:user) { create(:user) }
+
+ before do
+ stub_sign_in(user)
+ stub_up_to(:hybrid_handoff, idv_session: subject.idv_session)
+ stub_analytics
+ end
+
+ describe '#step info' do
+ it 'returns a valid StepInfo object' do
+ expect(Idv::ChooseIdTypeController.step_info).to be_valid
+ end
+ end
+
+ describe 'before actions' do
+ it 'includes redirect_if_passport_not_available before_action' do
+ expect(subject).to have_actions(
+ :before,
+ :redirect_if_passport_not_available,
+ )
+ end
+ end
+
+ describe '#show' do
+ context 'passport is not available' do
+ it 'redirects to how to verify' do
+ subject.idv_session.passport_allowed = false
+
+ get :show
+
+ expect(response).to redirect_to(idv_how_to_verify_url)
+ end
+ end
+
+ context 'passport is available' do
+ let(:analytics_name) { :idv_doc_auth_choose_id_type_visited }
+ let(:analytics_args) do
+ {
+ step: 'choose_id_type',
+ analytics_id: 'Doc Auth',
+ flow_path: 'standard',
+ }
+ end
+
+ it 'renders the show template' do
+ subject.idv_session.passport_allowed = true
+
+ get :show
+
+ expect(response).to render_template :show
+ end
+
+ it 'sends analytics_visited event' do
+ subject.idv_session.passport_allowed = true
+
+ get :show
+
+ expect(@analytics).to have_logged_event(analytics_name, analytics_args)
+ end
+ end
+ end
+
+ describe '#update' do
+ let(:chosen_id_type) { 'drivers_license' }
+ let(:analytics_name) { :idv_doc_auth_choose_id_type_submitted }
+ let(:analytics_args) do
+ {
+ success: true,
+ step: 'choose_id_type',
+ analytics_id: 'Doc Auth',
+ flow_path: 'standard',
+ chosen_id_type: chosen_id_type,
+ }
+ end
+
+ let(:params) do
+ { doc_auth: { choose_id_type_preference: chosen_id_type } }
+ end
+
+ before do
+ allow(subject.idv_session).to receive(:passport_allowed).and_return(true)
+ end
+
+ it 'invalidates future steps' do
+ expect(subject).to receive(:clear_future_steps!)
+
+ put :update, params: params
+ end
+
+ it 'sends analytics submitted event for id choice' do
+ put :update, params: params
+
+ expect(@analytics).to have_logged_event(analytics_name, analytics_args)
+ end
+
+ context 'user selects drivers license' do
+ it 'sets idv_session.passport_requested to false' do
+ put :update, params: params
+
+ expect(subject.idv_session.passport_requested).to eq(false)
+ end
+
+ it 'redirects to document capture session' do
+ put :update, params: params
+
+ expect(response).to redirect_to(idv_document_capture_url)
+ end
+ end
+
+ context 'user selects passport' do
+ let(:chosen_id_type) { 'passport' }
+
+ it 'sets idv_session.passport_requested to true' do
+ put :update, params: params
+
+ expect(subject.idv_session.passport_requested).to eq(true)
+ end
+
+ # currently we do not have a passport route so it redirects to ipp route
+ # change when the new passport is added
+ it 'redirects to passport document capture' do
+ put :update, params: params
+
+ expect(response).to redirect_to(idv_document_capture_url)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/idv/hybrid_handoff_controller_spec.rb b/spec/controllers/idv/hybrid_handoff_controller_spec.rb
index 00a45e6f853..3315e00fd6d 100644
--- a/spec/controllers/idv/hybrid_handoff_controller_spec.rb
+++ b/spec/controllers/idv/hybrid_handoff_controller_spec.rb
@@ -359,6 +359,28 @@
expect(@analytics).to have_logged_event(analytics_name, analytics_args)
end
+
+ context 'passports are not enabled' do
+ before do
+ allow(subject.idv_session).to receive(:passport_allowed).and_return(false)
+ end
+ it 'redirects to choose id type url' do
+ put :update, params: params
+
+ expect(response).to redirect_to(idv_document_capture_url)
+ end
+ end
+
+ context 'passports are enabled' do
+ before do
+ allow(subject.idv_session).to receive(:passport_allowed).and_return(true)
+ end
+ it 'redirects to choose id type url' do
+ put :update, params: params
+
+ expect(response).to redirect_to(idv_choose_id_type_url)
+ end
+ end
end
end
end
diff --git a/spec/controllers/idv/welcome_controller_spec.rb b/spec/controllers/idv/welcome_controller_spec.rb
index 793e1f6092a..26a66b36c83 100644
--- a/spec/controllers/idv/welcome_controller_spec.rb
+++ b/spec/controllers/idv/welcome_controller_spec.rb
@@ -100,6 +100,18 @@
expect(subject.idv_session.proofing_started_at).to eq(Time.zone.now.iso8601)
end
+ context 'passports are enabled' do
+ before do
+ allow(IdentityConfig.store).to receive(:doc_auth_passports_enabled).and_return(true)
+ end
+
+ it 'sets passport_allowed in idv session' do
+ get :show
+
+ expect(subject.idv_session.passport_allowed).to eq(true)
+ end
+ end
+
context 'welcome already visited' do
before do
subject.idv_session.welcome_visited = true
diff --git a/spec/features/idv/doc_auth/choose_id_type_spec.rb b/spec/features/idv/doc_auth/choose_id_type_spec.rb
new file mode 100644
index 00000000000..69cff5195ec
--- /dev/null
+++ b/spec/features/idv/doc_auth/choose_id_type_spec.rb
@@ -0,0 +1,38 @@
+require 'rails_helper'
+
+RSpec.feature 'choose id type step error checking' do
+ include DocAuthHelper
+ context 'desktop flow', :js do
+ before do
+ allow(IdentityConfig.store).to receive(:doc_auth_passports_enabled).and_return(true)
+ sign_in_and_2fa_user
+ complete_doc_auth_steps_before_hybrid_handoff_step
+ end
+
+ it 'shows choose id type screen and continues after passport option' do
+ expect(page).to have_content(t('doc_auth.headings.upload_from_computer'))
+ click_on t('forms.buttons.upload_photos')
+ expect(page).to have_current_path(idv_choose_id_type_url)
+ choose(t('doc_auth.forms.id_type_preference.passport'))
+ click_on t('forms.buttons.continue')
+ expect(page).to have_current_path(idv_document_capture_url)
+ end
+ end
+ context 'mobile flow', :js, driver: :headless_chrome_mobile do
+ before do
+ allow(IdentityConfig.store).to receive(:doc_auth_passports_enabled).and_return(true)
+ allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true)
+ allow(IdentityConfig.store).to receive(:in_person_proofing_opt_in_enabled).and_return(true)
+ sign_in_and_2fa_user
+ complete_doc_auth_steps_before_agreement_step
+ complete_agreement_step
+ end
+
+ it 'shows choose id type screen and continues after drivers license option' do
+ expect(page).to have_current_path(idv_choose_id_type_url)
+ choose(t('doc_auth.forms.id_type_preference.drivers_license'))
+ click_on t('forms.buttons.continue')
+ expect(page).to have_current_path(idv_document_capture_url)
+ end
+ end
+end
diff --git a/spec/forms/idv/choose_id_type_form_spec.rb b/spec/forms/idv/choose_id_type_form_spec.rb
new file mode 100644
index 00000000000..746b618673f
--- /dev/null
+++ b/spec/forms/idv/choose_id_type_form_spec.rb
@@ -0,0 +1,39 @@
+require 'rails_helper'
+
+RSpec.describe Idv::ChooseIdTypeForm do
+ let(:subject) { Idv::ChooseIdTypeForm.new }
+
+ describe '#submit' do
+ context 'when the form is valid' do
+ let(:params) { { choose_id_type_preference: 'passport' } }
+
+ it 'returns a successful form response' do
+ result = subject.submit(params)
+
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(true)
+ expect(result.errors).to be_empty
+ end
+ end
+ context 'when the choose_id_type_preference is nil' do
+ let(:params) { { choose_id_type_preference: nil } }
+ it 'returns a failed form response when id type is nil' do
+ result = subject.submit(params)
+
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors).not_to be_empty
+ end
+ end
+ context 'when the choose_id_type_preference is not supported type' do
+ let(:params) { { choose_id_type_preference: 'unknown-type' } }
+ it 'returns a failed form response when id type is nil' do
+ result = subject.submit(params)
+
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors).not_to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/support/flow_policy_helper.rb b/spec/support/flow_policy_helper.rb
index e99ad069d12..9c63c52c7e6 100644
--- a/spec/support/flow_policy_helper.rb
+++ b/spec/support/flow_policy_helper.rb
@@ -17,6 +17,9 @@ def stub_step(key:, idv_session:)
idv_session.skip_doc_auth_from_how_to_verify = false
when :hybrid_handoff
idv_session.flow_path = 'standard'
+ when :choose_id_type
+ idv_session.flow_path = 'standard'
+ idv_session.passport_allowed == true
when :link_sent
idv_session.flow_path = 'hybrid'
idv_session.pii_from_doc = Pii::StateId.new(**Idp::Constants::MOCK_IDV_APPLICANT)
@@ -69,6 +72,8 @@ def keys_up_to(key:)
%i[welcome agreement how_to_verify]
when :hybrid_handoff
%i[welcome agreement how_to_verify hybrid_handoff]
+ when :choose_id_type
+ %i[welcome agreement how_to_verify hybrid_handoff choose_id_type]
when :link_sent
%i[welcome agreement how_to_verify hybrid_handoff link_sent]
when :document_capture