import ai.shieldlabs.Webhooks;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.time.Instant;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public final class QuickStart {
private final HttpClient http = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NEVER).build();
private final ObjectMapper json = new ObjectMapper();
private final String domain = UUID.fromString(System.getenv("SHIELDLABS_DOMAIN_ID")).toString();
private final Set<String> used = ConcurrentHashMap.newKeySet(); // demonstration only
public JsonNode readIdentification(String identificationId) throws Exception {
String id = UUID.fromString(identificationId).toString();
HttpRequest request = HttpRequest.newBuilder(URI.create(
"https://account.shieldlabs.ai/api/v1/domains/" + domain + "/history/identifications/" + id))
.timeout(Duration.ofSeconds(5))
.header("Authorization", "Bearer " + System.getenv("SHIELDLABS_API_KEY"))
.header("Accept", "application/json").GET().build();
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) throw new IllegalStateException("History status " + response.statusCode());
JsonNode card = json.readTree(response.body()).path("data");
if (!id.equals(card.path("id").asText()) || !domain.equals(card.path("domain_id").asText())
|| !"identification".equals(card.path("type").asText())) throw new IllegalStateException("History identity mismatch");
return card;
}
public boolean allowSignup(String identificationId, String expectedUserHid) throws Exception {
JsonNode card = readIdentification(identificationId);
JsonNode risk = card.path("risk"), signals = card.path("signals");
if (!"ok".equals(risk.path("state").asText()) || !"ok".equals(signals.path("state").asText())
|| !risk.path("score").isNumber() || !signals.path("items").isArray()) return false;
double score = risk.path("score").asDouble();
if (!Double.isFinite(score) || score < 0 || score > 100) return false;
String band = score < 30 ? "trusted" : score < 60 ? "suspicious" : "dangerous";
if (!band.equals(risk.path("band").asText()) || score >= 60) return false;
for (JsonNode signal : signals.path("items")) {
if (!signal.isTextual() || Set.of("banned_ip", "browser_automation").contains(signal.asText())) return false;
}
Duration age = Duration.between(Instant.parse(card.path("created_at").asText()), Instant.now());
if (age.isNegative() || age.compareTo(Duration.ofMinutes(5)) > 0) return false;
if (expectedUserHid != null && !expectedUserHid.equals(card.path("user_hid").asText())) return false;
return used.add(domain + ":" + card.path("id").asText());
}
public JsonNode verifyWebhook(byte[] rawBody, String signature) throws Exception {
String secret = System.getenv("SHIELDLABS_WEBHOOK_SECRET");
if (secret == null || secret.isEmpty()) throw new IllegalStateException("Webhook secret is not configured");
if (!Webhooks.verifySignature(rawBody, signature, secret)) throw new IllegalArgumentException("Invalid signature");
JsonNode event = json.readTree(rawBody);
if (!event.path("event_id").isTextual() || event.path("event_id").asText().isEmpty()) throw new IllegalArgumentException("Missing event_id");
return event;
}
}