From 6e0879dda56bf74e3e015bc79ee2ba10e2b33f81 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 24 Feb 2026 14:31:18 +0100 Subject: [PATCH 1/3] test/added debug error message --- obp-api/src/main/scala/code/api/util/ConsentUtil.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala index 45358d0289..64da2b9e44 100644 --- a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala +++ b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala @@ -255,8 +255,12 @@ object Consent extends MdcLoggable { if(requestConsumerId == "NONE" || consumerValidationMethodForConsent.isEmpty) { logger.warn(s"consumer_validation_method_for_consent is empty while request consumer_id=NONE - consent_id=${consent.jti}, aud=${consent.aud}") } + // Get consumer keys for debugging + val consentConsumerKey = Consumers.consumers.vend.getConsumerByConsumerId(consentConsumerId).map(_.key.get).getOrElse("Unknown") + val requestConsumerKey = callContext.consumer.map(_.key.get).getOrElse("None") + val detailedErrorMsg = s"${ErrorMessages.ConsentNotFound} Consumer mismatch: consent has consumer_id='$consentConsumerId' (consumer_key='$consentConsumerKey'), but current request has consumer_id='$requestConsumerId' (consumer_key='$requestConsumerKey')" logger.debug(s"ConsentNotFound: TPP/Consumer mismatch. Consent holder consumer_id=$consentConsumerId, Request consumer_id=$requestConsumerId, consent_id=${consent.jti}") - ErrorUtil.apiFailureToBox(ErrorMessages.ConsentNotFound, 401)(Some(callContext)) + ErrorUtil.apiFailureToBox(detailedErrorMsg, 401)(Some(callContext)) } else if (!verifyHmacSignedJwt(consentIdAsJwt, c)) { // verify signature Failure(ErrorMessages.ConsentVerificationIssue) } else { From 6b8c0b235d485b381cdd903febf0f6151b7e24cf Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 24 Feb 2026 14:59:35 +0100 Subject: [PATCH 2/3] bugfix/enhancement: Fix Berlin Group Consent-ID consumer validation and improve error messages 1. Fix Consumer-Key support for Berlin Group Consent-ID requests - APIUtil.scala: Use consumerByConsumerKey when consumer_validation_method_for_consent=CONSUMER_KEY_VALUE - Previously only used consumerByCertificate, ignoring Consumer-Key header - Now correctly selects consumer based on validation method configuration 2. Add early consent validation with detailed error messages - AccountInformationServiceAISApi.scala: Add early validation in getBalances endpoint - Show both consumer_id and consumer_key in error messages for debugging - Error format: "Consumer mismatch: consent has consumer_id='X' (consumer_key='Y'), but current request has consumer_id='Z' (consumer_key='W')" Impact: - Enables Consumer-Key based consent validation for Berlin Group endpoints - Provides clear debugging information when consumer mismatch occurs - Helps identify whether certificate or consumer key should be used Related Issue: TTK production Consent-ID validation failure --- obp-api/src/main/scala/code/api/util/APIUtil.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/obp-api/src/main/scala/code/api/util/APIUtil.scala b/obp-api/src/main/scala/code/api/util/APIUtil.scala index 925fcfdc14..7e6bbc17ff 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -3148,7 +3148,13 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ val message = ErrorMessages.InvalidConsentIdUsage Future { (fullBoxOrException(Empty ~> APIFailureNewStyle(message, 400, Some(cc.toLight))), Some(cc)) } } else if (APIUtil.`hasConsent-ID`(reqHeaders)) { // Berlin Group's Consent - Consent.applyBerlinGroupRules(APIUtil.`getConsent-ID`(reqHeaders), cc.copy(consumer = consumerByCertificate)) + // Choose consumer based on validation method configuration + val consumerForConsent = if (method == "CONSUMER_KEY_VALUE" && consumerByConsumerKey.isDefined) { + consumerByConsumerKey + } else { + consumerByCertificate + } + Consent.applyBerlinGroupRules(APIUtil.`getConsent-ID`(reqHeaders), cc.copy(consumer = consumerForConsent)) } else if (APIUtil.hasConsentJWT(reqHeaders)) { // Open Bank Project's Consent val consentValue = APIUtil.getConsentJWT(reqHeaders) Consent.getConsentJwtValueByConsentId(consentValue.getOrElse("")) match { From 7f46603b8ef0a70fe7bfa9d997f9ff6956bbaeb0 Mon Sep 17 00:00:00 2001 From: hongwei Date: Tue, 24 Feb 2026 16:31:49 +0100 Subject: [PATCH 3/3] test/hide details in error message --- obp-api/src/main/scala/code/api/util/ConsentUtil.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala index 64da2b9e44..40ab6630ce 100644 --- a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala +++ b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala @@ -260,7 +260,8 @@ object Consent extends MdcLoggable { val requestConsumerKey = callContext.consumer.map(_.key.get).getOrElse("None") val detailedErrorMsg = s"${ErrorMessages.ConsentNotFound} Consumer mismatch: consent has consumer_id='$consentConsumerId' (consumer_key='$consentConsumerKey'), but current request has consumer_id='$requestConsumerId' (consumer_key='$requestConsumerKey')" logger.debug(s"ConsentNotFound: TPP/Consumer mismatch. Consent holder consumer_id=$consentConsumerId, Request consumer_id=$requestConsumerId, consent_id=${consent.jti}") - ErrorUtil.apiFailureToBox(detailedErrorMsg, 401)(Some(callContext)) + logger.debug(s"ConsentNotFound: $detailedErrorMsg") + ErrorUtil.apiFailureToBox(ErrorMessages.ConsentNotFound, 401)(Some(callContext)) } else if (!verifyHmacSignedJwt(consentIdAsJwt, c)) { // verify signature Failure(ErrorMessages.ConsentVerificationIssue) } else {