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].
- Client Protocol:
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>
- Build with
mvn clean package
[^1].
SPI Implementation:
- Implement
Authenticator
andAuthenticatorFactory
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.
}
- 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();
}
}
- 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"
}
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);
}
}
}
- 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
- 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
- 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}
- 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)