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 d7cbacae0..6f983201b 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -2762,11 +2762,16 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ Consent.applyBerlinGroupRules(APIUtil.`getConsent-ID`(reqHeaders), cc) } else if (APIUtil.hasConsentJWT(reqHeaders)) { // Open Bank Project's Consent val consentValue = APIUtil.getConsentJWT(reqHeaders) - Consent.getConsentsJwtValueByConsentId(consentValue.getOrElse("")) match { + Consent.getConsentJwtValueByConsentId(consentValue.getOrElse("")) match { case Some(jwt) => // JWT value obtained via "Consent-Id" request header Consent.applyRules(Some(jwt), cc) - case _ => // Assume it's JWT obtained via "Consent-JWT" request header - Consent.applyRules(APIUtil.getConsentJWT(reqHeaders), cc) + case _ => + JwtUtil.checkIfStringIsJWTValue(consentValue.getOrElse("")).isDefined match { + case true => // It's JWT obtained via "Consent-JWT" request header + Consent.applyRules(APIUtil.getConsentJWT(reqHeaders), cc) + case false => // Unrecognised consent value + Future { (Failure(ErrorMessages.ConsentHeaderValueInvalid), None) } + } } } else if (hasAnOAuthHeader(cc.authReqHeaderField)) { // OAuth 1 getUserFromOAuthHeaderFuture(cc) @@ -3488,8 +3493,11 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ * This function validates UUID (Universally Unique Identifier) strings * @param value a string we're trying to validate * @return false in case the string doesn't represent a UUID, true in case the string represents a UUID + * + *A Version 1 UUID is a universally unique identifier that is generated using + * a timestamp and the MAC address of the computer on which it was generated. */ - def checkIfStringIsUUID(value: String): Boolean = { + def checkIfStringIsUUIDVersion1(value: String): Boolean = { Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") .matcher(value).matches() } 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 1523063ac..1f65f737a 100644 --- a/obp-api/src/main/scala/code/api/util/ConsentUtil.scala +++ b/obp-api/src/main/scala/code/api/util/ConsentUtil.scala @@ -380,8 +380,8 @@ object Consent { } } - def getConsentsJwtValueByConsentId(consentId: String): Option[String] = { - APIUtil.checkIfStringIsUUID(consentId) match { + def getConsentJwtValueByConsentId(consentId: String): Option[String] = { + APIUtil.checkIfStringIsUUIDVersion1(consentId) match { case true => // String is a UUID Consents.consentProvider.vend.getConsentByConsentId(consentId) match { case Full(consent) => Some(consent.jsonWebToken) diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index 3edd270eb..3adfbf940 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -502,6 +502,7 @@ object ErrorMessages { val ConsentRequestIsInvalid = "OBP-35029: The CONSENT_REQUEST_ID is invalid. " val ConsumerKeyIsInvalid = "OBP-35030: The Consumer Key must be alphanumeric. (A-Z, a-z, 0-9)" val ConsumerKeyIsToLong = "OBP-35031: The Consumer Key max length <= 512" + val ConsentHeaderValueInvalid = "OBP-35032: The Consent's Request Header value is not formatted as UUID or JWT." //Authorisations val AuthorisationNotFound = "OBP-36001: Authorisation not found. Please specify valid values for PAYMENT_ID and AUTHORISATION_ID. " diff --git a/obp-api/src/main/scala/code/api/util/JwtUtil.scala b/obp-api/src/main/scala/code/api/util/JwtUtil.scala index b82b44cea..f77b05087 100644 --- a/obp-api/src/main/scala/code/api/util/JwtUtil.scala +++ b/obp-api/src/main/scala/code/api/util/JwtUtil.scala @@ -19,6 +19,16 @@ import net.liftweb.common.{Box, Empty, Failure, Full} object JwtUtil extends MdcLoggable { + def checkIfStringIsJWTValue(jwtToken: String): Box[String] = { + try { + val signedJWT: SignedJWT = SignedJWT.parse(jwtToken) + Full(signedJWT.toString()) + } catch { + case e: Exception => + Failure(e.getMessage()) + } + } + def getSignedPayloadAsJson(jwtToken: String): Box[String] = { try { val signedJWT = SignedJWT.parse(jwtToken) diff --git a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala index a85383f38..2601cffde 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala @@ -3678,7 +3678,7 @@ trait APIMethods310 { | - You as the service provider have determined an application is compromised or malicious, and want to disable it | - etc. | - |Please note that this endpoint oly supports the case: "The user explicitly wishes to revoke the application’s access" + |Please note that this endpoint only supports the case:: "The user explicitly wishes to revoke the application’s access" | |OBP as a resource server stores access tokens in a database, then it is relatively easy to revoke some token that belongs to a particular user. |The status of the token is changed to "REVOKED" so the next time the revoked client makes a request, their token will fail to validate. diff --git a/obp-api/src/test/scala/code/api/v5_0_0/ConsentRequestTest.scala b/obp-api/src/test/scala/code/api/v5_0_0/ConsentRequestTest.scala index 200f0919a..fbd2cc4dd 100644 --- a/obp-api/src/test/scala/code/api/v5_0_0/ConsentRequestTest.scala +++ b/obp-api/src/test/scala/code/api/v5_0_0/ConsentRequestTest.scala @@ -150,8 +150,14 @@ class ConsentRequestTest extends V500ServerSetupAsync with PropsReset{ responseGetUsersSecond.code should equal(200) val usersSecond = responseGetUsersSecond.body.extract[UsersJsonV400].users usersSecond.size should be > 0 - users.size should equal(usersSecond.size) + + // Test Request Header "Consent-JWT:INVALID_JWT_VALUE" + val wrongRequestHeader = (s"Consent-JWT", "INVALID_JWT_VALUE") + val responseGetUsersWrong = makeGetRequest(requestGetUsers, List(wrongRequestHeader)) + Then("We get successful response") + responseGetUsersWrong.code should equal(401) + responseGetUsersWrong.body.extract[ErrorMessage].message contains (ConsentHeaderValueInvalid) should be (true) } }