All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.onfido.WebhookEventVerifier Maven / Gradle / Ivy

package com.onfido;

import com.google.gson.FieldNamingPolicy;
import com.google.gson.FieldNamingStrategy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonParseException;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.onfido.model.WebhookEvent;
import com.onfido.model.WebhookEventType;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.OffsetDateTime;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

/** Contains a public method for verifying webhook responses. */
public class WebhookEventVerifier {
  private final String webhookToken;
  private Gson gson;

  /**
   * Instantiates a new WebhookEventVerifier.
   *
   * @param webhookToken the webhook token
   */
  public WebhookEventVerifier(String webhookToken) {
    this.webhookToken = webhookToken;
    this.gson =
        new GsonBuilder()
            .registerTypeAdapter(
                WebhookEventType.class,
                new TypeAdapter() {
                  @Override
                  public void write(JsonWriter out, WebhookEventType value) throws IOException {
                    out.value(value.name().toLowerCase().replace("_", "."));
                  }

                  @Override
                  public WebhookEventType read(JsonReader in) throws IOException {
                    return WebhookEventType.valueOf(
                        in.nextString().toUpperCase().replace(".", "_"));
                  }
                })
            .registerTypeAdapter(
                OffsetDateTime.class,
                (JsonDeserializer)
                    (json, type, context) -> OffsetDateTime.parse(json.getAsString()))
            .setFieldNamingStrategy(
                new FieldNamingStrategy() {
                  @Override
                  public String translateName(Field field) {
                    String name =
                        FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES.translateName(field);

                    // To handle object (and possibily others) prefixed with _ since reserved
                    if (name.startsWith("_")) {
                      return name.substring(1);
                    }

                    return name;
                  }
                })
            .create();
  }

  /**
   * Takes in a webhook response and signature and verifies it against the instance's token
   *
   * @param rawEventBody the raw event body
   * @param hexSignature the hex signature
   * @return the webhook event
   * @throws OnfidoInvalidSignatureError the onfido exception
   */
  public WebhookEvent readPayload(String rawEventBody, String hexSignature)
      throws OnfidoInvalidSignatureError {
    Mac sha256Hmac;
    SecretKeySpec secretKey;

    try {
      sha256Hmac = Mac.getInstance("HmacSHA256");
      secretKey = new SecretKeySpec(webhookToken.getBytes(), "HmacSHA256");
      sha256Hmac.init(secretKey);
    } catch (Exception ex) {
      throw new OnfidoInvalidSignatureError(ex);
    }

    String eventSignature =
        new String(
            encodeHexString(sha256Hmac.doFinal(rawEventBody.getBytes(StandardCharsets.UTF_8))));

    // MessageDigest.isEqual() is a time safe comparison
    if (!MessageDigest.isEqual(eventSignature.getBytes(), hexSignature.getBytes())) {
      throw new OnfidoInvalidSignatureError("Invalid signature for webhook event");
    }

    try {
      JsonReader jsonReader = new JsonReader(new StringReader(rawEventBody));
      jsonReader.setLenient(true);

      return gson.fromJson(jsonReader, WebhookEvent.class);
    } catch (JsonParseException e) {
      throw new OnfidoInvalidSignatureError("Invalid payload for webhook event", e);
    }
  }

  private String encodeHexString(byte[] byteArray) {
    StringBuffer hexStringBuffer = new StringBuffer();
    for (int i = 0; i < byteArray.length; i++) {
      hexStringBuffer.append(byteToHex(byteArray[i]));
    }
    return hexStringBuffer.toString();
  }

  private String byteToHex(byte num) {
    char[] hexDigits = new char[2];
    hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);
    hexDigits[1] = Character.forDigit((num & 0xF), 16);
    return new String(hexDigits);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy