diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9a8eac9a..ad36517e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -48,7 +48,6 @@ def authenticate_identity! # verification flows — controllers serving those flows use this as a # before_action so users land on their case instead of a legacy page def redirect_to_open_manual_case - return unless Flipper.enabled?(VerificationCase::FLIPPER_FLAG, current_identity) return unless current_identity.verification_cases.open_cases.exists? redirect_to manual_verification_path diff --git a/app/controllers/backend/verification_cases_controller.rb b/app/controllers/backend/verification_cases_controller.rb index 71b33020..f9673825 100644 --- a/app/controllers/backend/verification_cases_controller.rb +++ b/app/controllers/backend/verification_cases_controller.rb @@ -57,7 +57,8 @@ def show end # staff entry point: user emailed identity@, staff opens a case. - # enables the flipper flag + sends the single-use link. + # the open case itself grants the flipper flag (group gate), so all + # that's left is sending the single-use link. def create authorize VerificationCase @@ -73,7 +74,6 @@ def create opened_by: current_user, skip_persona: params[:skip_persona] == "1" ) - @case.enable_flag! deliver_link! @case.log_event!(:case_opened, actor: current_user, request: request, diff --git a/app/controllers/manual_verifications_controller.rb b/app/controllers/manual_verifications_controller.rb index c9ca4201..fcfe4709 100644 --- a/app/controllers/manual_verifications_controller.rb +++ b/app/controllers/manual_verifications_controller.rb @@ -1,5 +1,5 @@ # the user side of a manual verification call case. gated three ways: -# logged in (ApplicationController), flipper flag on the identity, and +# logged in (ApplicationController), an open case on the identity, and # a single-use emailed link consumed on first visit. class ManualVerificationsController < ApplicationController before_action :set_case @@ -175,11 +175,6 @@ def acknowledge_call_capture private def set_case - unless Flipper.enabled?(VerificationCase::FLIPPER_FLAG, current_identity) - render(:link_invalid, status: :forbidden) and return if params[:token].present? - redirect_to root_path and return - end - @case = current_identity.verification_cases.open_cases.order(created_at: :desc).first return if @case diff --git a/app/models/verification_case.rb b/app/models/verification_case.rb index 26cee00f..4902ac44 100644 --- a/app/models/verification_case.rb +++ b/app/models/verification_case.rb @@ -14,7 +14,6 @@ class VerificationCase < ApplicationRecord include PublicIdentifiable set_public_id_prefix "vcase" - FLIPPER_FLAG = :manual_verification_call_2026_07_03 ACCESS_TOKEN_TTL = 7.days belongs_to :identity @@ -127,20 +126,15 @@ class VerificationCase < ApplicationRecord event :approve do transitions from: :call_held, to: :approved - after { close_out! } end event :deny do transitions from: :call_held, to: :denied - after { close_out! } end event :withdraw do transitions from: [ :requested, :link_sent, :docs_submitted, :call_scheduled, :call_held ], to: :withdrawn - after do - close_out! - invalidate_access_token! - end + after { invalidate_access_token! } end end @@ -159,11 +153,6 @@ def destroy_fully! end def decided? = approved? || denied? - # -- feature flag ------------------------------------------------------ - - def enable_flag! = Flipper.enable(FLIPPER_FLAG, identity) - def revoke_flag! = Flipper.disable(FLIPPER_FLAG, identity) - # -- single-use access link (mirrors Identity::V2LoginCode) ------------- # once the link has been opened, the authenticated session is what gets @@ -305,10 +294,4 @@ def log_event!(key, actor: nil, data: {}, request: nil) def reset_capture! update!(persona_inquiry_id: nil, persona_session_token: nil) end - - private - - # decision time: drop the flag. documents are retained (break-glass - # gated) — the ManualVerificationCall itself is created by the decision flow. - def close_out! = revoke_flag! end diff --git a/config/flipper_features.yml b/config/flipper_features.yml index 1d434abe..f2a87f1d 100644 --- a/config/flipper_features.yml +++ b/config/flipper_features.yml @@ -15,5 +15,4 @@ shared: dev_allow_manual_flow_2026_05_27: enables the manual document verification flow for an actor disable_slack_invites_2025_12_08: suppresses Slack SCIM provisioning for an actor dev_force_manual_review_2026_05_27: forces verifications for an actor into manual review - dev_force_deny_verification_2026_05_27: auto-denies verifications for an actor - manual_verification_call_2026_07_03: enables the manual verification call flow for an actor with an open case + dev_force_deny_verification_2026_05_27: auto-denies verifications for an actor \ No newline at end of file diff --git a/spec/models/verification_case_spec.rb b/spec/models/verification_case_spec.rb index ddf0dcfb..bf675dca 100644 --- a/spec/models/verification_case_spec.rb +++ b/spec/models/verification_case_spec.rb @@ -52,23 +52,20 @@ expect { kase.deny! }.to raise_error(AASM::InvalidTransition) end - it "revokes the flag on decision and keeps documents" do + it "keeps documents on decision" do kase = create(:verification_case, :call_held) doc = create(:verification_case_document, verification_case: kase) - Flipper.enable(described_class::FLIPPER_FLAG, kase.identity) kase.approve! - expect(Flipper.enabled?(described_class::FLIPPER_FLAG, kase.identity)).to be(false) expect(doc.reload.file).to be_attached end end describe "#withdraw" do - it "closes from any open state, drops the flag, and kills the link" do + it "closes from any open state and kills the link" do %i[requested link_sent docs_submitted call_scheduled call_held].each do |state| kase = create(:verification_case, state == :requested ? nil : state, access_token: "tok", access_token_expires_at: 1.day.from_now) - Flipper.enable(described_class::FLIPPER_FLAG, kase.identity) kase.withdraw! @@ -78,7 +75,6 @@ expect(kase).not_to be_decided expect(kase.access_token).to be_nil expect(kase.consume_access_token!("tok")).to be(false) - expect(Flipper.enabled?(described_class::FLIPPER_FLAG, kase.identity)).to be(false) end end diff --git a/spec/requests/backend/verification_cases_spec.rb b/spec/requests/backend/verification_cases_spec.rb index e4763b81..4630f758 100644 --- a/spec/requests/backend/verification_cases_spec.rb +++ b/spec/requests/backend/verification_cases_spec.rb @@ -9,10 +9,8 @@ allow_any_instance_of(Backend::ApplicationController).to receive(:require_2fa!).and_return(true) end - after { Flipper.disable(VerificationCase::FLIPPER_FLAG) } - describe "POST /backend/verification_cases" do - it "opens a case, enables the flag, and emails the single-use link" do + it "opens a case and emails the single-use link" do target = create(:identity) expect { @@ -22,7 +20,6 @@ kase = target.verification_cases.sole expect(kase).to be_link_sent expect(kase.skip_persona).to be(false) - expect(Flipper.enabled?(VerificationCase::FLIPPER_FLAG, target)).to be(true) end it "opens a skip-persona case when asked" do @@ -315,10 +312,9 @@ def guardian_pings = enqueued_jobs.select { |j| j[:job] == Slack::NotifyGuardian end describe "POST /backend/verification_cases/:id/withdraw" do - it "closes the case, revokes the flag, invalidates the link, and logs the reason" do + it "closes the case, invalidates the link, and logs the reason" do kase = create(:verification_case, :call_scheduled) token = kase.access_token - Flipper.enable(VerificationCase::FLIPPER_FLAG, kase.identity) post withdraw_backend_verification_case_path(kase), params: { reason: "opened on the wrong account" } @@ -327,7 +323,6 @@ def guardian_pings = enqueued_jobs.select { |j| j[:job] == Slack::NotifyGuardian expect(kase.verification).to be_nil expect(kase.access_token).to be_nil expect(kase.consume_access_token!(token)).to be(false) - expect(Flipper.enabled?(VerificationCase::FLIPPER_FLAG, kase.identity)).to be(false) event = kase.events.find_by(key: "case_withdrawn") expect(event.actor).to eq(verifier) diff --git a/spec/requests/manual_verifications_spec.rb b/spec/requests/manual_verifications_spec.rb index 0530d96e..5bb19afa 100644 --- a/spec/requests/manual_verifications_spec.rb +++ b/spec/requests/manual_verifications_spec.rb @@ -15,16 +15,8 @@ allow_any_instance_of(ApplicationController).to receive(:identity_signed_in?).and_return(true) end - after { Flipper.disable(VerificationCase::FLIPPER_FLAG) } - describe "gating" do - it "bounces users without the flipper flag" do - get manual_verification_path - expect(response).to redirect_to(root_path) - end - - it "bounces flagged users with no open case" do - Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) + it "bounces users with no open case" do get manual_verification_path expect(response).to redirect_to(root_path) end @@ -32,7 +24,6 @@ it "requires the single-use token on first visit" do kase = create(:verification_case, identity: identity, status: :link_sent) kase.generate_access_token! - Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) get manual_verification_path expect(response).to have_http_status(:forbidden) @@ -41,7 +32,6 @@ it "consumes a valid token then allows session access" do kase = create(:verification_case, identity: identity, status: :link_sent) token = kase.generate_access_token! - Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) get manual_verification_path(token: token) expect(response).to redirect_to(manual_verification_path) @@ -56,7 +46,6 @@ it "shows the invalid-link page to a user following the old emailed link" do kase = create(:verification_case, identity: identity, status: :link_sent) token = kase.generate_access_token! - Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) kase.withdraw! @@ -78,7 +67,6 @@ it "puts the user back at document submission, needing the new link first" do kase = create(:verification_case, identity: identity, status: :docs_submitted, document_class: "government_id", access_token_used_at: 1.hour.ago) - Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) kase.request_redo! token = kase.rotate_access_link!(reopen_gate: true) @@ -101,10 +89,7 @@ access_token_used_at: Time.current) end - before do - kase - Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) - end + before { kase } it "shows document class selection first" do get manual_verification_path