diff --git a/Gemfile.lock b/Gemfile.lock index 9b1844352da..d31c1a552bd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -484,7 +484,7 @@ GEM nio4r (~> 2.0) raabro (1.4.0) racc (1.7.3) - rack (3.0.8) + rack (3.0.9.1) rack-cors (2.0.1) rack (>= 2.0.0) rack-headers_filter (0.0.1) diff --git a/app/controllers/accounts/connected_accounts_controller.rb b/app/controllers/accounts/connected_accounts_controller.rb index ada1d08095e..00cd3891257 100644 --- a/app/controllers/accounts/connected_accounts_controller.rb +++ b/app/controllers/accounts/connected_accounts_controller.rb @@ -8,7 +8,6 @@ class ConnectedAccountsController < ApplicationController def show @presenter = AccountShowPresenter.new( decrypted_pii: nil, - personal_key: flash[:personal_key], sp_session_request_url: sp_session_request_url_with_updated_params, sp_name: decorated_sp_session.sp_name, user: current_user, diff --git a/app/controllers/accounts/history_controller.rb b/app/controllers/accounts/history_controller.rb index 542626588b1..8a2a4e941d6 100644 --- a/app/controllers/accounts/history_controller.rb +++ b/app/controllers/accounts/history_controller.rb @@ -8,7 +8,6 @@ class HistoryController < ApplicationController def show @presenter = AccountShowPresenter.new( decrypted_pii: nil, - personal_key: flash[:personal_key], sp_session_request_url: sp_session_request_url_with_updated_params, sp_name: decorated_sp_session.sp_name, user: current_user, diff --git a/app/controllers/accounts/two_factor_authentication_controller.rb b/app/controllers/accounts/two_factor_authentication_controller.rb index fb58dcbc14a..9b6122b9135 100644 --- a/app/controllers/accounts/two_factor_authentication_controller.rb +++ b/app/controllers/accounts/two_factor_authentication_controller.rb @@ -9,7 +9,6 @@ def show session[:account_redirect_path] = account_two_factor_authentication_path @presenter = AccountShowPresenter.new( decrypted_pii: nil, - personal_key: flash[:personal_key], sp_session_request_url: sp_session_request_url_with_updated_params, sp_name: decorated_sp_session.sp_name, user: current_user, diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 44f72bcf444..95b3f1acff9 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -11,7 +11,6 @@ def show cacher = Pii::Cacher.new(current_user, user_session) @presenter = AccountShowPresenter.new( decrypted_pii: cacher.fetch(current_user.active_or_pending_profile&.id), - personal_key: flash[:personal_key], sp_session_request_url: sp_session_request_url_with_updated_params, sp_name: decorated_sp_session.sp_name, user: current_user, diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 66bb1f601d1..d15585666e7 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -405,7 +405,7 @@ def service_provider_mfa_policy service_provider: sp_from_sp_session, auth_methods_session:, aal_level_requested: sp_session[:aal_level_requested], - piv_cac_requested: sp_session[:piv_cac_requested], + piv_cac_requested: resolved_authn_context_result.hspd12?, phishing_resistant_requested: resolved_authn_context_result.phishing_resistant?, ) end diff --git a/app/controllers/concerns/fraud_review_concern.rb b/app/controllers/concerns/fraud_review_concern.rb index 1f98fd1eda5..c70dce1b2fc 100644 --- a/app/controllers/concerns/fraud_review_concern.rb +++ b/app/controllers/concerns/fraud_review_concern.rb @@ -4,11 +4,9 @@ module FraudReviewConcern delegate :fraud_check_failed?, :fraud_review_pending?, :fraud_rejection?, - :ipp_fraud_review_pending?, to: :fraud_review_checker def handle_fraud - in_person_handle_pending_fraud_review handle_pending_fraud_review handle_fraud_rejection end @@ -16,25 +14,20 @@ def handle_fraud def handle_pending_fraud_review # If the user has not passed IPP at a post office, allow them to # complete another enrollment by not redirecting to please call - return if in_person_can_perform_fraud_review? + # or rejection screen + return if in_person_prevent_fraud_redirection? redirect_to_fraud_review if fraud_review_pending? end def handle_fraud_rejection + return if in_person_prevent_fraud_redirection? redirect_to_fraud_rejection if fraud_rejection? end - def in_person_handle_pending_fraud_review - return unless in_person_can_perform_fraud_review? - if fraud_review_pending? && current_user.in_person_enrollment_status == 'passed' - redirect_to_fraud_review - end - end - - def in_person_can_perform_fraud_review? + def in_person_prevent_fraud_redirection? IdentityConfig.store.in_person_proofing_enforce_tmx && - current_user.in_person_enrollment_status != 'canceled' && - !current_user.in_person_enrollment_status.nil? + !current_user.in_person_enrollment_status.nil? && + current_user.in_person_enrollment_status != 'passed' end def redirect_to_fraud_review diff --git a/app/controllers/concerns/remember_device_concern.rb b/app/controllers/concerns/remember_device_concern.rb index df20d56a90a..92e929eebd9 100644 --- a/app/controllers/concerns/remember_device_concern.rb +++ b/app/controllers/concerns/remember_device_concern.rb @@ -9,7 +9,7 @@ def save_remember_device_preference(remember_device_preference) return if remember_device_preference != '1' && remember_device_preference != 'true' cookies.encrypted[:remember_device] = { value: RememberDeviceCookie.new(user_id: current_user.id, created_at: Time.zone.now).to_json, - expires: remember_device_cookie_expiration, + expires: IdentityConfig.store.remember_device_expiration_hours_aal_1.hours.from_now, } end @@ -83,12 +83,4 @@ def handle_valid_remember_device_analytics(cookie_created_at:) cookie_age_seconds: (Time.zone.now - cookie_created_at).to_i, ) end - - def remember_device_cookie_expiration - if IdentityConfig.store.set_remember_device_session_expiration - nil - else - IdentityConfig.store.remember_device_expiration_hours_aal_1.hours.from_now - end - end end diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index c3496720b99..c779eec499e 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -9,7 +9,6 @@ def show analytics.events_visit @presenter = AccountShowPresenter.new( decrypted_pii: nil, - personal_key: nil, sp_session_request_url: sp_session_request_url_with_updated_params, sp_name: decorated_sp_session.sp_name, user: current_user, diff --git a/app/controllers/saml_idp_controller.rb b/app/controllers/saml_idp_controller.rb index 42bbceaace9..d3265294ac8 100644 --- a/app/controllers/saml_idp_controller.rb +++ b/app/controllers/saml_idp_controller.rb @@ -104,15 +104,8 @@ def saml_metadata SamlEndpoint.new(params[:path_year]).saml_metadata end - def ialmax_request_with_ial1_acr_and_pii_requested_and_locked? - requested_ial == 'ialmax' && - current_user.identity_verified? && - !Pii::Cacher.new(current_user, user_session).exists_in_session? - end - def prompt_for_password_if_ial2_request_and_pii_locked - return unless pii_requested_but_locked? || - ialmax_request_with_ial1_acr_and_pii_requested_and_locked? + return unless pii_requested_but_locked? redirect_to capture_password_url end diff --git a/app/controllers/users/passwords_controller.rb b/app/controllers/users/passwords_controller.rb index ae838e6078e..8fab9ad5cb7 100644 --- a/app/controllers/users/passwords_controller.rb +++ b/app/controllers/users/passwords_controller.rb @@ -51,8 +51,13 @@ def handle_valid_password # that the user remains authenticated. bypass_sign_in current_user - flash[:personal_key] = @update_user_password_form.personal_key - redirect_to account_url, flash: { info: t('notices.password_changed') } + flash[:info] = t('notices.password_changed') + if @update_user_password_form.personal_key.present? + user_session[:personal_key] = @update_user_password_form.personal_key + redirect_to manage_personal_key_url + else + redirect_to account_url + end end def send_password_reset_risc_event diff --git a/app/controllers/users/piv_cac_login_controller.rb b/app/controllers/users/piv_cac_login_controller.rb index 7f627d58b9c..9e9b542ef16 100644 --- a/app/controllers/users/piv_cac_login_controller.rb +++ b/app/controllers/users/piv_cac_login_controller.rb @@ -58,7 +58,7 @@ def piv_cac_login_form @piv_cac_login_form ||= UserPivCacLoginForm.new( token: params[:token], nonce: piv_cac_nonce, - piv_cac_required: sp_session[:piv_cac_requested], + piv_cac_required: resolved_authn_context_result.hspd12?, ) end diff --git a/app/controllers/users/verify_password_controller.rb b/app/controllers/users/verify_password_controller.rb index ffde05879a6..fc017680bcb 100644 --- a/app/controllers/users/verify_password_controller.rb +++ b/app/controllers/users/verify_password_controller.rb @@ -35,10 +35,10 @@ def confirm_personal_key end def handle_success(result) - flash[:personal_key] = result.extra[:personal_key] + user_session[:personal_key] = result.extra[:personal_key] irs_attempts_api_tracker.idv_personal_key_generated reactivate_account_session.clear - redirect_to account_url + redirect_to manage_personal_key_url end def verify_password_form diff --git a/app/javascript/packages/document-capture/components/acuant-capture.tsx b/app/javascript/packages/document-capture/components/acuant-capture.tsx index a004e9e83f4..3478f75ab8c 100644 --- a/app/javascript/packages/document-capture/components/acuant-capture.tsx +++ b/app/javascript/packages/document-capture/components/acuant-capture.tsx @@ -35,11 +35,11 @@ interface ImageAnalyticsPayload { /** * Image width, or null if unknown */ - width: number | null; + width?: number | null; /** * Image height, or null if unknown */ - height: number | null; + height?: number | null; /** * Mime type, or null if unknown */ @@ -65,7 +65,7 @@ interface ImageAnalyticsPayload { /** * Fingerprint of the image, base64 encoded SHA-256 digest */ - fingerprint: string | null; + fingerprint?: string | null; /** * @@ -512,9 +512,16 @@ function AcuantCapture( } function onSelfieCaptureSuccess({ image }: { image: string }) { + const analyticsPayload: ImageAnalyticsPayload = getAddAttemptAnalyticsPayload({ + mimeType: 'image/jpeg', // Acuant Web SDK currently encodes all images as JPEG + source: 'acuant', + size: getDecodedBase64ByteSize(image), + failedImageResubmission: false, + }); + trackEvent('idv_sdk_selfie_image_added', { captureAttempts }); - onChangeAndResetError(image); + onChangeAndResetError(image, analyticsPayload); onResetFailedCaptureAttempts(); setIsCapturingEnvironment(false); } diff --git a/app/javascript/packages/document-capture/components/document-side-acuant-capture.jsx b/app/javascript/packages/document-capture/components/document-side-acuant-capture.tsx similarity index 73% rename from app/javascript/packages/document-capture/components/document-side-acuant-capture.jsx rename to app/javascript/packages/document-capture/components/document-side-acuant-capture.tsx index 4580197e70d..c94c1504ea9 100644 --- a/app/javascript/packages/document-capture/components/document-side-acuant-capture.jsx +++ b/app/javascript/packages/document-capture/components/document-side-acuant-capture.tsx @@ -1,24 +1,25 @@ +import { useContext } from 'react'; import { t } from '@18f/identity-i18n'; import { FormError, FormStepsContext } from '@18f/identity-form-steps'; -import { useContext } from 'react'; +import type { + FormStepError, + OnErrorCallback, + RegisterFieldCallback, +} from '@18f/identity-form-steps'; import AcuantCapture from './acuant-capture'; -/** @typedef {import('@18f/identity-form-steps').FormStepError<*>} FormStepError */ -/** @typedef {import('@18f/identity-form-steps').RegisterFieldCallback} RegisterFieldCallback */ -/** @typedef {import('@18f/identity-form-steps').OnErrorCallback} OnErrorCallback */ - -/** - * @typedef DocumentSideAcuantCaptureProps - * - * @prop {'front'|'back'|'selfie'} side - * @prop {RegisterFieldCallback} registerField - * @prop {Blob|string|null|undefined} value - * @prop {(nextValues:{[key:string]: Blob|string|null|undefined})=>void} onChange Update values, - * merging with existing values. - * @prop {FormStepError[]} errors - * @prop {OnErrorCallback} onError - * @prop {string=} className - */ +interface DocumentSideAcuantCaptureProps { + side: 'front' | 'back' | 'selfie'; + registerField: RegisterFieldCallback; + value: Blob | string | null | undefined; + /** + * Update values, merging with existing values. + */ + onChange: (nextValues: { [key: string]: Blob | string | null | undefined }) => void; + errors: FormStepError<{ front: string; back: string; selfie: string }>[]; + onError: OnErrorCallback; + className?: string; +} /** * An error representing user declined access to camera. @@ -31,9 +32,6 @@ export class CameraAccessDeclinedError extends FormError { } } -/** - * @param {DocumentSideAcuantCaptureProps} props Props object. - */ function DocumentSideAcuantCapture({ side, registerField, @@ -42,7 +40,7 @@ function DocumentSideAcuantCapture({ errors, onError, className, -}) { +}: DocumentSideAcuantCaptureProps) { const error = errors.find(({ field }) => field === side)?.error; const { changeStepCanComplete } = useContext(FormStepsContext); return ( diff --git a/app/javascript/packages/phone-input/package.json b/app/javascript/packages/phone-input/package.json index 893a9b9fe43..1b3000d2846 100644 --- a/app/javascript/packages/phone-input/package.json +++ b/app/javascript/packages/phone-input/package.json @@ -4,7 +4,7 @@ "version": "1.0.0", "dependencies": { "intl-tel-input": "^17.0.19", - "libphonenumber-js": "^1.10.56" + "libphonenumber-js": "^1.10.57" }, "sideEffects": [ "./index.ts" diff --git a/app/presenters/account_show_presenter.rb b/app/presenters/account_show_presenter.rb index 17ac0ccbd5c..0a5025603aa 100644 --- a/app/presenters/account_show_presenter.rb +++ b/app/presenters/account_show_presenter.rb @@ -1,11 +1,9 @@ class AccountShowPresenter - attr_reader :user, :decrypted_pii, :personal_key, :locked_for_session, :pii, - :sp_session_request_url, :sp_name + attr_reader :user, :decrypted_pii, :locked_for_session, :pii, :sp_session_request_url, :sp_name - def initialize(decrypted_pii:, personal_key:, sp_session_request_url:, sp_name:, user:, + def initialize(decrypted_pii:, sp_session_request_url:, sp_name:, user:, locked_for_session:) @decrypted_pii = decrypted_pii - @personal_key = personal_key @user = user @sp_name = sp_name @sp_session_request_url = sp_session_request_url @@ -13,10 +11,6 @@ def initialize(decrypted_pii:, personal_key:, sp_session_request_url:, sp_name:, @pii = determine_pii end - def show_personal_key_partial? - personal_key.present? - end - def show_password_reset_partial? user.password_reset_profile.present? end @@ -41,7 +35,6 @@ def show_gpo_partial? def showing_any_partials? show_service_provider_continue_partial? || show_password_reset_partial? || - show_personal_key_partial? || show_gpo_partial? end diff --git a/app/services/doc_auth/mock/result_response.rb b/app/services/doc_auth/mock/result_response.rb index b1778865ada..4f1d388866a 100644 --- a/app/services/doc_auth/mock/result_response.rb +++ b/app/services/doc_auth/mock/result_response.rb @@ -18,6 +18,7 @@ def initialize(uploaded_file, config, selfie_required = false) doc_type_supported: id_type_supported?, selfie_live: selfie_live?, selfie_quality_good: selfie_quality_good?, + selfie_status: selfie_status, extra: { doc_auth_result: doc_auth_result, portrait_match_results: portrait_match_results, @@ -33,12 +34,12 @@ def errors @errors ||= begin file_data = parsed_data_from_uploaded_file - if file_data.blank? || attention_with_barcode? + if file_data.blank? {} else doc_auth_result = file_data.dig('doc_auth_result') image_metrics = file_data.dig('image_metrics') - failed = file_data.dig('failed_alerts') + failed = failed_file_data(file_data.dig('failed_alerts')&.dup) passed = file_data.dig('passed_alerts') face_match_result = file_data.dig('portrait_match_results', 'FaceMatchResult') classification_info = file_data.dig('classification_info') @@ -85,7 +86,7 @@ def pii_from_doc end def success? - (errors.blank? || attention_with_barcode?) && id_type_supported? + doc_auth_success? && (selfie_check_performed? ? selfie_passed? : true) end def attention_with_barcode? @@ -254,6 +255,13 @@ def create_response_info( extra: { liveness_checking_required: liveness_enabled }, }.compact end + + def failed_file_data(failed_alerts_data) + if attention_with_barcode? + failed_alerts_data&.delete(ATTENTION_WITH_BARCODE_ALERT) + end + failed_alerts_data + end end end end diff --git a/app/services/id_token_builder.rb b/app/services/id_token_builder.rb index 368d4e8d0a9..cf582448c21 100644 --- a/app/services/id_token_builder.rb +++ b/app/services/id_token_builder.rb @@ -38,12 +38,14 @@ def jwt_payload def id_token_claims { acr: acr, + vot: (vot if sp_requests_vot?), + vtm: (IdentityConfig.store.vtm_url if sp_requests_vot?), nonce: identity.nonce, aud: identity.service_provider, jti: SecureRandom.urlsafe_base64, at_hash: hash_token(identity.access_token), c_hash: hash_token(code), - } + }.compact end def timestamp_claims @@ -55,24 +57,43 @@ def timestamp_claims end def acr - ial = identity.ial - case ial - when Idp::Constants::IAL_MAX then determine_ial_max_acr - when Idp::Constants::IAL1 then Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF - when Idp::Constants::IAL2 then Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF + return nil unless identity.acr_values.present? + + if resolved_authn_context_result.ialmax? + determine_ial_max_acr.name + elsif resolved_authn_context_result.identity_proofing? + Vot::LegacyComponentValues::IAL2.name else - raise "Unknown ial #{ial}" + Vot::LegacyComponentValues::IAL1.name end end + def sp_requests_vot? + return false unless identity.vtr.present? + IdentityConfig.store.use_vot_in_sp_requests + end + + def vot + return nil unless identity.vtr.present? + resolved_authn_context_result.component_values.map(&:name).join('.') + end + def determine_ial_max_acr if identity.user.identity_verified? - Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF + Vot::LegacyComponentValues::IAL2 else - Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF + Vot::LegacyComponentValues::IAL1 end end + def resolved_authn_context_result + @resolved_authn_context_result ||= AuthnContextResolver.new( + service_provider: identity.service_provider_record, + vtr: [identity.vtr], + acr_values: identity.acr_values, + ).resolve + end + def expires now.to_i + ttl end diff --git a/app/services/proofing/aamva/request/verification_request.rb b/app/services/proofing/aamva/request/verification_request.rb index 256b8f641e6..2035d41588b 100644 --- a/app/services/proofing/aamva/request/verification_request.rb +++ b/app/services/proofing/aamva/request/verification_request.rb @@ -105,6 +105,9 @@ def build_request_headers end def message_destination_id + # Note: AAMVA uses this field to route the request to the appropriate state DMV. + # We are required to use 'P6' as the jurisdiction when we make requests + # in the AAMVA CERT/Test environment. return 'P6' if config.cert_enabled.to_s == 'true' applicant.state_id_data.state_id_jurisdiction end diff --git a/app/services/store_sp_metadata_in_session.rb b/app/services/store_sp_metadata_in_session.rb index e42746c95b5..f1dbf5e3ff5 100644 --- a/app/services/store_sp_metadata_in_session.rb +++ b/app/services/store_sp_metadata_in_session.rb @@ -34,26 +34,10 @@ def sp_request @sp_request ||= ServiceProviderRequestProxy.from_uuid(request_id) end - def ial_value - return nil unless parsed_vot - - if parsed_vot&.ialmax? - 0 - elsif parsed_vot&.identity_proofing? - 2 - elsif parsed_vot - 1 - end - end - def ial2_value parsed_vot&.identity_proofing? end - def ialmax_value - parsed_vot&.ialmax? - end - def aal_level_requested_value return nil unless parsed_vot @@ -68,10 +52,6 @@ def piv_cac_requested_value parsed_vot&.hspd12? end - def phishing_resistant_value - parsed_vot&.phishing_resistant? - end - def biometric_comparison_required_value parsed_vot&.biometric_comparison? || sp_request&.biometric_comparison_required end @@ -82,12 +62,8 @@ def update_session request_url: sp_request.url, request_id: sp_request.uuid, requested_attributes: sp_request.requested_attributes, - ial: ial_value, - ial2: ial2_value, - ialmax: ialmax_value, aal_level_requested: aal_level_requested_value, piv_cac_requested: piv_cac_requested_value, - phishing_resistant_requested: phishing_resistant_value, biometric_comparison_required: biometric_comparison_required_value, acr_values: sp_request.acr_values, vtr: sp_request.vtr, diff --git a/app/services/vot/legacy_component_values.rb b/app/services/vot/legacy_component_values.rb index 785c70a7bf3..d21b47ccc78 100644 --- a/app/services/vot/legacy_component_values.rb +++ b/app/services/vot/legacy_component_values.rb @@ -53,7 +53,7 @@ module LegacyComponentValues ) AAL2_PHISHING_RESISTANT = ComponentValue.new( name: Saml::Idp::Constants::AAL2_PHISHING_RESISTANT_AUTHN_CONTEXT_CLASSREF, - description: 'Legacy AAL2 with phishing resitance', + description: 'Legacy AAL2 with phishing resistance', implied_component_values: [], requirements: [:aal2, :phishing_resistant], ) diff --git a/app/views/accounts/_personal_key.html.erb b/app/views/accounts/_personal_key.html.erb deleted file mode 100644 index 7d61fa289d5..00000000000 --- a/app/views/accounts/_personal_key.html.erb +++ /dev/null @@ -1,8 +0,0 @@ -<%= render AlertComponent.new(type: :warning, class: 'margin-bottom-2', text_tag: 'div') do %> -

- <%= t('idv.messages.personal_key') %> -

-
- <%= presenter.personal_key %> -
-<% end %> diff --git a/app/views/accounts/show.html.erb b/app/views/accounts/show.html.erb index 1bf739dbda9..23d491c2340 100644 --- a/app/views/accounts/show.html.erb +++ b/app/views/accounts/show.html.erb @@ -2,10 +2,6 @@ <% if @presenter.showing_any_partials? %>
- <% if @presenter.show_personal_key_partial? %> - <%= render 'accounts/personal_key', presenter: @presenter %> - <% end %> - <% if @presenter.show_password_reset_partial? %> <%= render 'accounts/password_reset', presenter: @presenter %> <% end %> diff --git a/app/views/idv/not_verified/show.html.erb b/app/views/idv/not_verified/show.html.erb index 81da87ddea8..9c65488feef 100644 --- a/app/views/idv/not_verified/show.html.erb +++ b/app/views/idv/not_verified/show.html.erb @@ -2,6 +2,7 @@ 'idv/shared/error', title: t('titles.failure.information_not_verified'), heading: t('idv.failure.verify.heading'), + action: { text: t('idv.failure.verify.exit', app_name: APP_NAME), url: :return_to_sp_failure_to_proof, method: :get }, ) do %>

<% if decorated_sp_session.sp_name.present? %> diff --git a/bin/oncall/email-deliveries b/bin/oncall/email-deliveries index 2be5c445f9b..5fd462a3c07 100755 --- a/bin/oncall/email-deliveries +++ b/bin/oncall/email-deliveries @@ -6,7 +6,6 @@ Dir.chdir(__dir__) { require 'bundler/setup' } require 'active_support' require 'active_support/core_ext/enumerable' # index_by require 'active_support/core_ext/integer/time' -require 'active_support/core_ext/string/access' require 'optparse' require 'terminal-table' @@ -69,7 +68,7 @@ class EmailDeliveries result.user_id, result.email_address_id, result.timestamp, - result.message_id.reverse.truncate(27).reverse, + result.message_id.slice(0..24), result.email_action, result.events.join(', '), ] diff --git a/config/application.yml.default b/config/application.yml.default index 9b82d3281e7..938086c6a9e 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -309,7 +309,6 @@ session_timeout_in_minutes: 15 session_timeout_warning_seconds: 150 session_total_duration_timeout_in_minutes: 720 ses_configuration_set_name: '' -set_remember_device_session_expiration: false sp_handoff_bounce_max_seconds: 2 show_unsupported_passkey_platform_authentication_setup: false show_user_attribute_deprecation_warnings: false @@ -340,6 +339,7 @@ verify_gpo_key_max_attempts: 5 verify_personal_key_attempt_window_in_minutes: 15 verify_personal_key_max_attempts: 5 version_headers_enabled: false +vtm_url: 'https://developer.login.gov/vot-trust-framework' use_dashboard_service_providers: false use_kms: false use_vot_in_sp_requests: false diff --git a/config/locales/idv/en.yml b/config/locales/idv/en.yml index ff153d1258a..543a20bf3b1 100644 --- a/config/locales/idv/en.yml +++ b/config/locales/idv/en.yml @@ -145,6 +145,7 @@ en: timeout: We are experiencing higher than usual wait time processing your request. Please try again. verify: + exit: Exit %{app_name} fail_link_html: Get help at %{sp_name} fail_text: to access services. heading: We couldn’t verify your identity @@ -258,8 +259,6 @@ en: timeframe_html: You’ll get a letter with a verification code in 5 to 10 days. otp_delivery_method_description: If you entered a landline above, please select “Phone call” below. - personal_key: This is your new personal key. Write it down and keep it in a safe - place. You will need it if you ever lose your password. phone: alert_html: 'Enter a phone number that is:' description: We’ll check this number with records and send you a one-time code. diff --git a/config/locales/idv/es.yml b/config/locales/idv/es.yml index d7f32050a59..d517b4ed74f 100644 --- a/config/locales/idv/es.yml +++ b/config/locales/idv/es.yml @@ -155,6 +155,7 @@ es: timeout: Estamos experimentando un tiempo de espera superior al habitual al procesar su solicitud. Inténtalo de nuevo. verify: + exit: Salir de %{app_name} fail_link_html: Obtenga ayuda en %{sp_name} fail_text: para acceder a los servicios. heading: No hemos podido verificar su identidad @@ -275,8 +276,6 @@ es: verificación en un plazo de 5 a 10 días. otp_delivery_method_description: Si ha introducido un teléfono fijo más arriba, seleccione “Llamada telefónica” más abajo. - personal_key: Esta es su nueva clave personal. Escríbala y guárdela en un lugar - seguro. La necesitará si pierde su contraseña. phone: alert_html: 'Introduzca un número de teléfono que sea:' description: Comprobaremos este número con los registros y le enviaremos un diff --git a/config/locales/idv/fr.yml b/config/locales/idv/fr.yml index 283c6958e1b..42755f8a338 100644 --- a/config/locales/idv/fr.yml +++ b/config/locales/idv/fr.yml @@ -161,6 +161,7 @@ fr: timeout: Le temps d’attente pour le traitement de votre demande est plus long que d’habitude Veuillez réessayer. verify: + exit: Quitter %{app_name} fail_link_html: Obtenez de l’aide auprès de %{sp_name} fail_text: pour accéder aux services. heading: Nous n’avons pas pu vérifier votre identité @@ -284,9 +285,6 @@ fr: vérification dans un délai de 5 à 10 jours. otp_delivery_method_description: Si vous avez saisi une ligne fixe ci-dessus, veuillez sélectionner « Appel téléphonique » ci-dessous. - personal_key: Il s’agit de votre nouvelle clé personnelle. Notez-la et - conservez-la dans un endroit sécuritaire. Vous en aurez besoin si vous - perdez votre mot de passe. phone: alert_html: 'Entrez un numéro de téléphone qui est :' description: Nous vérifierons ce numéro dans nos archives et vous enverrons un diff --git a/lib/identity_config.rb b/lib/identity_config.rb index 66cbe876517..423989b9d75 100644 --- a/lib/identity_config.rb +++ b/lib/identity_config.rb @@ -448,7 +448,6 @@ def self.build_store(config_map) config.add(:session_timeout_in_minutes, type: :integer) config.add(:session_timeout_warning_seconds, type: :integer) config.add(:session_total_duration_timeout_in_minutes, type: :integer) - config.add(:set_remember_device_session_expiration, type: :boolean) config.add(:show_unsupported_passkey_platform_authentication_setup, type: :boolean) config.add(:show_user_attribute_deprecation_warnings, type: :boolean) config.add(:skip_encryption_allowed_list, type: :json) @@ -500,6 +499,7 @@ def self.build_store(config_map) config.add(:version_headers_enabled, type: :boolean) config.add(:voice_otp_pause_time) config.add(:voice_otp_speech_rate) + config.add(:vtm_url) config.add(:weekly_auth_funnel_report_config, type: :json) @key_types = config.key_types diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb index e588287857c..5e796a918ce 100644 --- a/spec/controllers/accounts_controller_spec.rb +++ b/spec/controllers/accounts_controller_spec.rb @@ -94,7 +94,6 @@ presenter = AccountShowPresenter.new( decrypted_pii: nil, - personal_key: nil, sp_session_request_url: nil, sp_name: nil, user: user, @@ -149,7 +148,6 @@ presenter = AccountShowPresenter.new( decrypted_pii: nil, - personal_key: nil, sp_session_request_url: nil, sp_name: nil, user: user, diff --git a/spec/controllers/idv/please_call_controller_spec.rb b/spec/controllers/idv/please_call_controller_spec.rb index 5fe2103b454..42aa071fd6c 100644 --- a/spec/controllers/idv/please_call_controller_spec.rb +++ b/spec/controllers/idv/please_call_controller_spec.rb @@ -73,8 +73,8 @@ expect(response).to render_template :show end - it 'returns true from in_person_can_perform_fraud_review' do - expect(subject.in_person_can_perform_fraud_review?).to eq(true) + it 'returns false from in_person_prevent_fraud_redirection' do + expect(subject.in_person_prevent_fraud_redirection?).to eq(false) end it 'redirects a user who is not fraud review pending' do @@ -93,6 +93,22 @@ expect(response).to redirect_to(idv_not_verified_url) end + context 'user fails ipp' do + let!(:enrollment) { create(:in_person_enrollment, :failed, user: user, profile: profile) } + + it 'returns true from in_person_prevent_fraud_redirection' do + expect(subject.in_person_prevent_fraud_redirection?).to eq(true) + end + + it 'does not redirect a user who has been fraud rejected' do + profile.reject_for_fraud(notify_user: false) + + get :show + + expect(response).not_to redirect_to(idv_not_verified_url) + end + end + context 'in person proofing and tmx disabled' do let(:in_person_proofing_enabled) { true } let(:in_person_proofing_enforce_tmx) { false } @@ -105,8 +121,8 @@ and_return(in_person_proofing_enforce_tmx) end - it 'returns false from in_person_can_perform_fraud_review' do - expect(subject.in_person_can_perform_fraud_review?).to eq(false) + it 'returns false from in_person_prevent_fraud_redirection' do + expect(subject.in_person_prevent_fraud_redirection?).to eq(false) end end end diff --git a/spec/controllers/openid_connect/authorization_controller_spec.rb b/spec/controllers/openid_connect/authorization_controller_spec.rb index 9f91bf0f566..a85faf7b05c 100644 --- a/spec/controllers/openid_connect/authorization_controller_spec.rb +++ b/spec/controllers/openid_connect/authorization_controller_spec.rb @@ -1081,10 +1081,6 @@ aal_level_requested: 1, acr_values: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, piv_cac_requested: false, - phishing_resistant_requested: false, - ial: 1, - ial2: false, - ialmax: false, issuer: 'urn:gov:gsa:openidconnect:test', request_id: sp_request_id, request_url: request.original_url, diff --git a/spec/controllers/saml_idp_controller_spec.rb b/spec/controllers/saml_idp_controller_spec.rb index db83b45530d..7d37e6d9098 100644 --- a/spec/controllers/saml_idp_controller_spec.rb +++ b/spec/controllers/saml_idp_controller_spec.rb @@ -731,10 +731,9 @@ def name_id_version(format_urn) ) end let(:sign_in_flow) { :sign_in } - let(:skip_sign_in) { false } before do - stub_sign_in(user) unless skip_sign_in + stub_sign_in(user) session[:sign_in_flow] = sign_in_flow IdentityLinker.new(user, ServiceProvider.find_by(issuer: sp1_issuer)).link_identity(ial: 2) user.identities.last.update!( @@ -825,41 +824,6 @@ def name_id_version(format_urn) expect(response).to redirect_to capture_password_url end end - - context 'profile is not in the session and an incorrect ACR value was stored' do - let(:pii) { nil } - let(:skip_sign_in) { true } - - before do - IdentityLinker.new( - user, - ServiceProvider.find_by(issuer: sp1_issuer), - ).link_identity(ial: 2) - user.identities.last.update!( - verified_attributes: %w[email given_name family_name social_security_number address], - ) - allow(subject).to receive(:attribute_asserter) { asserter } - end - - it 'redirects the user to capture password' do - params = { - SAMLRequest: CGI.unescape(saml_request(ialmax_settings)), - path_year: SamlAuthHelper::PATH_YEAR, - } - - # Initial request to store the SP request - get :auth, params: params - - # The old code would store the IAL1 authn context instead of the IALMAX context - # This commit duplicates that behavior by overriding the value in the session here - controller.session[:sp][:acr_values] = Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF - stub_sign_in(user) - - get :auth, params: params - - expect(response).to redirect_to capture_password_url - end - end end context 'authn_context is invalid' do @@ -1233,10 +1197,6 @@ def name_id_version(format_urn) aal_level_requested: aal_level, piv_cac_requested: false, acr_values: acr_values, - phishing_resistant_requested: false, - ial: 1, - ial2: false, - ialmax: false, request_url: @stored_request_url.gsub('authpost', 'auth'), request_id: sp_request_id, requested_attributes: ['email'], @@ -1273,10 +1233,6 @@ def name_id_version(format_urn) aal_level_requested: aal_level, acr_values: acr_values, piv_cac_requested: false, - phishing_resistant_requested: false, - ial: 1, - ial2: false, - ialmax: false, request_url: @saml_request.request.original_url.gsub('authpost', 'auth'), request_id: sp_request_id, requested_attributes: ['email'], diff --git a/spec/controllers/users/passwords_controller_spec.rb b/spec/controllers/users/passwords_controller_spec.rb index 32832b706aa..5a6047469cc 100644 --- a/spec/controllers/users/passwords_controller_spec.rb +++ b/spec/controllers/users/passwords_controller_spec.rb @@ -40,7 +40,7 @@ ) expect(response).to redirect_to account_url expect(flash[:info]).to eq t('notices.password_changed') - expect(flash[:personal_key]).to be_nil + expect(controller.user_session[:personal_key]).to be_nil end it 'updates the user password and regenerates personal key' do @@ -64,8 +64,10 @@ ), ) - expect(flash[:personal_key]).to eq(assigns(:update_user_password_form).personal_key) - expect(flash[:personal_key]).to be_present + expect(controller.user_session[:personal_key]).to eq( + assigns(:update_user_password_form).personal_key, + ) + expect(response).to redirect_to manage_personal_key_url end it 'creates a user Event for the password change' do diff --git a/spec/controllers/users/two_factor_authentication_controller_spec.rb b/spec/controllers/users/two_factor_authentication_controller_spec.rb index 20129412a2f..4468862f24a 100644 --- a/spec/controllers/users/two_factor_authentication_controller_spec.rb +++ b/spec/controllers/users/two_factor_authentication_controller_spec.rb @@ -259,9 +259,14 @@ def index end context 'when SP requires PIV/CAC' do + let(:service_provider) { create(:service_provider) } + before do stub_sign_in(user) - controller.session[:sp] = { phishing_resistant_requeste: true, piv_cac_requested: true } + controller.session[:sp] = { + issuer: service_provider.issuer, + acr_values: Saml::Idp::Constants::AAL2_HSPD12_AUTHN_CONTEXT_CLASSREF, + } end it 'redirects to MFA setup if no PIV/CAC is enabled' do diff --git a/spec/controllers/users/verify_password_controller_spec.rb b/spec/controllers/users/verify_password_controller_spec.rb index b1af63dee0e..56c165e66a4 100644 --- a/spec/controllers/users/verify_password_controller_spec.rb +++ b/spec/controllers/users/verify_password_controller_spec.rb @@ -94,12 +94,12 @@ expect(@irs_attempts_api_tracker).to have_received(:idv_personal_key_generated) end - it 'redirects to the account page' do - expect(response).to redirect_to(account_url) + it 'redirects to the manage personal key page' do + expect(response).to redirect_to(manage_personal_key_url) end it 'sets a new personal key as a flash message' do - expect(flash[:personal_key]).to eq(key) + expect(controller.user_session[:personal_key]).to eq(key) end end diff --git a/spec/features/idv/doc_auth/document_capture_spec.rb b/spec/features/idv/doc_auth/document_capture_spec.rb index fcfc5da4c30..db820bf5297 100644 --- a/spec/features/idv/doc_auth/document_capture_spec.rb +++ b/spec/features/idv/doc_auth/document_capture_spec.rb @@ -383,6 +383,47 @@ inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) expect(page).to have_content(inline_error) end + + context 'with Attention with Barcode' do + it 'try again and page show selfie fail inline error message' do + visit_idp_from_oidc_sp_with_ial2 + sign_in_and_2fa_user(user) + complete_doc_auth_steps_before_document_capture_step + attach_images( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_barcode_attention_liveness_fail.yml' + ), + ) + attach_selfie( + Rails.root.join( + 'spec', 'fixtures', + 'ial2_test_credential_barcode_attention_liveness_fail.yml' + ), + ) + submit_images + message = strip_tags(t('errors.doc_auth.selfie_not_live_or_poor_quality_heading')) + expect(page).to have_content(message) + detail_message = strip_tags(t('doc_auth.errors.alerts.selfie_not_live')) + security_message = strip_tags( + t( + 'idv.warning.attempts_html', + count: IdentityConfig.store.doc_auth_max_attempts - 1, + ), + ) + + expect(page).to have_content(detail_message << "\n" << security_message) + review_issues_header = strip_tags( + t('errors.doc_auth.selfie_not_live_or_poor_quality_heading'), + ) + expect(page).to have_content(review_issues_header) + expect(page).to have_current_path(idv_document_capture_path) + click_try_again + expect(page).to have_current_path(idv_document_capture_path) + inline_error = strip_tags(t('doc_auth.errors.general.selfie_failure')) + expect(page).to have_content(inline_error) + end + end end context 'when selfie check is not enabled (flag off, and/or in production)' do diff --git a/spec/features/remember_device/cookie_expiration_spec.rb b/spec/features/remember_device/cookie_expiration_spec.rb index ea1b30e6bed..4aa8f4a6af4 100644 --- a/spec/features/remember_device/cookie_expiration_spec.rb +++ b/spec/features/remember_device/cookie_expiration_spec.rb @@ -6,34 +6,12 @@ let(:user) { user_with_2fa } - context 'with feature flag set' do - before do - allow(IdentityConfig.store).to receive(:set_remember_device_session_expiration). - and_return(true) - end - - it 'expires the remember device cookie' do - sign_in_user_with_remember_device - expire_cookies - sign_in_user(user) - - expect(current_url).to match(%r{/login/two_factor/}) - end - end - - context 'with feature flag unset' do - before do - allow(IdentityConfig.store).to receive(:set_remember_device_session_expiration). - and_return(false) - end - - it 'does not expire the remember device cookie' do - sign_in_user_with_remember_device - expire_cookies - sign_in_user(user) + it 'does not expire the remember device cookie' do + sign_in_user_with_remember_device + expire_cookies + sign_in_user(user) - expect(current_url).to match(%r{/account}) - end + expect(current_url).to match(%r{/account}) end def sign_in_user_with_remember_device diff --git a/spec/features/users/password_recovery_via_recovery_code_spec.rb b/spec/features/users/password_recovery_via_recovery_code_spec.rb index ec68610d145..ef92a6cab7f 100644 --- a/spec/features/users/password_recovery_via_recovery_code_spec.rb +++ b/spec/features/users/password_recovery_via_recovery_code_spec.rb @@ -24,8 +24,12 @@ reactivate_profile(new_password, personal_key) - expect(page).to have_content t('idv.messages.personal_key') - expect(page).to have_content t('headings.account.verified_account') + expect(page).to have_content(t('forms.personal_key_partial.header')) + expect(page).to have_current_path(manage_personal_key_path) + + personal_key = PersonalKeyGenerator.new(user).normalize(scrape_personal_key) + + expect(user.reload.valid_personal_key?(personal_key)).to eq(true) end scenario 'resets password and reactivates profile with no personal key', email: true, js: true do diff --git a/spec/features/users/profile_recovery_for_gpo_verified_spec.rb b/spec/features/users/profile_recovery_for_gpo_verified_spec.rb index 81d664d1f96..a228c3fc303 100644 --- a/spec/features/users/profile_recovery_for_gpo_verified_spec.rb +++ b/spec/features/users/profile_recovery_for_gpo_verified_spec.rb @@ -51,7 +51,11 @@ fill_in 'Password', with: new_password click_continue - expect(page).to have_content t('idv.messages.personal_key') - expect(page).to have_content t('headings.account.verified_account') + expect(page).to have_content(t('forms.personal_key_partial.header')) + expect(page).to have_current_path(manage_personal_key_path) + + personal_key = PersonalKeyGenerator.new(user).normalize(scrape_personal_key) + + expect(user.reload.valid_personal_key?(personal_key)).to eq(true) end end diff --git a/spec/features/users/user_profile_spec.rb b/spec/features/users/user_profile_spec.rb index 5f92e87a97d..0c90faf9bab 100644 --- a/spec/features/users/user_profile_spec.rb +++ b/spec/features/users/user_profile_spec.rb @@ -146,8 +146,16 @@ with: 'this is a great sentence' click_button 'Update' - expect(current_path).to eq account_path - expect(page).to have_content(t('idv.messages.personal_key')) + expect(page).to have_content(t('forms.personal_key_partial.header')) + expect(page).to have_current_path(manage_personal_key_path) + + personal_key = PersonalKeyGenerator.new(profile.user).normalize(scrape_personal_key) + + expect(profile.user.reload.valid_personal_key?(personal_key)).to eq(true) + + click_continue + + expect(current_path).to eq(account_path) end it 'allows the user reactivate their profile by reverifying', js: true do diff --git a/spec/fixtures/ial2_test_credential_barcode_attention_liveness_fail.yml b/spec/fixtures/ial2_test_credential_barcode_attention_liveness_fail.yml new file mode 100644 index 00000000000..132502d887e --- /dev/null +++ b/spec/fixtures/ial2_test_credential_barcode_attention_liveness_fail.yml @@ -0,0 +1,24 @@ +portrait_match_results: + # returns the portrait match result + FaceMatchResult: Fail + # returns the liveness result + FaceErrorMessage: 'Liveness: NotLive' +doc_auth_result: Attention +document: + first_name: Jane + last_name: Doe + middle_name: Q + address1: 1800 F Street + address2: Apt 3 + city: Bayside + state: NY + zipcode: '11364' + dob: 10/06/1938 + phone: +1 314-555-1212 + state_id_jurisdiction: 'ND' + state_id_number: 'S59397998' + state_id_type: drivers_license +failed_alerts: + - name: 2D Barcode Read + result: Attention + diff --git a/spec/presenters/account_show_presenter_spec.rb b/spec/presenters/account_show_presenter_spec.rb index bd083feccbc..270e4b99e17 100644 --- a/spec/presenters/account_show_presenter_spec.rb +++ b/spec/presenters/account_show_presenter_spec.rb @@ -13,9 +13,11 @@ dob: birthday ) profile_index = AccountShowPresenter.new( - decrypted_pii: decrypted_pii, personal_key: '', user: user, - sp_session_request_url: nil, sp_name: nil, - locked_for_session: false + decrypted_pii: decrypted_pii, + user: user, + sp_session_request_url: nil, + sp_name: nil, + locked_for_session: false, ) expect(profile_index.header_personalization).to eq first_name @@ -28,9 +30,11 @@ email_address = user.reload.email_addresses.last email_address.update!(last_sign_in_at: 1.minute.from_now) profile_index = AccountShowPresenter.new( - decrypted_pii: {}, personal_key: '', user: user, - sp_session_request_url: nil, sp_name: nil, - locked_for_session: false + decrypted_pii: {}, + user: user, + sp_session_request_url: nil, + sp_name: nil, + locked_for_session: false, ) expect(profile_index.header_personalization).to eq email_address.email @@ -47,9 +51,11 @@ ).to receive(:enabled?).and_return(true) profile_index = AccountShowPresenter.new( - decrypted_pii: {}, personal_key: '', user: user, - sp_session_request_url: nil, sp_name: nil, - locked_for_session: false + decrypted_pii: {}, + user: user, + sp_session_request_url: nil, + sp_name: nil, + locked_for_session: false, ) expect(profile_index.totp_content).to eq t('account.index.auth_app_enabled') @@ -63,9 +69,11 @@ TwoFactorAuthentication::AuthAppPolicy, ).to receive(:enabled?).and_return(false) profile_index = AccountShowPresenter.new( - decrypted_pii: {}, personal_key: '', user: user, - sp_session_request_url: nil, sp_name: nil, - locked_for_session: false + decrypted_pii: {}, + user: user, + sp_session_request_url: nil, + sp_name: nil, + locked_for_session: false, ) expect(profile_index.totp_content).to eq t('account.index.auth_app_disabled') @@ -81,7 +89,6 @@ account_show = AccountShowPresenter.new( decrypted_pii: {}, - personal_key: '', sp_session_request_url: nil, sp_name: nil, user: user.reload, @@ -100,7 +107,6 @@ account_show = AccountShowPresenter.new( decrypted_pii: {}, - personal_key: '', sp_session_request_url: nil, sp_name: nil, user: user.reload, @@ -120,7 +126,6 @@ subject(:account_show) do AccountShowPresenter.new( decrypted_pii: decrypted_pii, - personal_key: '', sp_session_request_url: nil, sp_name: nil, user: user, @@ -158,7 +163,6 @@ user = profile.user profile_index = AccountShowPresenter.new( decrypted_pii: {}, - personal_key: '', user: user, sp_session_request_url: nil, sp_name: nil, @@ -182,7 +186,6 @@ user = profile.user profile_index = AccountShowPresenter.new( decrypted_pii: {}, - personal_key: '', user: user, sp_session_request_url: nil, sp_name: nil, @@ -201,7 +204,6 @@ profile_index = AccountShowPresenter.new( decrypted_pii: {}, - personal_key: '', user: user, sp_session_request_url: nil, sp_name: nil, diff --git a/spec/services/id_token_builder_spec.rb b/spec/services/id_token_builder_spec.rb index 22adaab045c..e575d70602e 100644 --- a/spec/services/id_token_builder_spec.rb +++ b/spec/services/id_token_builder_spec.rb @@ -21,6 +21,7 @@ let(:now) { Time.zone.now } let(:custom_expiration) { (now + 5.minutes).to_i } + let(:vtm_url) { 'https://example.com/vot-trust-framework' } subject(:builder) do IdTokenBuilder.new( identity: identity, @@ -61,22 +62,89 @@ expect(decoded_payload[:nonce]).to eq(identity.nonce) end + context 'it sets the vot' do + context 'sp requests vot' do + before do + allow(IdentityConfig.store).to receive(:use_vot_in_sp_requests). + and_return(true) + allow(IdentityConfig.store).to receive(:vtm_url). + and_return(vtm_url) + end + + it 'sets the vot if the sp requests it' do + identity.vtr = 'Pb' + expect(decoded_payload[:vot]).to eq('C1.C2.P1.Pb') + end + + it 'sets the vtm' do + identity.vtr = 'Pb' + expect(decoded_payload[:vtm]).to eq(vtm_url) + end + end + + context 'sp does not request vot' do + before do + allow(IdentityConfig.store).to receive(:use_vot_in_sp_requests). + and_return(false) + allow(IdentityConfig.store).to receive(:vtm_url). + and_return(vtm_url) + end + + it 'does not set the vot if the sp does not request it' do + identity.vtr = 'Pb' + expect(decoded_payload[:vot]).to eq nil + end + + it 'does not set the vtm' do + identity.vtr = nil + expect(decoded_payload[:vtm]).to eq nil + end + end + end + context 'it sets the acr' do + context 'aal and ial request' do + before do + identity.aal = 2 + acr_values = [ + Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, + Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, + ].join(' ') + identity.acr_values = acr_values + end + + it 'ignores the aal value' do + expect(decoded_payload[:acr]).to eq(Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF) + end + end + context 'ial2 request' do + before do + identity.ial = 2 + identity.acr_values = Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF + end + it 'sets the acr to the ial2 constant' do expect(decoded_payload[:acr]).to eq(Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF) end end context 'ial1 request' do - before { identity.ial = 1 } + before do + identity.ial = 1 + identity.acr_values = Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF + end + it 'sets the acr to the ial1 constant' do expect(decoded_payload[:acr]).to eq(Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF) end end context 'ialmax request' do - before { identity.ial = 0 } + before do + identity.ial = 0 + identity.acr_values = Saml::Idp::Constants::IALMAX_AUTHN_CONTEXT_CLASSREF + end context 'non-verified user' do it 'sets the acr to the ial1 constant' do diff --git a/spec/services/store_sp_metadata_in_session_spec.rb b/spec/services/store_sp_metadata_in_session_spec.rb index 4ecbf0e194f..057ada83968 100644 --- a/spec/services/store_sp_metadata_in_session_spec.rb +++ b/spec/services/store_sp_metadata_in_session_spec.rb @@ -48,10 +48,6 @@ aal_level_requested: 1, acr_values: request_acr, piv_cac_requested: false, - phishing_resistant_requested: false, - ial: 1, - ial2: false, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, @@ -75,10 +71,6 @@ aal_level_requested: 2, acr_values: request_acr, piv_cac_requested: false, - phishing_resistant_requested: true, - ial: 2, - ial2: true, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, @@ -101,10 +93,6 @@ aal_level_requested: 2, acr_values: request_acr, piv_cac_requested: false, - phishing_resistant_requested: true, - ial: 2, - ial2: true, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, @@ -129,10 +117,6 @@ aal_level_requested: 2, acr_values: request_acr, piv_cac_requested: false, - phishing_resistant_requested: true, - ial: 2, - ial2: true, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, @@ -153,10 +137,6 @@ aal_level_requested: 1, acr_values: request_acr, piv_cac_requested: false, - phishing_resistant_requested: false, - ial: 1, - ial2: false, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, @@ -178,10 +158,6 @@ aal_level_requested: 2, acr_values: request_acr, piv_cac_requested: false, - phishing_resistant_requested: false, - ial: 2, - ial2: true, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, @@ -203,10 +179,6 @@ aal_level_requested: 2, acr_values: request_acr, piv_cac_requested: false, - phishing_resistant_requested: false, - ial: 2, - ial2: true, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, @@ -227,10 +199,6 @@ aal_level_requested: 2, acr_values: request_acr, piv_cac_requested: false, - phishing_resistant_requested: false, - ial: 2, - ial2: true, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, @@ -251,10 +219,6 @@ aal_level_requested: 2, acr_values: request_acr, piv_cac_requested: false, - phishing_resistant_requested: true, - ial: 2, - ial2: true, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, @@ -275,10 +239,6 @@ acr_values: request_acr, aal_level_requested: 2, piv_cac_requested: false, - phishing_resistant_requested: true, - ial: 2, - ial2: true, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, @@ -299,10 +259,6 @@ aal_level_requested: 2, acr_values: request_acr, piv_cac_requested: true, - phishing_resistant_requested: false, - ial: 2, - ial2: true, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, @@ -323,10 +279,6 @@ aal_level_requested: 2, acr_values: request_acr, piv_cac_requested: true, - phishing_resistant_requested: false, - ial: 2, - ial2: true, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, @@ -347,10 +299,6 @@ aal_level_requested: 2, acr_values: request_acr, piv_cac_requested: false, - phishing_resistant_requested: false, - ial: 2, - ial2: true, - ialmax: false, request_url: request_url, request_id: request_id, requested_attributes: requested_attributes, diff --git a/spec/support/shared_examples/sign_in.rb b/spec/support/shared_examples/sign_in.rb index ed3d4761418..d19d0e5984e 100644 --- a/spec/support/shared_examples/sign_in.rb +++ b/spec/support/shared_examples/sign_in.rb @@ -163,13 +163,12 @@ reactivate_profile(new_password, user.personal_key) - expect(current_path).to eq account_path - expect(page).to have_content(t('idv.messages.personal_key')) + expect(page).to have_content(t('forms.personal_key_partial.header')) + expect(page).to have_current_path(manage_personal_key_path) - sp_friendly_name = ServiceProvider.find_by(issuer: service_provider_issuer(sp)).friendly_name - click_link t('account.index.continue_to_service_provider', service_provider: sp_friendly_name) + check t('forms.personal_key.required_checkbox') + click_continue - click_submit_default if current_path == complete_saml_path click_agree_and_continue expect(current_url).to eq complete_saml_url if sp == :saml diff --git a/spec/views/accounts/connected_accounts/show.html.erb_spec.rb b/spec/views/accounts/connected_accounts/show.html.erb_spec.rb index facf70fe2e6..5046e71bbdc 100644 --- a/spec/views/accounts/connected_accounts/show.html.erb_spec.rb +++ b/spec/views/accounts/connected_accounts/show.html.erb_spec.rb @@ -7,9 +7,11 @@ assign( :presenter, AccountShowPresenter.new( - decrypted_pii: nil, personal_key: nil, user: user, - sp_session_request_url: nil, sp_name: nil, - locked_for_session: false + decrypted_pii: nil, + user: user, + sp_session_request_url: nil, + sp_name: nil, + locked_for_session: false, ), ) end diff --git a/spec/views/accounts/history/show.html.erb_spec.rb b/spec/views/accounts/history/show.html.erb_spec.rb index 3bca386aff1..f6547cc3851 100644 --- a/spec/views/accounts/history/show.html.erb_spec.rb +++ b/spec/views/accounts/history/show.html.erb_spec.rb @@ -8,9 +8,11 @@ assign( :presenter, AccountShowPresenter.new( - decrypted_pii: nil, personal_key: nil, user: user, - sp_session_request_url: nil, sp_name: nil, - locked_for_session: false + decrypted_pii: nil, + user: user, + sp_session_request_url: nil, + sp_name: nil, + locked_for_session: false, ), ) end diff --git a/spec/views/accounts/show.html.erb_spec.rb b/spec/views/accounts/show.html.erb_spec.rb index 58ddbac7c7b..1923dfb8e7e 100644 --- a/spec/views/accounts/show.html.erb_spec.rb +++ b/spec/views/accounts/show.html.erb_spec.rb @@ -8,9 +8,11 @@ assign( :presenter, AccountShowPresenter.new( - decrypted_pii: nil, personal_key: nil, user: user, - sp_session_request_url: nil, sp_name: nil, - locked_for_session: false + decrypted_pii: nil, + user: user, + sp_session_request_url: nil, + sp_name: nil, + locked_for_session: false, ), ) end @@ -162,9 +164,11 @@ assign( :presenter, AccountShowPresenter.new( - decrypted_pii: nil, personal_key: 'abc123', user: user, - sp_session_request_url: sp.return_to_sp_url, sp_name: sp.friendly_name, - locked_for_session: false + decrypted_pii: nil, + user: user, + sp_session_request_url: sp.return_to_sp_url, + sp_name: sp.friendly_name, + locked_for_session: false, ), ) end diff --git a/spec/views/accounts/two_factor_authentication/show.html.erb_spec.rb b/spec/views/accounts/two_factor_authentication/show.html.erb_spec.rb index 66baa50c0e0..a8295d9e709 100644 --- a/spec/views/accounts/two_factor_authentication/show.html.erb_spec.rb +++ b/spec/views/accounts/two_factor_authentication/show.html.erb_spec.rb @@ -8,9 +8,11 @@ assign( :presenter, AccountShowPresenter.new( - decrypted_pii: nil, personal_key: nil, user: user, - sp_session_request_url: nil, sp_name: nil, - locked_for_session: false + decrypted_pii: nil, + user: user, + sp_session_request_url: nil, + sp_name: nil, + locked_for_session: false, ), ) end @@ -31,9 +33,11 @@ assign( :presenter, AccountShowPresenter.new( - decrypted_pii: nil, personal_key: nil, user: user, - sp_session_request_url: nil, sp_name: nil, - locked_for_session: false + decrypted_pii: nil, + user: user, + sp_session_request_url: nil, + sp_name: nil, + locked_for_session: false, ), ) end diff --git a/spec/views/idv/not_verified/show.html.erb_spec.rb b/spec/views/idv/not_verified/show.html.erb_spec.rb new file mode 100644 index 00000000000..abb99c3018e --- /dev/null +++ b/spec/views/idv/not_verified/show.html.erb_spec.rb @@ -0,0 +1,52 @@ +require 'rails_helper' + +RSpec.describe 'idv/not_verified/show.html.erb' do + let(:sp_name) { nil } + + before do + allow(view).to receive(:decorated_sp_session).and_return( + instance_double(ServiceProviderSession, sp_name: sp_name), + ) + + render + end + + context 'without an sp' do + it 'renders the fail link text with application name' do + expect(rendered).to have_text( + strip_tags( + t( + 'idv.failure.verify.fail_link_html', + sp_name: APP_NAME, + ), + ), + ) + end + end + + context 'with an sp' do + let(:sp_name) { 'Department of Departments' } + it 'renders the fail link text with the SP name' do + expect(rendered).to have_text( + strip_tags( + t('idv.failure.verify.fail_link_html', sp_name: sp_name), + ), + ) + end + end + + describe('exit button') do + it 'is rendered' do + expect(rendered).to have_selector( + 'a', + text: t('idv.failure.verify.exit', app_name: APP_NAME), + ) + end + it 'links to the right place' do + expect(rendered).to have_link( + t('idv.failure.verify.exit', app_name: APP_NAME), + href: return_to_sp_failure_to_proof_path, + ) + end + end +end diff --git a/yarn.lock b/yarn.lock index 6313b6499fe..87d56d37124 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4639,10 +4639,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libphonenumber-js@^1.10.56: - version "1.10.56" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.56.tgz#96d9d64c31888ec363ed99fbf77cf4ad12117aa4" - integrity sha512-d0GdKshNnyfl5gM7kZ9rXjGiAbxT/zCXp0k+EAzh8H4zrb2R7GXtMCrULrX7UQxtfx6CLy/vz/lomvW79FAFdA== +libphonenumber-js@^1.10.57: + version "1.10.57" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.57.tgz#4a8174014c758b8ffd865e06a43c3885706f7a9c" + integrity sha512-OjsEd9y4LgcX+Ig09SbxWqcGESxliDDFNVepFhB9KEsQZTrnk3UdEU+cO0sW1APvLprHstQpS23OQpZ3bwxy6Q== lightningcss-darwin-arm64@1.23.0: version "1.23.0"