Skip to content
14 changes: 13 additions & 1 deletion app/controllers/openid_connect/authorization_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class AuthorizationController < ApplicationController
def index
if @authorize_form.ial2_or_greater?
return redirect_to reactivate_account_url if user_needs_to_reactivate_account?
return redirect_to url_for_pending_profile_reason if user_has_pending_profile?
return redirect_to url_for_pending_profile_reason if user_has_usable_pending_profile?
return redirect_to idv_url if identity_needs_verification?
return redirect_to idv_url if selfie_needed?
end
Expand All @@ -47,6 +47,18 @@ def index

private

def pending_profile_policy
@pending_profile_policy ||= PendingProfilePolicy.new(
user: current_user,
resolved_authn_context_result: resolved_authn_context_result,
biometric_comparison_requested: biometric_comparison_requested?,
)
end

def user_has_usable_pending_profile?
pending_profile_policy.user_has_usable_pending_profile?
end

def block_biometric_requests_in_production
if biometric_comparison_requested? &&
!FeatureManagement.idv_allow_selfie_check?
Expand Down
40 changes: 40 additions & 0 deletions app/policies/pending_profile_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class PendingProfilePolicy
def initialize(user:, resolved_authn_context_result:, biometric_comparison_requested:)
@user = user
@resolved_authn_context_result = resolved_authn_context_result
@biometric_comparison_requested = biometric_comparison_requested
end

def user_has_usable_pending_profile?
if biometric_comparison_requested?
pending_biometric_profile?
else
pending_legacy_profile? || fraud_review_pending?
end
end

private

attr_reader :user, :resolved_authn_context_result, :biometric_comparison_requested

def active_biometric_profile?
user.active_profile&.idv_level == 'unsupervised_with_selfie'
end

def pending_biometric_profile?
user.pending_profile&.idv_level == 'unsupervised_with_selfie'
end

def biometric_comparison_requested?
return false if !FeatureManagement.idv_allow_selfie_check?
resolved_authn_context_result.biometric_comparison? || biometric_comparison_requested
end

def pending_legacy_profile?
user.pending_profile.present? && user.pending_profile&.idv_level != 'unsupervised_with_selfie'
end

def fraud_review_pending?
user.fraud_review_pending?
end
end
78 changes: 78 additions & 0 deletions spec/controllers/openid_connect/authorization_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,84 @@
end
end

context 'verified non-biometric profile with pending biometric profile' do
before do
allow(IdentityConfig.store).to receive(:openid_connect_redirect).
and_return('server_side')
IdentityLinker.new(user, service_provider).link_identity(ial: 3)
user.identities.last.update!(
verified_attributes: %w[birthdate family_name given_name verified_at],
)
allow(controller).to receive(:pii_requested_but_locked?).and_return(false)
end

context 'sp does not request biometrics' do
let(:selfie_capture_enabled) { true }
let(:user) { create(:profile, :active, :verified).user }

before do
expect(FeatureManagement).to receive(:idv_allow_selfie_check?).at_least(:once).
and_return(selfie_capture_enabled)
end

it 'redirects to the redirect_uri immediately when pii is unlocked if client-side redirect is disabled' do
create(:profile, :verify_by_mail_pending, :with_pii, idv_level: :unsupervised_with_selfie, user: user)
user.active_profile.idv_level = :legacy_unsupervised

action

expect(response).to redirect_to(/^#{params[:redirect_uri]}/)
Comment thread
matthinz marked this conversation as resolved.
expect(user.identities.last.verified_attributes).to eq(%w[birthdate family_name given_name verified_at])
end

it 'redirects to please call page if user has a fraudualent profile' do
create(:profile, :fraud_review_pending, :with_pii, idv_level: :unsupervised_with_selfie, user: user)

action

expect(response).to redirect_to(idv_please_call_url)
end
end

context 'sp requests biometrics' do
let(:selfie_capture_enabled) { true }
let(:user) { create(:profile, :active, :verified).user }

before do
expect(FeatureManagement).to receive(:idv_allow_selfie_check?).at_least(:once).
and_return(selfie_capture_enabled)
end

context 'with biometric_comparison_required param' do
before do
params[:biometric_comparison_required] = 'true'
end

it 'redirects to gpo enter code page' do
create(:profile, :verify_by_mail_pending, idv_level: :unsupervised_with_selfie, user: user)

action

expect(controller).to redirect_to(idv_verify_by_mail_enter_code_url)
end
end

context 'with vectors of trust' do
before do
params[:vtr] = ['C1.C2.P1.Pb'].to_json
end

it 'redirects to gpo enter code page' do
create(:profile, :verify_by_mail_pending, idv_level: :unsupervised_with_selfie, user: user)

action

expect(controller).to redirect_to(idv_verify_by_mail_enter_code_url)
end
end
end
end

context 'account is not already verified' do
it 'redirects to have the user verify their account' do
action
Expand Down
80 changes: 80 additions & 0 deletions spec/policies/pending_profile_policy_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
require 'rails_helper'

RSpec.describe PendingProfilePolicy do
let(:user) { create(:user) }
let(:resolved_authn_context_result) do
AuthnContextResolver.new(
service_provider: nil,
vtr: vtr,
acr_values: acr_values,
).resolve
end
let(:biometric_comparison_requested) { nil }
let(:vtr) { nil }
let(:acr_values) { nil }

subject(:policy) do
described_class.new(
user: user,
resolved_authn_context_result: resolved_authn_context_result,
biometric_comparison_requested: biometric_comparison_requested,
)
end

describe '#user_has_usable_pending_profile?' do
context 'has an active non-biometric profile and biometric comparison is requested' do
let(:idv_level) { :unsupervised_with_selfie }
before do
create(:profile, :active, :verified, idv_level: :legacy_unsupervised, user: user)
create(:profile, :verify_by_mail_pending, idv_level: idv_level, user: user)
allow(FeatureManagement).to receive(:idv_allow_selfie_check?).and_return(true)
end

context 'with resolved authn context result' do
let(:vtr) { ['C2.Pb'] }

it 'has a usable pending profile' do
expect(policy.user_has_usable_pending_profile?).to eq(true)
end
end

context 'with biometric_comparison_requested param set to true' do
let(:biometric_comparison_requested) { true }
let(:acr_values) { Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF }

it 'has a usable pending profile' do
expect(policy.user_has_usable_pending_profile?).to eq(true)
end
end
end

context 'no biometric comparison is requested' do
let(:idv_level) { :legacy_unsupervised }
let(:vtr) { ['C2'] }
context 'user has pending profile' do
before do
create(:profile, :verify_by_mail_pending, idv_level: idv_level, user: user)
end

it { expect(policy.user_has_usable_pending_profile?).to eq(true) }
end

context 'user has an active profile' do
before do
create(:profile, :active, :verified, idv_level: idv_level, user: user)
end

it { expect(policy.user_has_usable_pending_profile?).to eq(false) }
end

context 'user has active legacy profile with a pending fraud biometric profile' do
before do
create(:profile, :active, :verified, idv_level: idv_level, user: user)
create(:profile, :fraud_review_pending, idv_level: :unsupervised_with_selfie, user: user)
end

it { expect(policy.user_has_usable_pending_profile?).to eq(true) }
end
end
end
end