Search in sources :

Example 1 with RequestValidationException

use of com.messagebird.exceptions.RequestValidationException in project java-rest-api by messagebird.

the class RequestValidator method validateSignature.

/**
 * Returns raw signature payload after validating a signature successfully,
 * otherwise throws {@code RequestValidationException}.
 * <p>
 * This JWT is signed with a MessageBird account unique secret key, ensuring the request is from MessageBird and
 * a specific account.
 * The JWT contains the following claims:
 * </p>
 * <ul>
 *   <li>"url_hash" - the raw URL hashed with SHA256 ensuring the URL wasn't altered.</li>
 *   <li> "payload_hash" - the raw payload hashed with SHA256 ensuring the payload wasn't altered.</li>
 *   <li> "jti" - a unique token ID to implement an optional non-replay check (NOT validated by default).</li>
 *   <li> "nbf" - the not before timestamp.</li>
 *   <li> "exp" - the expiration timestamp is ensuring that a request isn't captured and used at a later time.</li>
 *   <li> "iss" - the issuer name, always MessageBird.</li>
 * </ul>
 *
 * @param clock       custom {@link Clock} instance to validate timestamp claims.
 * @param signature   the actual signature.
 * @param url         the raw url including the protocol, hostname and query string,
 *                    {@code https://example.com/?example=42}.
 * @param requestBody the raw request body.
 * @return raw signature payload as {@link DecodedJWT} object.
 * @throws RequestValidationException when the signature is invalid.
 * @see <a href="https://developers.messagebird.com/docs/verify-http-requests">Verify HTTP Requests</a>
 */
public DecodedJWT validateSignature(Clock clock, String signature, String url, byte[] requestBody) throws RequestValidationException {
    if (signature == null || signature.length() == 0)
        throw new RequestValidationException("The signature can not be empty.");
    if (!skipURLValidation && (url == null || url.length() == 0))
        throw new RequestValidationException("The url can not be empty.");
    DecodedJWT jwt = JWT.decode(signature);
    Algorithm algorithm;
    switch(jwt.getAlgorithm()) {
        case "HS256":
            algorithm = HMAC256;
            break;
        case "HS384":
            algorithm = HMAC384;
            break;
        case "HS512":
            algorithm = HMAC512;
            break;
        default:
            throw new RequestValidationException(String.format("The signing method '%s' is invalid.", jwt.getAlgorithm()));
    }
    BaseVerification builder = (BaseVerification) JWT.require(algorithm).withIssuer("MessageBird").ignoreIssuedAt().acceptLeeway(1);
    if (!skipURLValidation)
        builder.withClaim("url_hash", calculateSha256(url.getBytes()));
    boolean payloadHashClaimExist = !jwt.getClaim("payload_hash").isNull();
    if (requestBody != null && requestBody.length > 0) {
        if (!payloadHashClaimExist) {
            throw new RequestValidationException("The Claim 'payload_hash' is not set but payload is present.");
        }
        builder.withClaim("payload_hash", calculateSha256(requestBody));
    } else if (payloadHashClaimExist) {
        throw new RequestValidationException("The Claim 'payload_hash' is set but actual payload is missing.");
    }
    JWTVerifier verifier = clock == null ? builder.build() : builder.build(clock);
    try {
        return verifier.verify(jwt);
    } catch (SignatureVerificationException e) {
        throw new RequestValidationException("Signature is invalid.", e);
    } catch (JWTVerificationException e) {
        throw new RequestValidationException(e.getMessage(), e.getCause());
    }
}
Also used : JWTVerificationException(com.auth0.jwt.exceptions.JWTVerificationException) SignatureVerificationException(com.auth0.jwt.exceptions.SignatureVerificationException) RequestValidationException(com.messagebird.exceptions.RequestValidationException) DecodedJWT(com.auth0.jwt.interfaces.DecodedJWT) Algorithm(com.auth0.jwt.algorithms.Algorithm) JWTVerifier(com.auth0.jwt.interfaces.JWTVerifier) BaseVerification(com.auth0.jwt.JWTVerifier.BaseVerification)

Example 2 with RequestValidationException

use of com.messagebird.exceptions.RequestValidationException in project java-rest-api by messagebird.

the class RequestValidatorTest method testWebhookSignature.

@Test
public void testWebhookSignature() throws Throwable {
    RequestValidator validator = new RequestValidator(testCase.secret != null ? testCase.secret : "");
    Clock clock = mock(Clock.class);
    Date clockDate = spy(Date.from(OffsetDateTime.parse(testCase.timestamp).toInstant()));
    when(clock.getToday()).thenReturn(clockDate);
    ThrowingRunnable runnable = () -> validator.validateSignature(clock, testCase.token, testCase.url, (testCase.payload == null) ? null : testCase.payload.getBytes(StandardCharsets.UTF_8));
    if (testCase.valid) {
        runnable.run();
        return;
    }
    assertTrue(String.format("Expected error message mapping for '%s' but it was not found.", testCase.reason), ERROR_MAP.containsKey(testCase.reason));
    String expectedError = ERROR_MAP.get(testCase.reason);
    RequestValidationException err = assertThrows(RequestValidationException.class, runnable);
    assertTrue(String.format("Expected error message containing: %s (originally %s) but was: %s", expectedError, testCase.reason, err.getMessage()), err.getMessage().contains(expectedError));
}
Also used : Clock(com.auth0.jwt.interfaces.Clock) RequestValidationException(com.messagebird.exceptions.RequestValidationException) ThrowingRunnable(org.junit.function.ThrowingRunnable) Test(org.junit.Test)

Aggregations

RequestValidationException (com.messagebird.exceptions.RequestValidationException)2 BaseVerification (com.auth0.jwt.JWTVerifier.BaseVerification)1 Algorithm (com.auth0.jwt.algorithms.Algorithm)1 JWTVerificationException (com.auth0.jwt.exceptions.JWTVerificationException)1 SignatureVerificationException (com.auth0.jwt.exceptions.SignatureVerificationException)1 Clock (com.auth0.jwt.interfaces.Clock)1 DecodedJWT (com.auth0.jwt.interfaces.DecodedJWT)1 JWTVerifier (com.auth0.jwt.interfaces.JWTVerifier)1 Test (org.junit.Test)1 ThrowingRunnable (org.junit.function.ThrowingRunnable)1