Skip to content
Open
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
2 changes: 2 additions & 0 deletions common/src/main/java/org/keycloak/common/Profile.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ public enum Feature {

FIPS("FIPS 140-2 mode", Type.DISABLED_BY_DEFAULT),

FAPI_V2("FAPI 2.0 Security Profile", Type.EXPERIMENTAL),

DPOP("OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer", Type.DEFAULT),

DEVICE_FLOW("OAuth 2.0 Device Authorization Grant", Type.DEFAULT),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ public Response sendError(ClientModel client, ClientData clientData, Error error
.session(session);
try {
checker.checkResponseType();
checker.checkResponseTypeExtended();
checker.checkRedirectUri();
} catch (AuthorizationEndpointChecker.AuthorizationCheckException ex) {
ex.throwAsErrorPageException(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ private Response process(final MultivaluedMap<String, String> params) {

try {
checker.checkResponseType();
checker.checkResponseTypeExtended();
this.parsedResponseType = checker.getParsedResponseType();
this.parsedResponseMode = checker.getParsedResponseMode();
} catch (AuthorizationEndpointChecker.AuthorizationCheckException ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@ public void checkResponseType() throws AuthorizationCheckException {
throw new AuthorizationCheckException(Response.Status.BAD_REQUEST, OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE, null);
}

OIDCResponseMode parsedResponseMode = null;
OIDCResponseMode responseMode;
try {
parsedResponseMode = OIDCResponseMode.parse(request.getResponseMode(), parsedResponseType);
responseMode = OIDCResponseMode.parse(request.getResponseMode(), parsedResponseType);
} catch (IllegalArgumentException iae) {
ServicesLogger.LOGGER.invalidParameter(OIDCLoginProtocol.RESPONSE_MODE_PARAM);
String errorMessage = "Invalid parameter: " + OIDCLoginProtocol.RESPONSE_MODE_PARAM;
Expand All @@ -174,18 +174,32 @@ public void checkResponseType() throws AuthorizationCheckException {
throw new AuthorizationCheckException(Response.Status.BAD_REQUEST, OAuthErrorException.INVALID_REQUEST, errorMessage);
}

event.detail(Details.RESPONSE_MODE, parsedResponseMode.toString().toLowerCase());
event.detail(Details.RESPONSE_MODE, responseMode.toString().toLowerCase());

// Disallowed by OIDC specs
if (parsedResponseType.isImplicitOrHybridFlow() && parsedResponseMode == OIDCResponseMode.QUERY) {
if (parsedResponseType.isImplicitOrHybridFlow() && responseMode == OIDCResponseMode.QUERY) {
ServicesLogger.LOGGER.responseModeQueryNotAllowed();
String errorMessage = "Response_mode 'query' not allowed for implicit or hybrid flow";
event.detail(Details.REASON, errorMessage);
event.error(Errors.INVALID_REQUEST);
throw new AuthorizationCheckException(Response.Status.BAD_REQUEST, OAuthErrorException.INVALID_REQUEST, errorMessage);
}

this.parsedResponseMode = parsedResponseMode;
this.parsedResponseMode = responseMode;
}

public void checkResponseTypeExtended() throws AuthorizationCheckException {

// Not allowed by FAPI2 Security Profile as it would return an id_token via the browser where it may be leaked.
// Only the authorization code flow ('response_type=code') is permitted.
// https://github.com/keycloak/keycloak/issues/48067
if (Profile.isFeatureEnabled(Profile.Feature.FAPI_V2) && !parsedResponseType.hasSingleResponseType(OIDCResponseType.CODE)) {
ServicesLogger.LOGGER.flowNotAllowed("Non Standard");
String errorMessage = "Non standard response type (i.e. other than 'code') not allowed by FAPI 2.0 Security Profile";
event.detail(Details.REASON, errorMessage);
event.error(Errors.NOT_ALLOWED);
throw new AuthorizationCheckException(Response.Status.BAD_REQUEST, OAuthErrorException.INVALID_REQUEST, errorMessage);
}

if (parsedResponseType.isImplicitOrHybridFlow() && parsedResponseMode == OIDCResponseMode.QUERY_JWT &&
(!StringUtil.isNotBlank(client.getAttribute(OIDCConfigAttributes.AUTHORIZATION_ENCRYPTED_RESPONSE_ALG)) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ public Response request() {

try {
checker.checkResponseType();
checker.checkResponseTypeExtended();
} catch (AuthorizationEndpointChecker.AuthorizationCheckException ex) {
if (ex.getError().equals(OAuthErrorException.UNSUPPORTED_RESPONSE_TYPE)) {
throw throwErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Unsupported response type", Response.Status.BAD_REQUEST);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ public boolean hasSingleResponseType(String responseType) {
return responseTypes.contains(responseType);
}


public boolean isImplicitOrHybridFlow() {
return hasResponseType(TOKEN) || hasResponseType(ID_TOKEN);
}
Expand Down
Loading