Skip to content

Commit 651ab16

Browse files
marcelocamanhoostrya
authored andcommitted
support confidential clients that authenticate using form fields
Signed-off-by: marcelocamanho <[email protected]>
1 parent c319dd4 commit 651ab16

File tree

2 files changed

+39
-4
lines changed

2 files changed

+39
-4
lines changed

mock/src/main/java/com/tngtech/keycloakmock/impl/handler/TokenRoute.java

+11-4
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,22 @@ private void handlePasswordFlow(RoutingContext routingContext) {
118118
}
119119

120120
private void handleClientCredentialsFlow(RoutingContext routingContext) {
121+
String clientId = routingContext.request().getFormAttribute("client_id");
122+
String password = routingContext.request().getFormAttribute("client_secret");
123+
boolean formBasedAuth = clientId != null && !clientId.isEmpty() && password != null && !password.isEmpty();
121124
final User user = routingContext.user();
122-
if (user == null) {
125+
if (user == null && !formBasedAuth) {
123126
routingContext.fail(401);
124127
return;
125128
}
126129

127-
final String clientId = routingContext.user().get("username");
128-
// Password is a list of roles
129-
final String password = routingContext.user().get("password");
130+
//if not form based, try using user (BASIC auth or custom)
131+
if (!formBasedAuth) {
132+
clientId = routingContext.user().get("username");
133+
// Password is a list of roles
134+
password = routingContext.user().get("password");
135+
}
136+
130137
if (clientId == null || clientId.isEmpty()) {
131138
routingContext.fail(400);
132139
return;

mock/src/test/java/com/tngtech/keycloakmock/api/KeycloakMockIntegrationTest.java

+28
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,34 @@ void mock_server_login_with_client_credentials_flow_works() {
594594
assertThat(tokenConfig.getAudience()).containsExactly("client");
595595
}
596596

597+
@Test
598+
void mock_server_login_with_client_credentials_flow_using_form_works() {
599+
keycloakMock = new KeycloakMock();
600+
keycloakMock.start();
601+
602+
ExtractableResponse<Response> extractableResponse =
603+
RestAssured.given()
604+
.when()
605+
.formParam("grant_type", "client_credentials")
606+
.formParam("client_id", "client")
607+
.formParam("client_secret", "role1,role2,role3")
608+
.post(TOKEN_ENDPOINT_URL)
609+
.then()
610+
.assertThat()
611+
.statusCode(200)
612+
.extract();
613+
614+
String accessToken = extractableResponse.body().jsonPath().getString("access_token");
615+
616+
Jws<Claims> jwt = jwtParser.parseClaimsJws(accessToken);
617+
assertThat(jwt.getBody().getIssuer()).isEqualTo("http://localhost:8000/auth/realms/realm");
618+
TokenConfig tokenConfig = TokenConfig.aTokenConfig().withSourceToken(accessToken).build();
619+
assertThat(tokenConfig.getPreferredUsername()).isEqualTo("client");
620+
assertThat(tokenConfig.getRealmAccess().getRoles())
621+
.containsExactlyInAnyOrder("role1", "role2", "role3");
622+
assertThat(tokenConfig.getAudience()).containsExactly("client");
623+
}
624+
597625
private void getTokenAndValidateAndReturnSessionCookie() {}
598626

599627
private static class ClientRequest {

0 commit comments

Comments
 (0)