The "tell, never ask" rule holds up even better than this argues, because of an asymmetry on the customer's side that is worth naming.
When an attacker runs the bank-impersonation play, the verification code the customer receives is genuine. The attacker is sitting on the bank's real login page and presses "send code" mid-call, so the message leaves the bank's real sender ID and lands in the real thread under all the customer's old bank messages. Nothing about it is forgeable, because nothing about it is forged.
That is why every piece of consumer advice shaped as "check the message looks legitimate" fails at exactly the moment it is needed. The message is legitimate. The only thing that still discriminates is direction: a code you go and fetch because you started something, versus a code that arrives while someone is talking to you. If it is the second, they started it.
Which is the same rule you have arrived at from the operator side, and it is why the absoluteness matters so much. A single edge case where the bank asks does not just teach one customer a bad habit - it destroys the only heuristic that survives contact with a real attacker, because that heuristic is binary by construction.
One extension worth considering, since you are already treating the escape hatch as the product: the same discipline belongs in the SMS template, not only the voice agent. A code that says "code to sign in on a new device" or "code to add a payee" carries information the attacker cannot influence, because they do not choose which action they triggered. Bare six digits carries none. The customer who gets an unexpected "code to add a payee" while a nice person explains there has been fraud on their account has been handed something no script can talk them out of.
The "tell, never ask" rule holds up even better than this argues, because of an asymmetry on the customer's side that is worth naming.
When an attacker runs the bank-impersonation play, the verification code the customer receives is genuine. The attacker is sitting on the bank's real login page and presses "send code" mid-call, so the message leaves the bank's real sender ID and lands in the real thread under all the customer's old bank messages. Nothing about it is forgeable, because nothing about it is forged.
That is why every piece of consumer advice shaped as "check the message looks legitimate" fails at exactly the moment it is needed. The message is legitimate. The only thing that still discriminates is direction: a code you go and fetch because you started something, versus a code that arrives while someone is talking to you. If it is the second, they started it.
Which is the same rule you have arrived at from the operator side, and it is why the absoluteness matters so much. A single edge case where the bank asks does not just teach one customer a bad habit - it destroys the only heuristic that survives contact with a real attacker, because that heuristic is binary by construction.
One extension worth considering, since you are already treating the escape hatch as the product: the same discipline belongs in the SMS template, not only the voice agent. A code that says "code to sign in on a new device" or "code to add a payee" carries information the attacker cannot influence, because they do not choose which action they triggered. Bare six digits carries none. The customer who gets an unexpected "code to add a payee" while a nice person explains there has been fraud on their account has been handed something no script can talk them out of.