Raising an event when a ClientPolicyException is caught #38366

Signed-off-by: Yoshiyuki Tabata <yoshiyuki.tabata.jy@hitachi.com>
This commit is contained in:
Yoshiyuki Tabata
2025-03-24 12:34:48 +09:00
committed by Marek Posolda
parent eb990bcf23
commit 08bac045be
29 changed files with 304 additions and 47 deletions

View File

@@ -108,4 +108,7 @@ public interface Details {
String LOGOUT_TRIGGERED_BY_REQUIRED_ACTION = "logout_triggered_by_required_action";
String ACCESS_TOKEN_EXPIRATION_TIME = "access_token_expiration_time";
String AGE_OF_REFRESH_TOKEN = "age_of_refresh_token";
String CLIENT_POLICY_ERROR = "client_policy_error";
String CLIENT_POLICY_ERROR_DETAIL = "client_policy_error_detail";
}

View File

@@ -292,6 +292,9 @@ public class OIDCLoginProtocol implements LoginProtocol {
try {
session.clientPolicy().triggerOnEvent(new ImplicitHybridTokenResponse(authSession, clientSessionCtx, responseBuilder));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
new AuthenticationSessionManager(session).removeTabIdInAuthenticationSession(realm, authSession);
redirectUri.addParam(OAuth2Constants.ERROR_DESCRIPTION, cpe.getError());

View File

@@ -138,6 +138,10 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
try {
session.clientPolicy().triggerOnEvent(new PreAuthorizationRequestContext(clientId, params));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new ErrorPageException(session, authenticationSession, cpe.getErrorStatus(), cpe.getErrorDetail());
}
checkClient(clientId);
@@ -199,6 +203,10 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase {
try {
session.clientPolicy().triggerOnEvent(new AuthorizationRequestContext(parsedResponseType, request, redirectUri, params, authenticationSession));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
new AuthenticationSessionManager(session).removeAuthenticationSession(realm, authenticationSession, false);
return redirectErrorToClient(parsedResponseMode, cpe.getError(), cpe.getErrorDetail());
}

View File

@@ -490,6 +490,10 @@ public class LogoutEndpoint {
session.clientPolicy().triggerOnEvent(new LogoutRequestContext(form));
refreshToken = form.getFirst(OAuth2Constants.REFRESH_TOKEN);
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
}

View File

@@ -17,6 +17,7 @@
package org.keycloak.protocol.oidc.endpoints;
import org.jboss.resteasy.reactive.NoCache;
import org.keycloak.events.Details;
import org.keycloak.http.HttpRequest;
import org.keycloak.common.ClientConnection;
import org.keycloak.events.Errors;
@@ -102,6 +103,10 @@ public class TokenIntrospectionEndpoint {
session.clientPolicy().triggerOnEvent(new TokenIntrospectContext(formParams));
token = formParams.getFirst(PARAM_TOKEN);
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw throwErrorResponseException(Errors.INVALID_REQUEST, cpe.getErrorDetail(), Status.BAD_REQUEST);
}

View File

@@ -104,6 +104,9 @@ public class TokenRevocationEndpoint {
try {
session.clientPolicy().triggerOnEvent(new TokenRevokeContext(formParams));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
}
@@ -128,6 +131,9 @@ public class TokenRevocationEndpoint {
try {
session.clientPolicy().triggerOnEvent(new TokenRevokeResponseContext(formParams));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
}

View File

@@ -162,16 +162,20 @@ public class UserInfoEndpoint {
private Response issueUserInfo() {
cors.allowAllOrigins();
try {
session.clientPolicy().triggerOnEvent(new UserInfoRequestContext(tokenForUserInfo));
} catch (ClientPolicyException cpe) {
throw error.error(cpe.getError()).errorDescription(cpe.getErrorDetail()).status(cpe.getErrorStatus()).build();
}
EventBuilder event = new EventBuilder(realm, session, clientConnection)
.event(EventType.USER_INFO_REQUEST)
.detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN);
try {
session.clientPolicy().triggerOnEvent(new UserInfoRequestContext(tokenForUserInfo));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw error.error(cpe.getError()).errorDescription(cpe.getErrorDetail()).status(cpe.getErrorStatus()).build();
}
if (tokenForUserInfo.getToken() == null) {
event.detail(Details.REASON, "Missing token");
event.error(Errors.INVALID_TOKEN);

View File

@@ -40,7 +40,6 @@ import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.protocol.oidc.utils.OAuth2Code;
import org.keycloak.protocol.oidc.utils.OAuth2CodeParser;
import org.keycloak.protocol.oidc.utils.PkceUtils;
import org.keycloak.representations.dpop.DPoP;
import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.context.TokenRequestContext;
@@ -182,6 +181,9 @@ public class AuthorizationCodeGrantType extends OAuth2GrantTypeBase {
try {
session.clientPolicy().triggerOnEvent(new TokenRequestContext(formParams, parseResult));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
}

View File

@@ -132,7 +132,9 @@ public class ClientCredentialsGrantType extends OAuth2GrantTypeBase {
try {
session.clientPolicy().triggerOnEvent(new ServiceAccountTokenRequestContext(formParams, clientSessionCtx.getClientSession()));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, cpe.getErrorDetail());
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
}
@@ -164,7 +166,9 @@ public class ClientCredentialsGrantType extends OAuth2GrantTypeBase {
try {
session.clientPolicy().triggerOnEvent(new ServiceAccountTokenResponseContext(formParams, clientSessionCtx.getClientSession(), responseBuilder));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, cpe.getErrorDetail());
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
}

View File

@@ -21,13 +21,7 @@ import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.keycloak.authentication.AuthenticationProcessor;
@@ -62,6 +56,10 @@ import org.keycloak.services.util.AuthorizationContextUtil;
import org.keycloak.services.util.MtlsHoKTokenUtil;
import org.keycloak.util.TokenUtil;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
/**
* Base class for OAuth 2.0 grant types
*
@@ -131,7 +129,9 @@ public abstract class OAuth2GrantTypeBase implements OAuth2GrantType {
try {
session.clientPolicy().triggerOnEvent(clientPolicyContextGenerator.apply(responseBuilder));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, cpe.getErrorDetail());
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
}

View File

@@ -24,7 +24,6 @@ import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.keycloak.common.Profile;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventType;
@@ -63,7 +62,9 @@ public class RefreshTokenGrantType extends OAuth2GrantTypeBase {
session.clientPolicy().triggerOnEvent(new TokenRefreshContext(formParams));
refreshToken = formParams.getFirst(OAuth2Constants.REFRESH_TOKEN);
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, cpe.getErrorDetail());
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
}
@@ -98,7 +99,9 @@ public class RefreshTokenGrantType extends OAuth2GrantTypeBase {
throw new CorsErrorResponseException(cors, e.getError(), e.getDescription(), Response.Status.BAD_REQUEST);
}
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, cpe.getErrorDetail());
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
}

View File

@@ -82,7 +82,9 @@ public class ResourceOwnerPasswordCredentialsGrantType extends OAuth2GrantTypeBa
try {
session.clientPolicy().triggerOnEvent(new ResourceOwnerPasswordCredentialsContext(formParams));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, cpe.getErrorDetail());
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
}
@@ -156,7 +158,9 @@ public class ResourceOwnerPasswordCredentialsGrantType extends OAuth2GrantTypeBa
try {
session.clientPolicy().triggerOnEvent(new ResourceOwnerPasswordCredentialsResponseContext(formParams, clientSessionCtx, responseBuilder));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, cpe.getErrorDetail());
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
}

View File

@@ -82,7 +82,9 @@ public class TokenExchangeGrantType extends OAuth2GrantTypeBase {
//trigger if there is a supported token exchange provider
session.clientPolicy().triggerOnEvent(new TokenExchangeRequestContext(exchange));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, cpe.getErrorDetail());
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
}

View File

@@ -24,10 +24,8 @@ import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.common.Profile;
import org.keycloak.common.util.Time;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
@@ -35,7 +33,6 @@ import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuth2DeviceCodeModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
@@ -44,14 +41,12 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.protocol.oidc.grants.OAuth2GrantType;
import org.keycloak.protocol.oidc.grants.OAuth2GrantTypeBase;
import org.keycloak.protocol.oidc.grants.ciba.channel.CIBAAuthenticationRequest;
import org.keycloak.protocol.oidc.grants.ciba.clientpolicy.context.BackchannelTokenRequestContext;
import org.keycloak.protocol.oidc.grants.ciba.clientpolicy.context.BackchannelTokenResponseContext;
import org.keycloak.protocol.oidc.grants.ciba.endpoints.CibaRootEndpoint;
import org.keycloak.protocol.oidc.grants.device.DeviceGrantType;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.Urls;
@@ -145,6 +140,9 @@ public class CibaGrantType extends OAuth2GrantTypeBase {
try {
session.clientPolicy().triggerOnEvent(new BackchannelTokenRequestContext(request, formParams));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
}

View File

@@ -19,6 +19,7 @@ package org.keycloak.protocol.oidc.grants.ciba.endpoints;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.NoCache;
import org.keycloak.events.Details;
import org.keycloak.http.HttpRequest;
import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
@@ -232,6 +233,10 @@ public class BackchannelAuthenticationEndpoint extends AbstractCibaEndpoint {
try {
session.clientPolicy().triggerOnEvent(new BackchannelAuthenticationRequestContext(endpointRequest, request, params));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
}

View File

@@ -333,7 +333,9 @@ public class DeviceGrantType extends OAuth2GrantTypeBase {
try {
session.clientPolicy().triggerOnEvent(new DeviceTokenRequestContext(deviceCodeModel, formParams));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, cpe.getErrorDetail());
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, cpe.getErrorDetail(),
Response.Status.BAD_REQUEST);

View File

@@ -151,6 +151,10 @@ public class DeviceEndpoint extends AuthorizationEndpointBase implements RealmRe
try {
session.clientPolicy().triggerOnEvent(new DeviceAuthorizationRequestContext(request, httpRequest.getDecodedFormParameters()));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
}

View File

@@ -18,6 +18,7 @@
package org.keycloak.protocol.oidc.par.endpoints;
import jakarta.ws.rs.core.MultivaluedMap;
import org.keycloak.events.Details;
import org.keycloak.http.HttpRequest;
import org.keycloak.OAuthErrorException;
import org.keycloak.common.Profile;
@@ -26,8 +27,6 @@ import org.keycloak.events.EventType;
import org.keycloak.headers.SecurityHeadersProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.SingleUseObjectProvider;
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.protocol.oidc.endpoints.AuthorizationEndpointChecker;
import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest;
@@ -155,6 +154,10 @@ public class ParEndpoint extends AbstractParEndpoint {
try {
session.clientPolicy().triggerOnEvent(new PushedAuthorizationRequestContext(authorizationRequest, decodedFormParameters));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw throwErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
}

View File

@@ -266,6 +266,10 @@ public class SamlService extends AuthorizationEndpointBase {
try {
session.clientPolicy().triggerOnEvent(ctx);
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
logger.warnf("Error in client policies processing the request: %s - %s", cpe.getError(), cpe.getErrorDetail());
return error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REQUEST);
}

View File

@@ -26,6 +26,8 @@ import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.ClientInitialAccessModel;
@@ -122,6 +124,10 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
} catch (ModelDuplicateException e) {
throw new ErrorResponseException(ErrorCodes.INVALID_CLIENT_METADATA, "Client Identifier in use", Response.Status.BAD_REQUEST);
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
}
}
@@ -195,6 +201,10 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
session.getContext().setClient(client);
session.clientPolicy().triggerOnEvent(new DynamicClientUpdatedContext(session, client, auth.getJwt(), client.getRealm()));
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
}
ClientRegistrationPolicyManager.triggerAfterUpdate(context, registrationAuth, client);

View File

@@ -17,6 +17,9 @@
package org.keycloak.services.clientregistration;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
import org.keycloak.Config;
import org.keycloak.OAuthErrorException;
import org.keycloak.authentication.AuthenticationProcessor;
@@ -44,10 +47,6 @@ import org.keycloak.services.clientregistration.policy.RegistrationAuth;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.util.TokenUtil;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
@@ -158,8 +157,14 @@ public class ClientRegistrationAuth {
try {
session.clientPolicy().triggerOnEvent(new DynamicClientRegisterContext(context, jwt, realm));
ClientRegistrationPolicyManager.triggerBeforeRegister(context, registrationAuth);
} catch (ClientRegistrationPolicyException | ClientPolicyException crpe) {
} catch (ClientRegistrationPolicyException crpe) {
throw forbidden(crpe.getMessage());
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw forbidden(cpe.getMessage());
}
return registrationAuth;
@@ -205,8 +210,14 @@ public class ClientRegistrationAuth {
try {
session.clientPolicy().triggerOnEvent(new DynamicClientViewContext(session, client, jwt, realm));
ClientRegistrationPolicyManager.triggerBeforeView(session, provider, authType, client);
} catch (ClientRegistrationPolicyException | ClientPolicyException crpe) {
} catch (ClientRegistrationPolicyException crpe) {
throw forbidden(crpe.getMessage());
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw forbidden(cpe.getMessage());
}
} else {
throw unauthorized("Not authorized to view client. Not valid token or client credentials provided.");
@@ -224,8 +235,14 @@ public class ClientRegistrationAuth {
try {
session.clientPolicy().triggerOnEvent(new DynamicClientUpdateContext(context, client, jwt, realm));
ClientRegistrationPolicyManager.triggerBeforeUpdate(context, regAuth, client);
} catch (ClientRegistrationPolicyException | ClientPolicyException crpe) {
} catch (ClientRegistrationPolicyException crpe) {
throw forbidden(crpe.getMessage());
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw forbidden(cpe.getMessage());
}
return regAuth;
@@ -237,8 +254,14 @@ public class ClientRegistrationAuth {
try {
session.clientPolicy().triggerOnEvent(new DynamicClientUnregisterContext(session, client, jwt, realm));
ClientRegistrationPolicyManager.triggerBeforeRemove(session, provider, chainType, client);
} catch (ClientRegistrationPolicyException | ClientPolicyException crpe) {
} catch (ClientRegistrationPolicyException crpe) {
throw forbidden(crpe.getMessage());
} catch (ClientPolicyException cpe) {
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw forbidden(cpe.getMessage());
}
}

View File

@@ -561,7 +561,7 @@ public class AuthenticationManager {
private static Response frontchannelLogoutClientSession(KeycloakSession session, RealmModel realm,
AuthenticatedClientSessionModel clientSession, AuthenticationSessionModel logoutAuthSession,
UriInfo uriInfo, HttpHeaders headers) {
UriInfo uriInfo, HttpHeaders headers, EventBuilder event) {
UserSessionModel userSession = clientSession.getUserSession();
ClientModel client = clientSession.getClient();
@@ -578,6 +578,11 @@ public class AuthenticationManager {
try {
session.clientPolicy().triggerOnEvent(new LogoutRequestContext());
} catch (ClientPolicyException cpe) {
event.event(EventType.LOGOUT);
event.detail(Details.REASON, Details.CLIENT_POLICY_ERROR);
event.detail(Details.CLIENT_POLICY_ERROR, cpe.getError());
event.detail(Details.CLIENT_POLICY_ERROR_DETAIL, cpe.getErrorDetail());
event.error(cpe.getError());
throw new ErrorResponseException(cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
}
@@ -692,7 +697,7 @@ public class AuthenticationManager {
return finishBrowserLogout(session, realm, userSession, uriInfo, connection, headers);
}
private static Response browserLogoutAllClients(UserSessionModel userSession, KeycloakSession session, RealmModel realm, HttpHeaders headers, UriInfo uriInfo, AuthenticationSessionModel logoutAuthSession) {
private static Response browserLogoutAllClients(UserSessionModel userSession, KeycloakSession session, RealmModel realm, HttpHeaders headers, UriInfo uriInfo, AuthenticationSessionModel logoutAuthSession, EventBuilder event) {
Map<Boolean, List<AuthenticatedClientSessionModel>> acss = userSession.getAuthenticatedClientSessions().values().stream()
.filter(clientSession -> !Objects.equals(AuthenticationSessionModel.Action.LOGGED_OUT.name(), clientSession.getAction())
&& !Objects.equals(AuthenticationSessionModel.Action.LOGGING_OUT.name(), clientSession.getAction()))
@@ -704,7 +709,7 @@ public class AuthenticationManager {
final List<AuthenticatedClientSessionModel> redirectClients = acss.get(true) == null ? Collections.emptyList() : acss.get(true);
for (AuthenticatedClientSessionModel nextRedirectClient : redirectClients) {
Response response = frontchannelLogoutClientSession(session, realm, nextRedirectClient, logoutAuthSession, uriInfo, headers);
Response response = frontchannelLogoutClientSession(session, realm, nextRedirectClient, logoutAuthSession, uriInfo, headers, event);
if (response != null) {
return response;
}
@@ -716,8 +721,8 @@ public class AuthenticationManager {
public static Response finishBrowserLogout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
final AuthenticationSessionManager asm = new AuthenticationSessionManager(session);
AuthenticationSessionModel logoutAuthSession = createOrJoinLogoutSession(session, realm, asm, userSession, true, false);
Response response = browserLogoutAllClients(userSession, session, realm, headers, uriInfo, logoutAuthSession);
EventBuilder event = new EventBuilder(realm, session, connection);
Response response = browserLogoutAllClients(userSession, session, realm, headers, uriInfo, logoutAuthSession, event);
if (response != null) {
return response;
}
@@ -729,14 +734,12 @@ public class AuthenticationManager {
expireRememberMeCookie(session);
String method = userSession.getNote(KEYCLOAK_LOGOUT_PROTOCOL);
EventBuilder event = new EventBuilder(realm, session, connection);
LoginProtocol protocol = session.getProvider(LoginProtocol.class, method);
protocol.setRealm(realm)
.setHttpHeaders(headers)
.setUriInfo(uriInfo)
.setEventBuilder(event);
response = protocol.finishBrowserLogout(userSession, logoutAuthSession);
// It may be possible that there are some client sessions that are still in LOGGING_OUT state

View File

@@ -248,6 +248,14 @@ public class AssertEvents implements TestRule {
.session(isUUID());
}
public ExpectedEvent expectClientPolicyError(EventType eventType, String error, String reason, String clientPolicyError, String clientPolicyErrorDetail) {
return expect(eventType)
.error(error)
.detail(Details.REASON, reason)
.detail(Details.CLIENT_POLICY_ERROR, clientPolicyError)
.detail(Details.CLIENT_POLICY_ERROR_DETAIL, clientPolicyErrorDetail);
}
public ExpectedEvent expect(EventType event) {
return new ExpectedEvent()
.realm(defaultRealmId())

View File

@@ -1610,6 +1610,10 @@ public class CIBATest extends AbstractClientPoliciesTest {
assertThat(response.getStatusCode(), is(equalTo(400)));
assertThat(response.getError(), is(OAuthErrorException.INVALID_REQUEST));
assertThat(response.getErrorDescription(), is("Missing parameter in the signed authentication request: exp"));
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Missing parameter in the signed authentication request: exp").user((String) null)
.assertEvent();
useRequestUri = true;
bindingMessage = "Flughafen-Wien-Schwechat";
@@ -1623,6 +1627,10 @@ public class CIBATest extends AbstractClientPoliciesTest {
assertThat(response.getStatusCode(), is(equalTo(400)));
assertThat(response.getError(), is(OAuthErrorException.INVALID_REQUEST));
assertThat(response.getErrorDescription(), is("Missing parameter in the signed authentication request: nbf"));
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Missing parameter in the signed authentication request: nbf").user((String) null)
.assertEvent();
useRequestUri = false;
bindingMessage = "Stuttgart-Hauptbahnhof";
@@ -1637,6 +1645,10 @@ public class CIBATest extends AbstractClientPoliciesTest {
assertThat(response.getStatusCode(), is(equalTo(400)));
assertThat(response.getError(), is(OAuthErrorException.INVALID_REQUEST));
assertThat(response.getErrorDescription(), is("signed authentication request's available period is long"));
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"signed authentication request's available period is long").user((String) null)
.assertEvent();
useRequestUri = true;
bindingMessage = "Flughafen-Wien-Schwechat";
@@ -1652,6 +1664,10 @@ public class CIBATest extends AbstractClientPoliciesTest {
assertThat(response.getStatusCode(), is(equalTo(400)));
assertThat(response.getError(), is(OAuthErrorException.INVALID_REQUEST));
assertThat(response.getErrorDescription(), is("Missing parameter in the 'request' object: aud"));
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Missing parameter in the 'request' object: aud").user((String) null)
.assertEvent();
useRequestUri = false;
bindingMessage = "Stuttgart-Hauptbahnhof";
@@ -1667,6 +1683,10 @@ public class CIBATest extends AbstractClientPoliciesTest {
assertThat(response.getStatusCode(), is(equalTo(400)));
assertThat(response.getError(), is(OAuthErrorException.INVALID_REQUEST));
assertThat(response.getErrorDescription(), is("Invalid parameter in the 'request' object: aud"));
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Invalid parameter in the 'request' object: aud").user((String) null)
.assertEvent();
useRequestUri = true;
bindingMessage = "Flughafen-Wien-Schwechat";
@@ -1682,6 +1702,10 @@ public class CIBATest extends AbstractClientPoliciesTest {
assertThat(response.getStatusCode(), is(equalTo(400)));
assertThat(response.getError(), is(OAuthErrorException.INVALID_REQUEST));
assertThat(response.getErrorDescription(), is("Missing parameter in the 'request' object: iss"));
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Missing parameter in the 'request' object: iss").user((String) null)
.assertEvent();
useRequestUri = false;
bindingMessage = "Stuttgart-Hauptbahnhof";
@@ -1698,6 +1722,10 @@ public class CIBATest extends AbstractClientPoliciesTest {
assertThat(response.getStatusCode(), is(equalTo(400)));
assertThat(response.getError(), is(OAuthErrorException.INVALID_REQUEST));
assertThat(response.getErrorDescription(), is("Invalid parameter in the 'request' object: iss"));
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Invalid parameter in the 'request' object: iss").user((String) null)
.assertEvent();
useRequestUri = true;
bindingMessage = "Flughafen-Wien-Schwechat";
@@ -1716,6 +1744,10 @@ public class CIBATest extends AbstractClientPoliciesTest {
assertThat(response.getStatusCode(), is(equalTo(400)));
assertThat(response.getError(), is(OAuthErrorException.INVALID_REQUEST));
assertThat(response.getErrorDescription(), is("Missing parameter in the signed authentication request: iat"));
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Missing parameter in the signed authentication request: iat").user((String) null)
.assertEvent();
useRequestUri = false;
bindingMessage = "Stuttgart-Hauptbahnhof";
@@ -1734,6 +1766,10 @@ public class CIBATest extends AbstractClientPoliciesTest {
assertThat(response.getStatusCode(), is(equalTo(400)));
assertThat(response.getError(), is(OAuthErrorException.INVALID_REQUEST));
assertThat(response.getErrorDescription(), is("Missing parameter in the signed authentication request: jti"));
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Missing parameter in the signed authentication request: jti").user((String) null)
.assertEvent();
useRequestUri = true;
bindingMessage = "Brno-hlavni-nadrazif";

View File

@@ -176,7 +176,7 @@ public class OAuth2_1ConfidentialClientTest extends AbstractFAPITest {
setupPolicyOAuth2_1ConfidentialClientForAllClient();
oauth.redirectUri(validRedirectUri);
failLoginByNotFollowingPKCE(clientId);
failLoginByNotFollowingPKCEWithoutClientPolicyValidation(clientId);
}
@Test

View File

@@ -160,7 +160,7 @@ public class OAuth2_1PublicClientTest extends AbstractFAPITest {
oauth.redirectUri(validRedirectUri);
pkceGenerator = null;
failLoginByNotFollowingPKCE(clientId);
failLoginByNotFollowingPKCEWithoutClientPolicyValidation(clientId);
}
@Test

View File

@@ -1586,6 +1586,21 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
AuthorizationEndpointResponse response = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, response.getError());
assertEquals("Missing parameter: code_challenge_method", response.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Missing parameter: code_challenge_method").client(clientId).user((String) null)
.assertEvent();
}
protected void failLoginByNotFollowingPKCEWithoutClientPolicyValidation(String clientId) {
oauth.client(clientId, TEST_CLIENT_SECRET);
oauth.openLoginForm();
AuthorizationEndpointResponse response = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, response.getError());
assertEquals("Missing parameter: code_challenge_method", response.getErrorDescription());
events.expect(EventType.LOGIN_ERROR).error(OAuthErrorException.INVALID_REQUEST)
.detail(Details.REASON, "Missing parameter: code_challenge_method").client(clientId)
.user((String) null).assertEvent();
}
protected void failTokenRequestByNotFollowingPKCE(String clientId, String clientSecret) {
@@ -1612,6 +1627,9 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
AuthorizationEndpointResponse response = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, response.getError());
assertEquals(errorDescription, response.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST, errorDescription).client(clientId)
.user((String) null).assertEvent();
}
protected void failLoginWithoutNonce(String clientId) {

View File

@@ -291,6 +291,9 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
AuthorizationEndpointResponse authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, authorizationEndpointResponse.getError());
assertEquals("invalid response_type", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"invalid response_type").client(clientId).user((String) null).assertEvent();
oauth.responseType(OIDCResponseType.CODE + " " + OIDCResponseType.ID_TOKEN);
oauth.loginForm().nonce("vbwe566fsfffds").doLogin(TEST_USER_NAME, TEST_USER_PASSWORD);
@@ -421,6 +424,9 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
AuthorizationEndpointResponse authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, authorizationEndpointResponse.getError());
assertEquals("invalid response_type", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"invalid response_type").client(clientId).user((String) null).assertEvent();
oauth.responseType(OIDCResponseType.CODE + " " + OIDCResponseType.ID_TOKEN);
oauth.loginForm().nonce("LIVieviDie028f").doLogin(TEST_USER_NAME, TEST_USER_PASSWORD);
@@ -491,6 +497,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
AuthorizationEndpointResponse authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, authorizationEndpointResponse.getError());
assertEquals("Missing parameter: 'request' or 'request_uri'", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Missing parameter: 'request' or 'request_uri'").client(clientId).user((String) null)
.assertEvent();
// check whether request_uri is https scheme
// cannot test because existing AuthorizationEndpoint check and return error before executing client policy
@@ -509,6 +519,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, authorizationEndpointResponse.getError());
assertEquals("Invalid parameter. Parameters in 'request' object not matching with request parameters", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Invalid parameter. Parameters in 'request' object not matching with request parameters")
.client(clientId).user((String) null).assertEvent();
// check whether client_id exists in both query parameter and request object
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -518,6 +532,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, authorizationEndpointResponse.getError());
assertEquals("Invalid parameter. Parameters in 'request' object not matching with request parameters", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Invalid parameter. Parameters in 'request' object not matching with request parameters")
.client(clientId).user((String) null).assertEvent();
// check whether response_type exists in both query parameter and request object
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -527,6 +545,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, authorizationEndpointResponse.getError());
assertEquals("Invalid parameter. Parameters in 'request' object not matching with request parameters", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Invalid parameter. Parameters in 'request' object not matching with request parameters")
.client(clientId).user((String) null).assertEvent();
// Check scope required
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -538,6 +560,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, authorizationEndpointResponse.getError());
assertEquals("Parameter 'scope' missing in the request parameters or in 'request' object", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Parameter 'scope' missing in the request parameters or in 'request' object")
.client(clientId).user((String) null).assertEvent();
oauth.openid(true);
// check whether "exp" claim exists
@@ -548,6 +574,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST_OBJECT, authorizationEndpointResponse.getError());
assertEquals("Missing parameter in the 'request' object: exp", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
"Missing parameter in the 'request' object: exp").client(clientId).user((String) null)
.assertEvent();
// check whether request object not expired
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -557,6 +587,9 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST_OBJECT, authorizationEndpointResponse.getError());
assertEquals("Request Expired", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
"Request Expired").client(clientId).user((String) null).assertEvent();
// check whether "nbf" claim exists
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -566,6 +599,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST_OBJECT, authorizationEndpointResponse.getError());
assertEquals("Missing parameter in the 'request' object: nbf", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
"Missing parameter in the 'request' object: nbf").client(clientId).user((String) null)
.assertEvent();
// check whether request object not yet being processed
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -575,6 +612,9 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST_OBJECT, authorizationEndpointResponse.getError());
assertEquals("Request not yet being processed", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
"Request not yet being processed").client(clientId).user((String) null).assertEvent();
// nbf ahead within allowed clock skew
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -590,6 +630,9 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST_OBJECT, authorizationEndpointResponse.getError());
assertEquals("Request issued in the future", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
"Request issued in the future").client(clientId).user((String) null).assertEvent();
// iat ahead within allowed clock skew
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -605,6 +648,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST_OBJECT, authorizationEndpointResponse.getError());
assertEquals("Request's available period is long", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
"Request's available period is long").client(clientId).user((String) null)
.assertEvent();
// check whether "aud" claim exists
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -614,6 +661,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST_OBJECT, authorizationEndpointResponse.getError());
assertEquals("Missing parameter in the 'request' object: aud", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
"Missing parameter in the 'request' object: aud").client(clientId)
.user((String) null).assertEvent();
// check whether "aud" claim points to this keycloak as authz server
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -623,6 +674,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST_URI, authorizationEndpointResponse.getError());
assertEquals("Invalid parameter in the 'request' object: aud", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST_URI,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST_URI,
"Invalid parameter in the 'request' object: aud").client(clientId)
.user((String) null).assertEvent();
// confirm whether all parameters in query string are included in the request object, and have the same values
// argument "request" are parameters overridden by parameters in request object
@@ -633,6 +688,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, authorizationEndpointResponse.getError());
assertEquals("Invalid parameter. Parameters in 'request' object not matching with request parameters", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"Invalid parameter. Parameters in 'request' object not matching with request parameters")
.client(clientId).user((String) null).assertEvent();
// valid request object
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -656,6 +715,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST_OBJECT, authorizationEndpointResponse.getError());
assertEquals("Missing parameter in the 'request' object: nbf", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
"Missing parameter in the 'request' object: nbf").client(clientId)
.user((String) null).assertEvent();
// check whether request object not yet being processed
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -665,6 +728,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST_OBJECT, authorizationEndpointResponse.getError());
assertEquals("Request not yet being processed", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
"Request not yet being processed").client(clientId).user((String) null)
.assertEvent();
// check whether request object's available period is short
requestObject = createValidRequestObjectForSecureRequestObjectExecutor(clientId);
@@ -674,6 +741,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST_OBJECT, authorizationEndpointResponse.getError());
assertEquals("Request's available period is long", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
"Request's available period is long").client(clientId).user((String) null)
.assertEvent();
// update profile : not check "nbf"
json = (new ClientProfilesBuilder()).addProfile(
@@ -716,6 +787,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST_OBJECT, authorizationEndpointResponse.getError());
assertEquals("Request object not encrypted", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST_OBJECT,
"Request object not encrypted").client(clientId).user((String) null)
.assertEvent();
}
@Test
@@ -1495,6 +1570,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
oauth.loginForm().state("randomstatesomething").requestUri(requestUri).open();
assertTrue(errorPage.isCurrent());
assertEquals("PAR request did not include necessary parameters", errorPage.getError());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"PAR request did not include necessary parameters").client((String) null)
.user((String) null).assertEvent();
oauth.client(clientBetaId, "secretBeta");
pResp = oauth.doPushedAuthorizationRequest();
@@ -1540,6 +1619,10 @@ public class ClientPoliciesExecutorTest extends AbstractClientPoliciesTest {
oauth.loginForm().requestUri(requestUri).state("mystate2").open();
assertTrue(errorPage.isCurrent());
assertEquals("PAR request did not include necessary parameters", errorPage.getError());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"PAR request did not include necessary parameters").client((String) null)
.user((String) null).assertEvent();
// Pushed Authorization Request with state parameter
oidcClientEndpointsResource.setOIDCRequest(REALM_NAME, TEST_CLIENT, oauth.getRedirectUri(), "10", "mystate2", "none");

View File

@@ -1026,6 +1026,10 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
oauth.openLoginForm();
assertTrue(errorPage.isCurrent());
assertEquals(ERR_MSG_REQ_NOT_ALLOWED, errorPage.getError());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
ERR_MSG_REQ_NOT_ALLOWED).client((String) null).user((String) null).assertEvent();
revertToBuiltinProfiles();
successfulLoginAndLogout(clientBetaId, "secretBeta");
} catch (Exception e) {
@@ -1216,6 +1220,10 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
AuthorizationEndpointResponse authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, authorizationEndpointResponse.getError());
assertEquals("The intent is not bound with the client", authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"The intent is not bound with the client").client(clientId).user((String) null)
.assertEvent();
// register a binding of an intent with a valid client
r = testingClient.testApp().oidcClientEndpoints().bindIntentWithClient(intentId, clientId);
@@ -1268,6 +1276,10 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
authorizationEndpointResponse = oauth.parseLoginResponse();
assertEquals(OAuthErrorException.INVALID_REQUEST, authorizationEndpointResponse.getError());
assertEquals("no claim for an intent value for ID token" , authorizationEndpointResponse.getErrorDescription());
events.expectClientPolicyError(EventType.LOGIN_ERROR, OAuthErrorException.INVALID_REQUEST,
Details.CLIENT_POLICY_ERROR, OAuthErrorException.INVALID_REQUEST,
"no claim for an intent value for ID token").client(clientId)
.user((String) null).assertEvent();
}
@Test