DEV Community

DEV-AI
DEV-AI

Posted on

Integrating Keycloak 24 with External Provider Using Custom Webhook and JWT Flow: Complete Java Implementation Guide

This guide covers the Java code and configuration needed to integrate Keycloak 24 with an external authentication provider using a webhook and JWT flow. It includes SPI setup, webhook handling, user management, and Spring Boot JWT validation.


1. Keycloak Realm and Client Configuration

Steps:

  • Login to Keycloak Admin Console.
  • Create a new realm (Create Realm → enter name → Create)[^4][^9].
  • Add a client under this realm:
    • Client Protocol: openid-connect
    • Root URL: your app's base URL
    • Valid Redirect URIs: your app's callback endpoint
    • Save and note the client ID and secret[^2][^4].

JWT Settings:
Keycloak issues JWTs by default for OIDC clients. You can adjust token lifetimes in the client settings[^2].


2. Implementing the Custom Authenticator SPI

Project Setup:

  • Java Maven project.
  • Add Keycloak SPI dependencies in pom.xml:
<dependency>
  <groupId>org.keycloak</groupId>
  <artifactId>keycloak-server-spi</artifactId>
  <version>24.0.0</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>org.keycloak</groupId>
  <artifactId>keycloak-server-spi-private</artifactId>
  <version>24.0.0</version>
  <scope>provided</scope>
</dependency>
Enter fullscreen mode Exit fullscreen mode
  • Build with mvn clean package[^1].

SPI Implementation:

  • Implement Authenticator and AuthenticatorFactory interfaces[^1][^3][^11].
  • Example skeleton:
public class ExternalWebhookAuthenticator implements Authenticator {
    @Override
    public void authenticate(AuthenticationFlowContext context) {
        UserModel user = context.getUser();
        if ("true".equals(user.getFirstAttribute("external_verified"))) {
            context.success();
        } else {
            // Trigger external verification, e.g., redirect or show waiting page
        }
    }
    // Implement action(), close(), etc.
}
Enter fullscreen mode Exit fullscreen mode
  • Register the factory in META-INF/services/org.keycloak.authentication.AuthenticatorFactory with the fully qualified class name[^3][^8][^11].

Deploy:

  • Copy the JAR to KEYCLOAK_HOME/providers/
  • Run bin/kc.sh build and restart Keycloak[^1][^6].

Configure in Admin Console:

  • Go to Authentication → Create new flow (type: Basic).
  • Add execution: select your custom authenticator, set to REQUIRED.
  • Assign this flow to your client under Authentication flow overrides[^1][^6].

3. Webhook Endpoint in Spring Boot

Dependencies:

  • spring-boot-starter-web
  • keycloak-admin-client
  • spring-boot-starter-oauth2-resource-server (for JWT validation)[^5][^10]

Example Controller:

@RestController
@RequestMapping("/external-auth")
public class WebhookController {
    @Autowired KeycloakUserService keycloakUserService;

    @PostMapping("/webhook")
    public ResponseEntity<?> handleWebhook(@RequestBody WebhookPayload payload) {
        if ("COMPLETED".equals(payload.getStatus())) {
            keycloakUserService.upsertUser(payload);
            return ResponseEntity.ok().build();
        }
        return ResponseEntity.badRequest().build();
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Security: Validate webhook requests (e.g., HMAC header, IP allowlist).

Example Payload:

{
  "status": "COMPLETED",
  "userId": "1000062537",
  "transactionId": "3136fd60-3f89-4d24-a92f-b9c63a53807f",
  "userData": {
    "firstName": "JOHN",
    "lastName": "SAT",
    "dateOfBirth": "1990-01-01",
    "nationality": "US"
  },
  "verificationTimestamp": "2025-06-03T12:23:45Z"
}
Enter fullscreen mode Exit fullscreen mode

4. Keycloak User Management (Java)

Service Example:

@Service
public class KeycloakUserService {
    @Autowired Keycloak keycloak;
    @Value("${keycloak.realm}") String realm;

    public void upsertUser(WebhookPayload payload) {
        RealmResource realmRes = keycloak.realm(realm);
        UsersResource users = realmRes.users();
        List<UserRepresentation> found = users.search(payload.getUserId(), true);
        UserRepresentation user;
        if (found.isEmpty()) {
            user = new UserRepresentation();
            user.setUsername(payload.getUserId());
            user.setEnabled(true);
            user.setFirstName(payload.getUserData().getFirstName());
            user.setLastName(payload.getUserData().getLastName());
            user.setAttributes(Map.of(
                "dateOfBirth", List.of(payload.getUserData().getDateOfBirth()),
                "nationality", List.of(payload.getUserData().getNationality()),
                "external_verified", List.of("true")
            ));
            users.create(user);
        } else {
            user = found.get(0);
            user.setFirstName(payload.getUserData().getFirstName());
            user.setLastName(payload.getUserData().getLastName());
            user.getAttributes().put("external_verified", List.of("true"));
            users.get(user.getId()).update(user);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Ensure your Keycloak client has manage-users role[^2].

5. Completing the JWT/OIDC Flow

OIDC Authorization URL:

GET /realms/{realm}/protocol/openid-connect/auth
  ?client_id={client_id}
  &redirect_uri={redirect_uri}
  &response_type=code
  &scope=openid
Enter fullscreen mode Exit fullscreen mode
  • After user is verified, redirect to this URL to complete login and receive JWT[^2].

Token Exchange Example:

POST /auth/realms/{realm}/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=AUTH_CODE&
client_id=MY_CLIENT_ID&
client_secret=MY_CLIENT_SECRET&
redirect_uri=MY_REDIRECT_URI
Enter fullscreen mode Exit fullscreen mode
  • Response includes access_token (JWT)[^2].

6. Application JWT Validation (Spring Boot)

application.yml:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://{keycloak-domain}/realms/{realm}
Enter fullscreen mode Exit fullscreen mode
  • Spring Security will now validate JWTs from Keycloak[^5][^10].

7. Security Best Practices

  • Webhook Security: Validate HMAC signatures or restrict to provider IPs[^7].
  • Short-lived JWTs: Adjust token lifetimes in Keycloak client settings[^2].
  • Attribute Mapping: Only trust data from verified webhooks.
  • Logging: Monitor webhook and authentication events.

Top comments (0)