diff --git a/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java b/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java
index e259738..b4e037b 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<String, String> notes = new HashMap<>();
+ @Deprecated // Backwards compatibility
+ @JsonProperty("cs")
+ protected String cs;
+
public Map<String, String> 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 533dfbb..7c61206 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> T fetch(FetchOnServer function, Class<T> 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 0000000..6135c56
--- /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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+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();
+ }
+}