[KEYCLOAK-16455][Adapter - JavaScript] Propagate 3rd party cookies check
errors outside of JS adapter
This commit is contained in:
parent
ba8d27121c
commit
17b374f53a
5 changed files with 102 additions and 23 deletions
|
@ -178,6 +178,14 @@ declare namespace Keycloak {
|
|||
* @default false
|
||||
*/
|
||||
enableLogging?: boolean
|
||||
|
||||
/**
|
||||
* Configures how long will Keycloak adapter wait for receiving messages from server in ms. This is used,
|
||||
* for example, when waiting for response of 3rd party cookies check.
|
||||
*
|
||||
* @default 10000
|
||||
*/
|
||||
messageReceiveTimeout?: number
|
||||
}
|
||||
|
||||
interface KeycloakLoginOptions {
|
||||
|
|
|
@ -195,6 +195,12 @@
|
|||
if (typeof initOptions.scope === 'string') {
|
||||
kc.scope = initOptions.scope;
|
||||
}
|
||||
|
||||
if (typeof initOptions.messageReceiveTimeout === 'number' && initOptions.messageReceiveTimeout > 0) {
|
||||
kc.messageReceiveTimeout = initOptions.messageReceiveTimeout;
|
||||
} else {
|
||||
kc.messageReceiveTimeout = 10000;
|
||||
}
|
||||
}
|
||||
|
||||
if (!kc.responseMode) {
|
||||
|
@ -211,8 +217,8 @@
|
|||
initPromise.promise.then(function() {
|
||||
kc.onReady && kc.onReady(kc.authenticated);
|
||||
promise.setSuccess(kc.authenticated);
|
||||
}).catch(function(errorData) {
|
||||
promise.setError(errorData);
|
||||
}).catch(function(error) {
|
||||
promise.setError(error);
|
||||
});
|
||||
|
||||
var configPromise = loadConfig(config);
|
||||
|
@ -225,8 +231,8 @@
|
|||
|
||||
kc.login(options).then(function () {
|
||||
initPromise.setSuccess();
|
||||
}).catch(function () {
|
||||
initPromise.setError();
|
||||
}).catch(function (error) {
|
||||
initPromise.setError(error);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -264,8 +270,8 @@
|
|||
} else {
|
||||
initPromise.setSuccess();
|
||||
}
|
||||
}).catch(function () {
|
||||
initPromise.setError();
|
||||
}).catch(function (error) {
|
||||
initPromise.setError(error);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
|
@ -290,8 +296,8 @@
|
|||
if (callback && callback.valid) {
|
||||
return setupCheckLoginIframe().then(function() {
|
||||
processCallback(callback, initPromise);
|
||||
}).catch(function (e) {
|
||||
initPromise.setError();
|
||||
}).catch(function (error) {
|
||||
initPromise.setError(error);
|
||||
});
|
||||
} else if (initOptions) {
|
||||
if (initOptions.token && initOptions.refreshToken) {
|
||||
|
@ -307,20 +313,20 @@
|
|||
} else {
|
||||
initPromise.setSuccess();
|
||||
}
|
||||
}).catch(function () {
|
||||
initPromise.setError();
|
||||
}).catch(function (error) {
|
||||
initPromise.setError(error);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
kc.updateToken(-1).then(function() {
|
||||
kc.onAuthSuccess && kc.onAuthSuccess();
|
||||
initPromise.setSuccess();
|
||||
}).catch(function() {
|
||||
}).catch(function(error) {
|
||||
kc.onAuthError && kc.onAuthError();
|
||||
if (initOptions.onLoad) {
|
||||
onLoad();
|
||||
} else {
|
||||
initPromise.setError();
|
||||
initPromise.setError(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -351,13 +357,15 @@
|
|||
}
|
||||
|
||||
configPromise.then(function () {
|
||||
domReady().then(check3pCookiesSupported).then(processInit)
|
||||
.catch(function() {
|
||||
promise.setError();
|
||||
});
|
||||
domReady()
|
||||
.then(check3pCookiesSupported)
|
||||
.then(processInit)
|
||||
.catch(function (error) {
|
||||
promise.setError(error);
|
||||
});
|
||||
});
|
||||
configPromise.catch(function() {
|
||||
promise.setError();
|
||||
configPromise.catch(function (error) {
|
||||
promise.setError(error);
|
||||
});
|
||||
|
||||
return promise.promise;
|
||||
|
@ -694,8 +702,8 @@
|
|||
var iframePromise = checkLoginIframe();
|
||||
iframePromise.then(function() {
|
||||
exec();
|
||||
}).catch(function() {
|
||||
promise.setError();
|
||||
}).catch(function(error) {
|
||||
promise.setError(error);
|
||||
});
|
||||
} else {
|
||||
exec();
|
||||
|
@ -1206,6 +1214,19 @@
|
|||
return p;
|
||||
}
|
||||
|
||||
// Function to extend existing native Promise with timeout
|
||||
function applyTimeoutToPromise(promise, timeout, errorMessage) {
|
||||
var timeoutHandle = null;
|
||||
var timeoutPromise = new Promise(function (resolve, reject) {
|
||||
timeoutHandle = setTimeout(function () {
|
||||
reject({ "error": errorMessage || "Promise is not settled within timeout of " + timeout + "ms" });
|
||||
}, timeout);
|
||||
});
|
||||
|
||||
return Promise.race([promise, timeoutPromise]).finally(function () {
|
||||
clearTimeout(timeoutHandle);
|
||||
});
|
||||
}
|
||||
|
||||
function setupCheckLoginIframe() {
|
||||
var promise = createPromise();
|
||||
|
@ -1337,7 +1358,7 @@
|
|||
promise.setSuccess();
|
||||
}
|
||||
|
||||
return promise.promise;
|
||||
return applyTimeoutToPromise(promise.promise, kc.messageReceiveTimeout, "Timeout when waiting for 3rd party check iframe message.");
|
||||
}
|
||||
|
||||
function loadAdapter(type) {
|
||||
|
|
|
@ -183,8 +183,8 @@ public class JavascriptTestExecutor {
|
|||
String script = "var callback = arguments[arguments.length - 1];" +
|
||||
" window.keycloak.init(" + arguments + ").then(function (authenticated) {" +
|
||||
" callback(\"Init Success (\" + (authenticated ? \"Authenticated\" : \"Not Authenticated\") + \")\");" +
|
||||
" }).catch(function () {" +
|
||||
" callback(\"Init Error\");" +
|
||||
" }).catch(function (error) {" +
|
||||
" callback(error);" +
|
||||
" });";
|
||||
|
||||
Object output;
|
||||
|
|
|
@ -30,8 +30,11 @@ import org.openqa.selenium.WebElement;
|
|||
import org.openqa.selenium.support.FindBy;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.anyOf;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.collection.IsMapContaining.hasEntry;
|
||||
import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_HOST;
|
||||
import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_HOST2;
|
||||
|
@ -214,4 +217,15 @@ public abstract class AbstractJavascriptTest extends AbstractAuthTest {
|
|||
public JavascriptStateValidator assertEventsDoesntContain(String text) {
|
||||
return buildFunction(this::assertEventsWebElementDoesntContain, text);
|
||||
}
|
||||
|
||||
public void assertErrorResponse(String expectedError, WebDriver drv, Object output, WebElement evt) {
|
||||
Assert.assertNotNull("Empty error response", output);
|
||||
Assert.assertTrue("Invalid error response type", output instanceof Map);
|
||||
assertThat((Map<String, String>) output, anyOf(hasEntry("error", expectedError), hasEntry("error_description", expectedError)));
|
||||
}
|
||||
|
||||
public JavascriptStateValidator assertErrorResponse(String expectedError) {
|
||||
return buildFunction(this::assertErrorResponse, expectedError);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -822,6 +822,42 @@ public class JavascriptAdapterTest extends AbstractJavascriptTest {
|
|||
.init(defaultArguments(), this::assertInitNotAuth);
|
||||
}
|
||||
|
||||
// In case of incorrect/unavailable realm provided in KeycloakConfig,
|
||||
// JavaScript Adapter init() should fail-fast and reject Promise with KeycloakError.
|
||||
@Test
|
||||
public void checkInitWithInvalidRealm() {
|
||||
|
||||
JSObjectBuilder keycloakConfig = JSObjectBuilder.create()
|
||||
.add("url", authServerContextRootPage + "/auth")
|
||||
.add("realm", "invalid-realm-name")
|
||||
.add("clientId", CLIENT_ID);
|
||||
|
||||
JSObjectBuilder initOptions = defaultArguments();
|
||||
|
||||
testExecutor
|
||||
.configure(keycloakConfig)
|
||||
.init(initOptions, assertErrorResponse("Timeout when waiting for 3rd party check iframe message."));
|
||||
|
||||
}
|
||||
|
||||
// In case of unavailable Authorization Server due to network or other kind of problems,
|
||||
// JavaScript Adapter init() should fail-fast and reject Promise with KeycloakError.
|
||||
@Test
|
||||
public void checkInitWithUnavailableAuthServer() {
|
||||
|
||||
JSObjectBuilder keycloakConfig = JSObjectBuilder.create()
|
||||
.add("url", "https://localhost:12345/auth")
|
||||
.add("realm", REALM_NAME)
|
||||
.add("clientId", CLIENT_ID);
|
||||
|
||||
JSObjectBuilder initOptions = defaultArguments();
|
||||
|
||||
testExecutor
|
||||
.configure(keycloakConfig)
|
||||
.init(initOptions, assertErrorResponse("Timeout when waiting for 3rd party check iframe message."));
|
||||
|
||||
}
|
||||
|
||||
protected void assertAdapterIsLoggedIn(WebDriver driver1, Object output, WebElement events) {
|
||||
assertTrue(testExecutor.isLoggedIn());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue