mirror of
https://github.com/keycloak/keycloak.git
synced 2025-12-21 06:20:05 -06:00
Skip processing HEAD requests for action tokens
Closes #41834 Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
@@ -30,6 +30,8 @@ import jakarta.ws.rs.core.MultivaluedMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static jakarta.ws.rs.HttpMethod.HEAD;
|
||||
import static jakarta.ws.rs.HttpMethod.OPTIONS;
|
||||
import static org.keycloak.models.BrowserSecurityHeaders.CONTENT_SECURITY_POLICY;
|
||||
|
||||
public class DefaultSecurityHeadersProvider implements SecurityHeadersProvider {
|
||||
@@ -147,10 +149,17 @@ public class DefaultSecurityHeadersProvider implements SecurityHeadersProvider {
|
||||
status == 400 || status == 401 || status == 403 || status == 404) {
|
||||
return true;
|
||||
}
|
||||
if (requestContext.getMethod().equalsIgnoreCase("OPTIONS")) {
|
||||
return true;
|
||||
|
||||
String method = requestContext.getMethod().toUpperCase();
|
||||
|
||||
switch (method) {
|
||||
case OPTIONS:
|
||||
return true;
|
||||
case HEAD:
|
||||
return status == 200;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package org.keycloak.services.resources;
|
||||
|
||||
import jakarta.ws.rs.HEAD;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.Profile.Feature;
|
||||
@@ -548,6 +549,19 @@ public class LoginActionsService {
|
||||
return handleActionToken(key, execution, clientId, tabId, clientData, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip processing {@link jakarta.ws.rs.HttpMethod#HEAD} requests for action tokens
|
||||
* as they are usually used by mail servers to validate links. The actual request will eventually be
|
||||
* processed by the {@link #executeActionToken} method.
|
||||
*
|
||||
* @return a {@link Response.Status#OK} response with no message body
|
||||
*/
|
||||
@Path("action-token")
|
||||
@HEAD
|
||||
public Response executeActionTokenHead() {
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
protected <T extends JsonWebToken & SingleUseObjectKeyModel> Response handleActionToken(String tokenString, String execution, String clientId, String tabId, String clientData,
|
||||
TriFunction<ActionTokenHandler<T>, T, ActionTokenContext<T>, Response> preHandleToken) {
|
||||
T token;
|
||||
|
||||
@@ -30,8 +30,10 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import jakarta.ws.rs.core.Response.Status;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -39,6 +41,7 @@ import org.keycloak.admin.client.resource.AuthenticationManagementResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.authentication.actiontoken.updateemail.UpdateEmailActionToken;
|
||||
import org.keycloak.authentication.requiredactions.UpdateEmail;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp.Response;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.UserModel;
|
||||
@@ -48,6 +51,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.UserSessionRepresentation;
|
||||
import org.keycloak.testsuite.broker.util.SimpleHttpDefault;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.InfoPage;
|
||||
import org.keycloak.testsuite.util.GreenMailRule;
|
||||
@@ -174,6 +178,26 @@ public class RequiredActionUpdateEmailTestWithVerificationTest extends AbstractR
|
||||
assertTrue(ActionUtil.findUserWithAdminClient(adminClient, "test-user@localhost").getRequiredActions().contains(UserModel.RequiredAction.UPDATE_EMAIL.name()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipHeadRequestWhenFollowingVerificationLink() throws MessagingException, IOException {
|
||||
oauth.openLoginForm();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
||||
updateEmailPage.assertCurrent();
|
||||
updateEmailPage.changeEmail("new@localhost");
|
||||
|
||||
String confirmationLink = fetchEmailConfirmationLink("new@localhost");
|
||||
|
||||
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
|
||||
try (Response response = SimpleHttpDefault.doHead(confirmationLink, httpClient).asResponse()) {
|
||||
assertEquals(Status.OK.getStatusCode(), response.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
driver.navigate().to(confirmationLink);
|
||||
infoPage.assertCurrent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceEmailVerification() throws MessagingException, IOException {
|
||||
// disables verify email at the realm level
|
||||
|
||||
Reference in New Issue
Block a user