diff --git a/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java b/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java index e259738de6..b4e037b337 100644 --- a/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java +++ b/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java @@ -64,6 +64,10 @@ public class RestartLoginCookie { @JsonProperty("notes") protected Map notes = new HashMap<>(); + @Deprecated // Backwards compatibility + @JsonProperty("cs") + protected String cs; + public Map getNotes() { return notes; } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java index 533dfbb08f..7c61206b28 100755 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/KeycloakTestingClient.java @@ -90,14 +90,14 @@ public class KeycloakTestingClient { public T fetch(FetchOnServer function, Class clazz) throws RunOnServerException { try { - String s = fetch(function); + String s = fetchString(function); return JsonSerialization.readValue(s, clazz); } catch (Exception e) { throw new RuntimeException(e); } } - public String fetch(FetchOnServer function) throws RunOnServerException { + public String fetchString(FetchOnServer function) throws RunOnServerException { String encoded = SerializationUtil.encode(function); String result = testing(realm != null ? realm : "master").runOnServer(encoded); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RestartCookieTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RestartCookieTest.java new file mode 100644 index 0000000000..6135c5617d --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RestartCookieTest.java @@ -0,0 +1,128 @@ +/* + * Copyright 2017 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.forms; + +import java.io.IOException; + +import javax.mail.MessagingException; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.graphene.page.Page; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.events.Details; +import org.keycloak.events.Errors; +import org.keycloak.jose.jws.JWSBuilder; +import org.keycloak.models.KeyManager; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.protocol.RestartLoginCookie; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; +import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.AssertEvents; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.runonserver.RunOnServerDeployment; +import org.openqa.selenium.Cookie; + +/** + * @author Marek Posolda + */ +public class RestartCookieTest extends AbstractTestRealmKeycloakTest { + + + @Page + protected LoginPage loginPage; + + + @Rule + public AssertEvents events = new AssertEvents(this); + + + @Deployment + public static WebArchive deploy() { + return RunOnServerDeployment.create(UserResource.class) + .addPackages(true, "org.keycloak.testsuite"); + } + + + // KC_RESTART cookie from Keycloak 3.1.0 + private static final String OLD_RESTART_COOKIE_JSON = "{\n" + + " \"cs\": \"874a1ea8-5579-4f21-add0-903dd8e3ec1b\",\n" + + " \"cid\": \"test-app\",\n" + + " \"pty\": \"openid-connect\",\n" + + " \"ruri\": \"http://localhost:8081/auth/realms/master/app/auth\",\n" + + " \"act\": \"AUTHENTICATE\",\n" + + " \"notes\": {\n" + + " \"auth_type\": \"code\",\n" + + " \"scope\": \"openid\",\n" + + " \"iss\": \"http://localhost:8081/auth/realms/master/app/auth\",\n" + + " \"response_type\": \"code\",\n" + + " \"redirect_uri\": \"http://localhost:8081/auth/realms/master/app/auth/\",\n" + + " \"state\": \"6c983e5b-2dc1-411a-9ed1-0f51095949c5\",\n" + + " \"code_challenge_method\": \"plain\",\n" + + " \"nonce\": \"65639660-99b2-4cdf-bc9f-9978fdce5b03\",\n" + + " \"response_mode\": \"fragment\"\n" + + " }\n" + + "}"; + + @Override + public void configureTestRealm(RealmRepresentation testRealm) { + } + + + // KEYCLOAK-5440 + @Test + public void invalidLoginAndBackButton() throws IOException, MessagingException { + String oldRestartCookie = testingClient.server().fetchString((KeycloakSession session) -> { + try { + String cookieVal = OLD_RESTART_COOKIE_JSON.replace("\n", "").replace(" ", ""); + RealmModel realm = session.realms().getRealmByName("test"); + + KeyManager.ActiveHmacKey activeKey = session.keys().getActiveHmacKey(realm); + + String encodedToken = new JWSBuilder() + .kid(activeKey.getKid()) + .content(cookieVal.getBytes("UTF-8")) + .hmac256(activeKey.getSecretKey()); + + return encodedToken; + + + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + }); + + oauth.openLoginForm(); + + driver.manage().deleteAllCookies(); + driver.manage().addCookie(new Cookie(RestartLoginCookie.KC_RESTART, oldRestartCookie)); + + loginPage.login("foo", "bar"); + loginPage.assertCurrent(); + Assert.assertEquals("You took too long to login. Login process starting from beginning.", loginPage.getError()); + + events.expectLogin().user((String) null).session((String) null).error(Errors.EXPIRED_CODE).clearDetails() + .detail(Details.RESTART_AFTER_TIMEOUT, "true") + .client((String) null) + .assertEvent(); + } +}