libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 3dbdedda04dc3014c5bfe18b5689c42e2e04efb4
parent d3ef68d4dd63c8b9e3dd59d40c70ff88d547b086
Author: Antoine A <>
Date:   Wed,  8 Oct 2025 15:22:46 +0200

bank: combine redundant mfa challenges

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/auth/mfa.kt | 10++++++----
Mbank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt | 8+++++++-
Mbank/src/test/kotlin/CoreBankApiTest.kt | 16++++++----------
3 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/auth/mfa.kt b/bank/src/main/kotlin/tech/libeufin/bank/auth/mfa.kt @@ -56,10 +56,15 @@ private suspend fun ApplicationCall.respondChallenges( tanChannel = channel, tanInfo = info ) + val privateInfo = if (op == Operation.create_token) { + "REDACTED" + } else { + info + } challenges.add(Challenge( challenge_id = uuid.toString(), tan_channel = channel, - tan_info = info + tan_info = privateInfo )) } return challenges @@ -78,9 +83,6 @@ suspend fun ApplicationCall.respondMfa( ) { val info = this.bankInfo(db) var challenges = respondChallenges(db, op, info.mfa) - if (op == Operation.create_token) { - challenges = challenges.map { it.copy(tan_info="REDACTED") } - } respond( status = HttpStatusCode.Accepted, message = ChallengeResponse( diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt @@ -311,7 +311,13 @@ class AccountDAO(private val db: Database) { if (!isAdmin && !is2fa) { // Check if mfa is required if (curr.channels.isNotEmpty()) { - return@serializableTransaction AccountPatchResult.Challenges(emptyList()) + val tans = curr.mfa; + if (tans.size == 1) { + // Perform mfa and validation at the same time + return@serializableTransaction AccountPatchResult.Challenges(validations + tans) + } else { + return@serializableTransaction AccountPatchResult.Challenges(emptyList()) + } } // Check if validation is required diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt @@ -2106,29 +2106,26 @@ class CoreBankTanApiTest { json { // Info change "contact_data" to obj { "phone" to "+98" } } - }.expectMfa(TanChannel.sms to "+99") - .expectValidation(TanChannel.sms to "+98") + }.expectValidation(TanChannel.sms to "+99", TanChannel.sms to "+98") .assertNoContent() client.patchA("/accounts/merchant") { json { // Channel change "tan_channel" to "email" } - }.expectMfa(TanChannel.sms to "+98") - .expectValidation(TanChannel.email to "email@example.com") + }.expectValidation(TanChannel.sms to "+98", TanChannel.email to "email@example.com") .assertNoContent() client.patchA("/accounts/merchant") { json { // Both change "contact_data" to obj { "phone" to "+97" } "tan_channel" to "sms" } - }.expectMfa(TanChannel.email to "email@example.com") - .expectValidation(TanChannel.sms to "+97") + }.expectValidation(TanChannel.email to "email@example.com", TanChannel.sms to "+97") .assertNoContent() // Disable 2fa client.patchA("/accounts/merchant") { json { "tan_channel" to null as String? } - }.expectMfa(TanChannel.sms to "+97") + }.expectValidation(TanChannel.sms to "+97") .assertNoContent() // Update mfa settings - first mfa challenge then new tan channel check @@ -2206,8 +2203,7 @@ class CoreBankTanApiTest { json { "tan_channel" to "email" } - }.expectMfa(TanChannel.sms to "+88") - .expectValidation(TanChannel.email to "email2@example.com") + }.expectValidation(TanChannel.sms to "+88", TanChannel.email to "email2@example.com") .assertNoContent() // Check invalidated @@ -2217,7 +2213,7 @@ class CoreBankTanApiTest { client.patchA("/accounts/merchant") { headers[TALER_CHALLENGE_IDS] = "${challenge.challenge_id}" json { "is_public" to false } - }.expectMfa(TanChannel.email to "email2@example.com") + }.expectValidation(TanChannel.email to "email2@example.com") .assertNoContent() }