KEYCLOAK-15448 FAPI-RW : Error Response on OIDC private_key_jwt Client Authentication Error (400 error=invalid_client)

This commit is contained in:
Takashi Norimatsu 2020-09-04 17:23:27 +09:00 committed by Marek Posolda
parent df52c12ebb
commit cbb79f0430
2 changed files with 15 additions and 13 deletions

View file

@ -33,6 +33,7 @@ import javax.ws.rs.core.Response;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.keycloak.authentication.AuthenticationFlowError; import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.ClientAuthenticationFlowContext; import org.keycloak.authentication.ClientAuthenticationFlowContext;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
@ -185,7 +186,7 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
context.success(); context.success();
} catch (Exception e) { } catch (Exception e) {
ServicesLogger.LOGGER.errorValidatingAssertion(e); ServicesLogger.LOGGER.errorValidatingAssertion(e);
Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", "Client authentication with signed JWT failed: " + e.getMessage()); Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), OAuthErrorException.INVALID_CLIENT, "Client authentication with signed JWT failed: " + e.getMessage());
context.failure(AuthenticationFlowError.INVALID_CLIENT_CREDENTIALS, challengeResponse); context.failure(AuthenticationFlowError.INVALID_CLIENT_CREDENTIALS, challengeResponse);
} }
} }
@ -193,7 +194,7 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
protected PublicKey getSignatureValidationKey(ClientModel client, ClientAuthenticationFlowContext context, JWSInput jws) { protected PublicKey getSignatureValidationKey(ClientModel client, ClientAuthenticationFlowContext context, JWSInput jws) {
PublicKey publicKey = PublicKeyStorageManager.getClientPublicKey(context.getSession(), client, jws); PublicKey publicKey = PublicKeyStorageManager.getClientPublicKey(context.getSession(), client, jws);
if (publicKey == null) { if (publicKey == null) {
Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "unauthorized_client", "Unable to load public key"); Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), OAuthErrorException.INVALID_CLIENT, "Unable to load public key");
context.failure(AuthenticationFlowError.CLIENT_CREDENTIALS_SETUP_REQUIRED, challengeResponse); context.failure(AuthenticationFlowError.CLIENT_CREDENTIALS_SETUP_REQUIRED, challengeResponse);
return null; return null;
} else { } else {

View file

@ -36,6 +36,7 @@ import org.junit.BeforeClass;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.keycloak.adapters.AdapterUtils; import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.authentication.JWTClientCredentialsProvider; import org.keycloak.adapters.authentication.JWTClientCredentialsProvider;
import org.keycloak.admin.client.resource.ClientAttributeCertificateResource; import org.keycloak.admin.client.resource.ClientAttributeCertificateResource;
@ -744,7 +745,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
CloseableHttpResponse resp = sendRequest(oauth.getServiceAccountUrl(), parameters); CloseableHttpResponse resp = sendRequest(oauth.getServiceAccountUrl(), parameters);
OAuthClient.AccessTokenResponse response = new OAuthClient.AccessTokenResponse(resp); OAuthClient.AccessTokenResponse response = new OAuthClient.AccessTokenResponse(resp);
assertError(response, "client1", "unauthorized_client", "client_credentials_setup_required"); assertError(response, "client1", OAuthErrorException.INVALID_CLIENT, "client_credentials_setup_required");
ClientManager.realm(adminClient.realm("test")).clientId("client1").updateAttribute(JWTClientAuthenticator.CERTIFICATE_ATTR, backupClient1Cert.certificate); ClientManager.realm(adminClient.realm("test")).clientId("client1").updateAttribute(JWTClientAuthenticator.CERTIFICATE_ATTR, backupClient1Cert.certificate);
} }
@ -762,7 +763,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
CloseableHttpResponse resp = sendRequest(oauth.getServiceAccountUrl(), parameters); CloseableHttpResponse resp = sendRequest(oauth.getServiceAccountUrl(), parameters);
OAuthClient.AccessTokenResponse response = new OAuthClient.AccessTokenResponse(resp); OAuthClient.AccessTokenResponse response = new OAuthClient.AccessTokenResponse(resp);
assertError(response, "client1", "unauthorized_client", AuthenticationFlowError.CLIENT_CREDENTIALS_SETUP_REQUIRED.toString().toLowerCase()); assertError(response, "client1", OAuthErrorException.INVALID_CLIENT, AuthenticationFlowError.CLIENT_CREDENTIALS_SETUP_REQUIRED.toString().toLowerCase());
} }
@ -782,7 +783,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
setTimeOffset(0); setTimeOffset(0);
assertError(response, "client1", "unauthorized_client", Errors.INVALID_CLIENT_CREDENTIALS); assertError(response, "client1", OAuthErrorException.INVALID_CLIENT, Errors.INVALID_CLIENT_CREDENTIALS);
} }
@Test @Test
@ -801,7 +802,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
setTimeOffset(0); setTimeOffset(0);
assertError(response, "client1", "unauthorized_client", Errors.INVALID_CLIENT_CREDENTIALS); assertError(response, "client1", OAuthErrorException.INVALID_CLIENT, Errors.INVALID_CLIENT_CREDENTIALS);
} }
@ -820,14 +821,14 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
response = doClientCredentialsGrantRequest(clientJwt); response = doClientCredentialsGrantRequest(clientJwt);
assertEquals(400, response.getStatusCode()); assertEquals(400, response.getStatusCode());
assertEquals("unauthorized_client", response.getError()); assertEquals(OAuthErrorException.INVALID_CLIENT, response.getError());
} }
@Test @Test
public void testMissingIdClaim() throws Exception { public void testMissingIdClaim() throws Exception {
OAuthClient.AccessTokenResponse response = testMissingClaim("id"); OAuthClient.AccessTokenResponse response = testMissingClaim("id");
assertError(response, app1.getClientId(), "unauthorized_client", Errors.INVALID_CLIENT_CREDENTIALS); assertError(response, app1.getClientId(), OAuthErrorException.INVALID_CLIENT, Errors.INVALID_CLIENT_CREDENTIALS);
} }
@Test @Test
@ -845,7 +846,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
@Test @Test
public void testMissingAudienceClaim() throws Exception { public void testMissingAudienceClaim() throws Exception {
OAuthClient.AccessTokenResponse response = testMissingClaim("audience"); OAuthClient.AccessTokenResponse response = testMissingClaim("audience");
assertError(response, app1.getClientId(), "unauthorized_client", Errors.INVALID_CLIENT_CREDENTIALS); assertError(response, app1.getClientId(), OAuthErrorException.INVALID_CLIENT, Errors.INVALID_CLIENT_CREDENTIALS);
} }
@Test @Test
@ -863,11 +864,11 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
// Test expired lifespan // Test expired lifespan
response = testMissingClaim(-11, "expiration"); response = testMissingClaim(-11, "expiration");
assertError(response, app1.getClientId(), "unauthorized_client", Errors.INVALID_CLIENT_CREDENTIALS); assertError(response, app1.getClientId(), OAuthErrorException.INVALID_CLIENT, Errors.INVALID_CLIENT_CREDENTIALS);
// Missing exp and issuedAt should return error // Missing exp and issuedAt should return error
response = testMissingClaim("expiration", "issuedAt"); response = testMissingClaim("expiration", "issuedAt");
assertError(response, app1.getClientId(), "unauthorized_client", Errors.INVALID_CLIENT_CREDENTIALS); assertError(response, app1.getClientId(), OAuthErrorException.INVALID_CLIENT, Errors.INVALID_CLIENT_CREDENTIALS);
} }
@Test @Test
@ -934,7 +935,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
@Test @Test
public void testCodeToTokenRequestFailureRS256() throws Exception { public void testCodeToTokenRequestFailureRS256() throws Exception {
testCodeToTokenRequestFailure(Algorithm.RS256, "unauthorized_client", "client_credentials_setup_required"); testCodeToTokenRequestFailure(Algorithm.RS256, OAuthErrorException.INVALID_CLIENT, "client_credentials_setup_required");
} }
@Test @Test
@ -1012,7 +1013,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest {
OAuthClient.AccessTokenResponse response = doGrantAccessTokenRequest("test-user@localhost", "password", getClient2SignedJWT()); OAuthClient.AccessTokenResponse response = doGrantAccessTokenRequest("test-user@localhost", "password", getClient2SignedJWT());
assertEquals(400, response.getStatusCode()); assertEquals(400, response.getStatusCode());
assertEquals("unauthorized_client", response.getError()); assertEquals(OAuthErrorException.INVALID_CLIENT, response.getError());
events.expect(EventType.LOGIN_ERROR) events.expect(EventType.LOGIN_ERROR)
.client("client2") .client("client2")