Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/src/main/java/org/keycloak/OAuthErrorException.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public class OAuthErrorException extends Exception {

// Others
public static final String INVALID_CLIENT = "invalid_client";
public static final String INVALID_CLIENT_ATTESTATION = "invalid_client_attestation";
public static final String INVALID_GRANT = "invalid_grant";
public static final String UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type";
public static final String UNSUPPORTED_TOKEN_TYPE = "unsupported_token_type";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,14 @@ public static String getJavaAlgorithm(Algorithm alg) {
return "SHA384withRSA";
case RS512:
return "SHA512withRSA";
case PS256:
return "SHA256withRSAandMGF1";
case PS384:
return "SHA384withRSAandMGF1";
case PS512:
return "SHA512withRSAandMGF1";
default:
throw new IllegalArgumentException("Not an RSA Algorithm");
throw new IllegalArgumentException("Not a supported RSA Algorithm: " + alg);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ public enum AuthenticationFlowError {
CLIENT_DISABLED,
CLIENT_CREDENTIALS_SETUP_REQUIRED,
INVALID_CLIENT_CREDENTIALS,
INVALID_CLIENT_ATTESTATION,

IDENTITY_PROVIDER_NOT_FOUND,
IDENTITY_PROVIDER_DISABLED,
IDENTITY_PROVIDER_ERROR,
DISPLAY_NOT_SUPPORTED,

ACCESS_DENIED,
UNAUTHORIZED_CLIENT,
GENERIC_AUTHENTICATION_ERROR
}
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ public static void addIdentityProviderAuthenticator(RealmModel realm, String def
}

public static void clientAuthFlow(RealmModel realm) {

AuthenticationFlowModel clients = new AuthenticationFlowModel();
clients.setAlias(CLIENT_AUTHENTICATION_FLOW);
clients.setDescription("Base authentication for clients");
Expand All @@ -474,6 +475,18 @@ public static void clientAuthFlow(RealmModel realm) {
clients = realm.addAuthenticationFlow(clients);
realm.setClientAuthenticationFlow(clients);

// Attestation-Based Client Authentication is a stronger authentication method
//
if (Profile.isFeatureEnabled(Feature.CLIENT_AUTH_ABCA)) {
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
execution.setParentFlow(clients.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
execution.setAuthenticator("attestation-based");
execution.setPriority(5);
execution.setAuthenticatorFlow(false);
realm.addAuthenticatorExecution(execution);
}

AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
execution.setParentFlow(clients.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

import jakarta.ws.rs.core.Response;

import org.keycloak.authentication.authenticators.client.AttestationBasedClientAuthenticator;
import org.keycloak.common.Profile;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.models.AuthenticationExecutionModel;
Expand Down Expand Up @@ -77,7 +79,8 @@ public Response processFlow() {
if (client != null) {
String expectedClientAuthType = client.getClientAuthenticatorType();

// Fallback to secret just in case (for backwards compatibility). Also for public clients, ignore the "clientAuthenticatorType", which is set to them and stick to the
// Fallback to secret just in case (for backwards compatibility).
// Also for public clients, ignore the "clientAuthenticatorType", which is set to them and stick to the
// default, which set the client just based on "client_id" parameter
if (expectedClientAuthType == null || client.isPublicClient()) {
if (expectedClientAuthType == null) {
Expand All @@ -86,6 +89,14 @@ public Response processFlow() {
expectedClientAuthType = KeycloakModelUtils.getDefaultClientAuthenticatorType();
}

// Use expectedClientAuthType=attestation-based for public client
// when AttestationBasedClientAuthenticator is processed
//
String abcaAuthType = AttestationBasedClientAuthenticator.PROVIDER_ID;
if (client.isPublicClient() && factory.getId().equals(abcaAuthType) && Profile.isFeatureEnabled(Profile.Feature.CLIENT_AUTH_ABCA)) {
expectedClientAuthType = abcaAuthType;
}

// Check if client authentication matches
if (factory.getId().equals(expectedClientAuthType)) {
Response response = processResult(context);
Expand Down
Loading
Loading