Require card verification for cost-restricted dial codes
What does this MR do and why?
Require card verification for cost-restricted dial codes
Phone verification is billed per SMS and termination cost varies sharply by destination. High-cost prefixes are what SMS pumping targets, and every attempt draws down the global daily transaction budget that gates phone verification for all users.
Add an admin-configurable list of dial codes that must complete credit card verification before phone verification. The check runs before the per-user rate limit, so a redirected user does not lose one of their daily send attempts, and before the Telesign call, so no transaction is spent on a number we are not going to text.
This reuses assume_high_risk!, which already produces the required email -> credit card -> phone ordering. A user already flagged for another reason keeps that reason rather than having it overwritten.
The list is empty by default, so behaviour is unchanged until an administrator populates it.
Related to https://gitlab.com/gitlab-org/gitlab/-/work_items/609536
Changelog: added EE: true
References
Screenshots or screen recordings
How to set up and validate locally
To set it up make sure the right flags and settings are enabled (see below). Register a user and before confirming email update the users arkose risk score to medium. then continue confirming email, when you use the country code you've added to the settings and submit it should refresh the page and force credit card verification first.
How to set up
GDK must run with SaaS simulation, since identity verification is gated on
Gitlab::Saas.feature_available?(:identity_verification):
# gdk.yml
env:
GITLAB_SIMULATE_SAAS: 1Then gdk restart rails. Use gdk rails console rather than bin/rails console,
otherwise gdk.env is not loaded and the instance will not be in SaaS mode.
Configure the setting (92 is used as the example restricted dial code):
ApplicationSetting.current.update!(
sms_cost_restricted_dial_codes: [92],
phone_verification_enabled: true,
credit_card_verification_enabled: true
)
Gitlab::CurrentSettings.expire_current_application_settingsVerify the gate
The gate returns before query_intelligence_api and before the Telesign send,
so this needs no Telesign or Arkose credentials.
user = create(:user) # or any user without a credit card
result = PhoneVerification::Users::SendVerificationCodeService.new(
user,
ip_address: '127.0.0.1',
country: 'PK',
international_dial_code: 92,
phone_number: '3001234567'
).execute
result.error? # => true
result.reason # => :card_verification_required
result.message # => "Verify a payment method to continue."Re-fetch the user (the risk profile is memoized on the instance):
user = User.find(user.id)
user.assumed_high_risk? # => true
user.custom_attributes.find_by(key: 'assumed_high_risk_reason').value
# => "Cost-restricted dial code"
user.required_identity_verification_methods
# => ["email", "credit_card", "phone"] <- credit card now precedes phoneConfirm nothing was spent:
user.phone_number_validation&.sms_send_count # => nil or unchanged, no SMS sent
Gitlab::ApplicationRateLimiter.peek(:phone_verification_send_code, scope: user)
# => false, the daily send allowance was not consumedVerify each guard lets the user through
Each of these should return a non-card_verification_required result. With no
Telesign credentials locally they will fail later in the chain, which is the
point: the gate is no longer what stops them.
- Dial code not on the list — resend with
international_dial_code: 1. - User already has a credit card —
create(:credit_card_validation, user: user), then resend with 92. - Credit card verification disabled instance-wide —
ApplicationSetting.current.update!(credit_card_verification_enabled: false)then resend with 92. This is the lockout case: the user must not be blocked, because there would be no credit card step for them to complete. - User already assumed high risk —
user.assume_high_risk!(reason: 'Duplicate phone number'), then resend with 92. The original reason must be preserved and the user must not be blocked a second time.
Verify in the UI
With a user partway through identity verification, enter a phone number on a restricted dial code and submit. Expected: the request returns 400, the page reloads, and the wizard re-renders with the credit card step ahead of phone.
What should not happen
- No SMS is sent and no Telesign transaction is spent
- The user's daily phone verification send allowance is not decremented
- With
credit_card_verification_enabledfalse, the user is neither flagged nor blocked
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Related to #609536