auth

Work-email registration in Keycloak

Options for checking Keycloak self-registration with isBusinessEmail: a custom registration form action, an event listener, or a check in front of Keycloak.

isBusinessEmail for Keycloak

Keycloak is extended in Java through its Service Provider Interfaces (SPIs). There’s no built-in “call this HTTP API on registration” switch, so you choose where the check runs:

Option Runs Can reject? Effort
Custom registration form action (Authentication SPI) During the self-registration form Yes, with a field error Medium: a small provider JAR
Event listener (Event Listener SPI) on REGISTER events Right after the user is created No, but can disable or flag the user Low–medium
Check in your app or gateway before sending users to Keycloak’s registration page Before Keycloak Yes, in your own UI Low

The Keycloak Server Developer Guide documents both SPIs and how to deploy custom providers.

The HTTP call (Java 11+)

Whichever option you choose, the call is the same. Keep it short-timeout and fail open.

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.util.Map;

public final class WorkEmailCheck {
    private static final HttpClient HTTP = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(2)).build();
    private static final ObjectMapper JSON = new ObjectMapper();

    /** Returns "allow", "review" or "block". Fails open to "allow". */
    public static String recommendation(String email) {
        try {
            HttpRequest req = HttpRequest.newBuilder(URI.create("https://api.isbusinessemail.com/v1/check"))
                    .timeout(Duration.ofMillis(2500))
                    .header("Authorization", "Bearer " + System.getenv("IBE_API_KEY"))
                    .header("Content-Type", "application/json")
                    .POST(HttpRequest.BodyPublishers.ofString(JSON.writeValueAsString(Map.of("email", email))))
                    .build();
            HttpResponse<String> res = HTTP.send(req, HttpResponse.BodyHandlers.ofString());
            if (res.statusCode() != 200) return "allow";
            JsonNode body = JSON.readTree(res.body());
            return body.path("recommendation").asText("allow");
        } catch (Exception e) {
            return "allow";
        }
    }
}

Keycloak already ships Jackson, so a provider JAR can usually use it without bundling it again.

Option 1: registration form action

Add a form action to a copy of the registration flow. In its validation step, read the email form field, call WorkEmailCheck.recommendation, and on block fail validation with an error on the email field (“Please use your work email.”). Then bind your copied flow as the realm’s registration flow.

Option 2: event listener

Implement an event listener that reacts to REGISTER events, looks up the new user’s email, and on block disables the user or sets an attribute such as email_category=personal for your app to act on. Enable the listener in the realm’s event settings.

Tips

  • Pass the secret key through an environment variable or Keycloak’s vault, not realm configuration visible to admins.
  • Identity-provider logins (Google, Microsoft) create users through the first-broker-login flow. Cover that flow too if you need every user checked.
  • Test with personal@test.isbusinessemail.com and business@test.isbusinessemail.com.

Official docs: Keycloak documentation. See also: Categories and policies.