KEYCLOAK-10734 Let the check-sso feature do the check in hidden iframe
This commit is contained in:
parent
b3004482fb
commit
49e9cd759b
6 changed files with 103 additions and 4 deletions
|
@ -106,6 +106,13 @@ declare namespace Keycloak {
|
||||||
*/
|
*/
|
||||||
redirectUri?: string;
|
redirectUri?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies an uri to redirect to after silent check-sso.
|
||||||
|
* Silent check-sso will only happen, when this redirect uri is given and
|
||||||
|
* the specified uri is available whithin the application.
|
||||||
|
*/
|
||||||
|
silentCheckSsoRedirectUri?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the OpenID Connect flow.
|
* Set the OpenID Connect flow.
|
||||||
* @default standard
|
* @default standard
|
||||||
|
|
|
@ -116,6 +116,10 @@
|
||||||
kc.redirectUri = initOptions.redirectUri;
|
kc.redirectUri = initOptions.redirectUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (initOptions.silentCheckSsoRedirectUri) {
|
||||||
|
kc.silentCheckSsoRedirectUri = initOptions.silentCheckSsoRedirectUri;
|
||||||
|
}
|
||||||
|
|
||||||
if (initOptions.pkceMethod) {
|
if (initOptions.pkceMethod) {
|
||||||
if (initOptions.pkceMethod !== "S256") {
|
if (initOptions.pkceMethod !== "S256") {
|
||||||
throw 'Invalid value for pkceMethod';
|
throw 'Invalid value for pkceMethod';
|
||||||
|
@ -157,6 +161,29 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var checkSsoSilently = function() {
|
||||||
|
var ifrm = document.createElement("iframe");
|
||||||
|
var src = kc.createLoginUrl({prompt: 'none', redirectUri: kc.silentCheckSsoRedirectUri});
|
||||||
|
ifrm.setAttribute("src", src);
|
||||||
|
ifrm.setAttribute("title", "keycloak-silent-check-sso");
|
||||||
|
ifrm.style.display = "none";
|
||||||
|
document.body.appendChild(ifrm);
|
||||||
|
|
||||||
|
var messageCallback = function(event) {
|
||||||
|
if (event.origin !== window.location.origin || ifrm.contentWindow !== event.source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var oauth = parseCallback(event.data);
|
||||||
|
processCallback(oauth, initPromise);
|
||||||
|
|
||||||
|
document.body.removeChild(ifrm);
|
||||||
|
window.removeEventListener("message", messageCallback);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("message", messageCallback);
|
||||||
|
};
|
||||||
|
|
||||||
var options = {};
|
var options = {};
|
||||||
switch (initOptions.onLoad) {
|
switch (initOptions.onLoad) {
|
||||||
case 'check-sso':
|
case 'check-sso':
|
||||||
|
@ -164,7 +191,7 @@
|
||||||
setupCheckLoginIframe().success(function() {
|
setupCheckLoginIframe().success(function() {
|
||||||
checkLoginIframe().success(function (unchanged) {
|
checkLoginIframe().success(function (unchanged) {
|
||||||
if (!unchanged) {
|
if (!unchanged) {
|
||||||
doLogin(false);
|
kc.silentCheckSsoRedirectUri ? checkSsoSilently() : doLogin(false);
|
||||||
} else {
|
} else {
|
||||||
initPromise.setSuccess();
|
initPromise.setSuccess();
|
||||||
}
|
}
|
||||||
|
@ -173,7 +200,7 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
doLogin(false);
|
kc.silentCheckSsoRedirectUri ? checkSsoSilently() : doLogin(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'login-required':
|
case 'login-required':
|
||||||
|
|
|
@ -30,6 +30,13 @@ public class TestJavascriptResource {
|
||||||
return resourceToString("/javascript/index.html");
|
return resourceToString("/javascript/index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/silent-check-sso.html")
|
||||||
|
@Produces(MediaType.TEXT_HTML)
|
||||||
|
public String getJavascriptTestingEnvironmentSilentCheckSso() throws IOException {
|
||||||
|
return resourceToString("/javascript/silent-check-sso.html");
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/keycloak.json")
|
@Path("/keycloak.json")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<html><body><script>parent.postMessage(location.href, location.origin)</script></body></html>
|
|
@ -78,6 +78,10 @@ public class JSObjectBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean skipQuotes(Object o) {
|
||||||
|
return (o instanceof Integer || o instanceof Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
public String build() {
|
public String build() {
|
||||||
StringBuilder argument = new StringBuilder("{");
|
StringBuilder argument = new StringBuilder("{");
|
||||||
String comma = "";
|
String comma = "";
|
||||||
|
@ -86,11 +90,11 @@ public class JSObjectBuilder {
|
||||||
.append(option.getKey())
|
.append(option.getKey())
|
||||||
.append(" : ");
|
.append(" : ");
|
||||||
|
|
||||||
if (!(option.getValue() instanceof Integer)) argument.append("\"");
|
if (!skipQuotes(option.getValue())) argument.append("\"");
|
||||||
|
|
||||||
argument.append(option.getValue());
|
argument.append(option.getValue());
|
||||||
|
|
||||||
if (!(option.getValue() instanceof Integer)) argument.append("\"");
|
if (!skipQuotes(option.getValue())) argument.append("\"");
|
||||||
comma = ",";
|
comma = ",";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ import static org.hamcrest.collection.IsMapContaining.hasEntry;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
|
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
|
||||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
|
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
|
||||||
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
|
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
|
||||||
|
@ -153,6 +154,58 @@ public class JavascriptAdapterTest extends AbstractJavascriptTest {
|
||||||
.init(pkceS256, this::assertInitNotAuth);
|
.init(pkceS256, this::assertInitNotAuth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSilentCheckSso() {
|
||||||
|
JSObjectBuilder checkSSO = defaultArguments().checkSSOOnLoad();
|
||||||
|
testExecutor.init(checkSSO, this::assertInitNotAuth)
|
||||||
|
.login(this::assertOnLoginPage)
|
||||||
|
.loginForm(testUser, this::assertOnTestAppUrl)
|
||||||
|
.init(checkSSO, this::assertSuccessfullyLoggedIn)
|
||||||
|
.refresh()
|
||||||
|
.init(checkSSO
|
||||||
|
.add("silentCheckSsoRedirectUri", authServerContextRootPage + JAVASCRIPT_URL + "/silent-check-sso.html")
|
||||||
|
, this::assertSuccessfullyLoggedIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSilentCheckSsoLoginWithLoginIframeDisabled() {
|
||||||
|
JSObjectBuilder checkSSO = defaultArguments().checkSSOOnLoad();
|
||||||
|
testExecutor.init(checkSSO, this::assertInitNotAuth)
|
||||||
|
.login(this::assertOnLoginPage)
|
||||||
|
.loginForm(testUser, this::assertOnTestAppUrl)
|
||||||
|
.init(checkSSO, this::assertSuccessfullyLoggedIn)
|
||||||
|
.refresh()
|
||||||
|
.init(checkSSO
|
||||||
|
.add("checkLoginIframe", false)
|
||||||
|
.add("silentCheckSsoRedirectUri", authServerContextRootPage + JAVASCRIPT_URL + "/silent-check-sso.html")
|
||||||
|
, this::assertSuccessfullyLoggedIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSilentCheckSsoWithoutRedirectUri() {
|
||||||
|
JSObjectBuilder checkSSO = defaultArguments().checkSSOOnLoad();
|
||||||
|
try {
|
||||||
|
testExecutor.init(checkSSO, this::assertInitNotAuth)
|
||||||
|
.login(this::assertOnLoginPage)
|
||||||
|
.loginForm(testUser, this::assertOnTestAppUrl)
|
||||||
|
.init(checkSSO, this::assertSuccessfullyLoggedIn)
|
||||||
|
.refresh()
|
||||||
|
.init(checkSSO);
|
||||||
|
fail();
|
||||||
|
} catch (WebDriverException e) {
|
||||||
|
// should happen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSilentCheckSsoNotAuthenticated() {
|
||||||
|
JSObjectBuilder checkSSO = defaultArguments().checkSSOOnLoad();
|
||||||
|
testExecutor.init(checkSSO
|
||||||
|
.add("checkLoginIframe", false)
|
||||||
|
.add("silentCheckSsoRedirectUri", authServerContextRootPage + JAVASCRIPT_URL + "/silent-check-sso.html")
|
||||||
|
, this::assertInitNotAuth);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRefreshToken() {
|
public void testRefreshToken() {
|
||||||
testExecutor.init(defaultArguments(), this::assertInitNotAuth)
|
testExecutor.init(defaultArguments(), this::assertInitNotAuth)
|
||||||
|
|
Loading…
Reference in a new issue