[KEYCLOAK-11330] - Properly handling POST formdata and UriInfo
This commit is contained in:
parent
90b29b0e31
commit
e8dc10b4a1
7 changed files with 52 additions and 29 deletions
|
@ -28,6 +28,7 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
|
||||
import javax.ws.rs.HttpMethod;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.ArrayList;
|
||||
|
@ -97,38 +98,42 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
|||
throw new AuthenticationFlowException("Execution not found", AuthenticationFlowError.INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
MultivaluedMap<String, String> inputData = processor.getRequest().getDecodedFormParameters();
|
||||
String authExecId = inputData.getFirst(Constants.AUTHENTICATION_EXECUTION);
|
||||
if (HttpMethod.POST.equals(processor.getRequest().getHttpMethod())) {
|
||||
MultivaluedMap<String, String> inputData = processor.getRequest().getDecodedFormParameters();
|
||||
String authExecId = inputData.getFirst(Constants.AUTHENTICATION_EXECUTION);
|
||||
|
||||
// User clicked on "try another way" link
|
||||
if (inputData.containsKey("tryAnotherWay")) {
|
||||
logger.trace("User clicked on link 'Try Another Way'");
|
||||
// User clicked on "try another way" link
|
||||
if (inputData.containsKey("tryAnotherWay")) {
|
||||
logger.trace("User clicked on link 'Try Another Way'");
|
||||
|
||||
List<AuthenticationSelectionOption> selectionOptions = createAuthenticationSelectionList(model);
|
||||
List<AuthenticationSelectionOption> selectionOptions = createAuthenticationSelectionList(model);
|
||||
|
||||
AuthenticationProcessor.Result result = processor.createAuthenticatorContext(model, null, null);
|
||||
result.setAuthenticationSelections(selectionOptions);
|
||||
return result.form().createSelectAuthenticator();
|
||||
}
|
||||
AuthenticationProcessor.Result result = processor.createAuthenticatorContext(model, null, null);
|
||||
result.setAuthenticationSelections(selectionOptions);
|
||||
return result.form().createSelectAuthenticator();
|
||||
}
|
||||
|
||||
// check if the user has switched to a new authentication execution, and if so switch to it.
|
||||
if (authExecId != null && !authExecId.isEmpty()) {
|
||||
// check if the user has switched to a new authentication execution, and if so switch to it.
|
||||
if (authExecId != null && !authExecId.isEmpty()) {
|
||||
|
||||
List<AuthenticationSelectionOption> selectionOptions = createAuthenticationSelectionList(model);
|
||||
List<AuthenticationSelectionOption> selectionOptions = createAuthenticationSelectionList(model);
|
||||
|
||||
// Check if switch to the requested authentication execution is allowed
|
||||
selectionOptions.stream()
|
||||
.filter(authSelectionOption -> authExecId.equals(authSelectionOption.getAuthExecId()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new AuthenticationFlowException("Requested authentication execution is not allowed", AuthenticationFlowError.INTERNAL_ERROR)
|
||||
);
|
||||
// Check if switch to the requested authentication execution is allowed
|
||||
selectionOptions.stream()
|
||||
.filter(authSelectionOption -> authExecId.equals(authSelectionOption.getAuthExecId()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new AuthenticationFlowException("Requested authentication execution is not allowed",
|
||||
AuthenticationFlowError.INTERNAL_ERROR)
|
||||
);
|
||||
|
||||
model = processor.getRealm().getAuthenticationExecutionById(authExecId);
|
||||
model = processor.getRealm().getAuthenticationExecutionById(authExecId);
|
||||
|
||||
Response response = processSingleFlowExecutionModel(model, false);
|
||||
if (response == null) {
|
||||
return continueAuthenticationAfterSuccessfulAction(model);
|
||||
} else return response;
|
||||
Response response = processSingleFlowExecutionModel(model, false);
|
||||
if (response == null) {
|
||||
return continueAuthenticationAfterSuccessfulAction(model);
|
||||
} else
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
//handle case where execution is a flow - This can happen during user registration for example
|
||||
|
|
|
@ -53,7 +53,7 @@ public class X509ClientAuthenticator extends AbstractClientAuthenticator {
|
|||
boolean hasFormData = mediaType != null && mediaType.isCompatible(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
|
||||
|
||||
MultivaluedMap<String, String> formData = hasFormData ? context.getHttpRequest().getDecodedFormParameters() : null;
|
||||
MultivaluedMap<String, String> queryParams = context.getHttpRequest().getUri().getQueryParameters();
|
||||
MultivaluedMap<String, String> queryParams = context.getSession().getContext().getUri().getQueryParameters();
|
||||
|
||||
if (formData != null) {
|
||||
client_id = formData.getFirst(OAuth2Constants.CLIENT_ID);
|
||||
|
|
|
@ -91,12 +91,14 @@ import org.keycloak.sessions.RootAuthenticationSessionModel;
|
|||
import org.keycloak.util.TokenUtil;
|
||||
import org.keycloak.utils.ProfileHelper;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.OPTIONS;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedHashMap;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
@ -160,11 +162,18 @@ public class TokenEndpoint {
|
|||
this.event = event;
|
||||
}
|
||||
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@POST
|
||||
public Response processGrantRequest() {
|
||||
cors = Cors.add(request).auth().allowedMethods("POST").auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
|
||||
formParams = request.getDecodedFormParameters();
|
||||
MultivaluedMap<String, String> formParameters = request.getDecodedFormParameters();
|
||||
|
||||
if (formParameters == null) {
|
||||
formParameters = new MultivaluedHashMap<>();
|
||||
}
|
||||
|
||||
formParams = formParameters;
|
||||
grantType = formParams.getFirst(OIDCLoginProtocol.GRANT_TYPE_PARAM);
|
||||
|
||||
// https://tools.ietf.org/html/rfc6749#section-5.1
|
||||
|
|
|
@ -35,6 +35,7 @@ import javax.ws.rs.NotAuthorizedException;
|
|||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -61,10 +62,11 @@ public class AccountLoader {
|
|||
|
||||
Theme theme = getTheme(session);
|
||||
boolean deprecatedAccount = isDeprecatedFormsAccountConsole(theme);
|
||||
UriInfo uriInfo = session.getContext().getUri();
|
||||
|
||||
if (request.getHttpMethod().equals(HttpMethod.OPTIONS)) {
|
||||
return new CorsPreflightService(request);
|
||||
} else if ((accepts.contains(MediaType.APPLICATION_JSON_TYPE) || MediaType.APPLICATION_JSON_TYPE.equals(content)) && !request.getUri().getPath().endsWith("keycloak.json")) {
|
||||
} else if ((accepts.contains(MediaType.APPLICATION_JSON_TYPE) || MediaType.APPLICATION_JSON_TYPE.equals(content)) && !uriInfo.getPath().endsWith("keycloak.json")) {
|
||||
AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(session);
|
||||
if (authResult == null) {
|
||||
throw new NotAuthorizedException("Bearer token required");
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.keycloak.authorization.store.ResourceStore;
|
|||
import org.keycloak.authorization.store.ScopeStore;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakUriInfo;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
@ -53,6 +54,7 @@ public abstract class AbstractResourceService {
|
|||
protected final PermissionTicketStore ticketStore;
|
||||
protected final ResourceStore resourceStore;
|
||||
protected final ScopeStore scopeStore;
|
||||
protected final KeycloakUriInfo uriInfo;
|
||||
protected HttpRequest request;
|
||||
protected Auth auth;
|
||||
|
||||
|
@ -64,6 +66,7 @@ public abstract class AbstractResourceService {
|
|||
ticketStore = provider.getStoreFactory().getPermissionTicketStore();
|
||||
resourceStore = provider.getStoreFactory().getResourceStore();
|
||||
scopeStore = provider.getStoreFactory().getScopeStore();
|
||||
uriInfo = session.getContext().getUri();
|
||||
}
|
||||
|
||||
protected Response cors(Response.ResponseBuilder response) {
|
||||
|
|
|
@ -219,14 +219,14 @@ public class ResourcesService extends AbstractResourceService {
|
|||
|
||||
if (nextPage) {
|
||||
links.add(Link.fromUri(
|
||||
KeycloakUriBuilder.fromUri(request.getUri().getRequestUri()).replaceQuery("first={first}&max={max}")
|
||||
KeycloakUriBuilder.fromUri(uriInfo.getRequestUri()).replaceQuery("first={first}&max={max}")
|
||||
.build(first + max, max))
|
||||
.rel("next").build());
|
||||
}
|
||||
|
||||
if (first > 0) {
|
||||
links.add(Link.fromUri(
|
||||
KeycloakUriBuilder.fromUri(request.getUri().getRequestUri()).replaceQuery("first={first}&max={max}")
|
||||
KeycloakUriBuilder.fromUri(uriInfo.getRequestUri()).replaceQuery("first={first}&max={max}")
|
||||
.build(Math.max(first - max, 0), max))
|
||||
.rel("prev").build());
|
||||
}
|
||||
|
|
|
@ -65,6 +65,9 @@ import java.util.List;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
|
@ -610,6 +613,7 @@ public class ResourceOwnerPasswordCredentialsGrantTest extends AbstractKeycloakT
|
|||
|
||||
try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
|
||||
HttpPost post = new HttpPost(oauth.getResourceOwnerPasswordCredentialGrantUrl());
|
||||
post.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED);
|
||||
OAuthClient.AccessTokenResponse response = new OAuthClient.AccessTokenResponse(client.execute(post));
|
||||
|
||||
assertEquals(400, response.getStatusCode());
|
||||
|
|
Loading…
Reference in a new issue