KEYCLOAK-13565 Add support for kc_action to keycloak.js
Co-authored-by mhajas <mhajas@redhat.com>
This commit is contained in:
parent
97b5654690
commit
1f02f87a6e
6 changed files with 70 additions and 6 deletions
|
@ -173,7 +173,7 @@ declare namespace Keycloak {
|
|||
* If value is `'register'` then user is redirected to registration page,
|
||||
* otherwise to login page.
|
||||
*/
|
||||
action?: 'register';
|
||||
action?: string;
|
||||
|
||||
/**
|
||||
* Used just if user is already authenticated. Specifies maximum time since
|
||||
|
@ -433,6 +433,11 @@ declare namespace Keycloak {
|
|||
*/
|
||||
onTokenExpired?(): void;
|
||||
|
||||
/**
|
||||
* Called when a AIA has been requested by the application.
|
||||
*/
|
||||
onActionUpdate?(status: 'success'|'cancelled'|'error'): void;
|
||||
|
||||
/**
|
||||
* Called to initialize the adapter.
|
||||
* @param initOptions Initialization options.
|
||||
|
|
|
@ -476,6 +476,10 @@
|
|||
url += '&kc_idp_hint=' + encodeURIComponent(options.idpHint);
|
||||
}
|
||||
|
||||
if (options && options.action && options.action != 'register') {
|
||||
url += '&kc_action=' + encodeURIComponent(options.action);
|
||||
}
|
||||
|
||||
if (options && options.locale) {
|
||||
url += '&ui_locales=' + encodeURIComponent(options.locale);
|
||||
}
|
||||
|
@ -740,6 +744,10 @@
|
|||
|
||||
var timeLocal = new Date().getTime();
|
||||
|
||||
if (oauth['kc_action_status']) {
|
||||
kc.onActionUpdate && kc.onActionUpdate(oauth['kc_action_status']);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
if (prompt != 'none') {
|
||||
var errorData = { error: error, error_description: oauth.error_description };
|
||||
|
@ -1085,13 +1093,13 @@
|
|||
var supportedParams;
|
||||
switch (kc.flow) {
|
||||
case 'standard':
|
||||
supportedParams = ['code', 'state', 'session_state'];
|
||||
supportedParams = ['code', 'state', 'session_state', 'kc_action_status'];
|
||||
break;
|
||||
case 'implicit':
|
||||
supportedParams = ['access_token', 'token_type', 'id_token', 'state', 'session_state', 'expires_in'];
|
||||
supportedParams = ['access_token', 'token_type', 'id_token', 'state', 'session_state', 'expires_in', 'kc_action_status'];
|
||||
break;
|
||||
case 'hybrid':
|
||||
supportedParams = ['access_token', 'id_token', 'code', 'state', 'session_state'];
|
||||
supportedParams = ['access_token', 'id_token', 'code', 'state', 'session_state', 'kc_action_status'];
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
<div>
|
||||
<button onclick="keycloak.login()">Login</button>
|
||||
<button onclick="keycloak.login({ action: 'UPDATE_PASSWORD' })">Update Password</button>
|
||||
<button onclick="keycloak.logout()">Logout</button>
|
||||
<button onclick="keycloak.register()">Register</button>
|
||||
<button onclick="keycloak.accountManagement()">Account</button>
|
||||
|
@ -155,6 +156,17 @@
|
|||
event('Access token expired.');
|
||||
};
|
||||
|
||||
keycloak.onActionUpdate = function (status) {
|
||||
switch (status) {
|
||||
case 'success':
|
||||
event('Action completed successfully'); break;
|
||||
case 'cancelled':
|
||||
event('Action cancelled by user'); break;
|
||||
case 'error':
|
||||
event('Action failed'); break;
|
||||
}
|
||||
};
|
||||
|
||||
// Flow can be changed to 'implicit' or 'hybrid', but then client must enable implicit flow in admin console too
|
||||
var initOptions = {
|
||||
responseMode: 'fragment',
|
||||
|
|
|
@ -114,6 +114,7 @@ public class JavascriptTestExecutor {
|
|||
jsExecutor.executeScript("window.keycloak.onAuthRefreshError = function () {event('Auth Refresh Error')}");
|
||||
jsExecutor.executeScript("window.keycloak.onAuthLogout = function () {event('Auth Logout')}");
|
||||
jsExecutor.executeScript("window.keycloak.onTokenExpired = function () {event('Access token expired.')}");
|
||||
jsExecutor.executeScript("window.keycloak.onActionUpdate = function (status) {event('AIA status: ' + status)}");
|
||||
|
||||
configured = true;
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ public abstract class AbstractJavascriptTest extends AbstractAuthTest {
|
|||
public static final String JAVASCRIPT_ENCODED_SPACE_URL = "/auth/realms/Example%20realm/testing/javascript";
|
||||
public static final String JAVASCRIPT_SPACE_URL = "/auth/realms/Example realm/testing/javascript";
|
||||
public static int TOKEN_LIFESPAN_LEEWAY = 3; // seconds
|
||||
public static final String USER_PASSWORD = "password";
|
||||
|
||||
|
||||
protected JavascriptExecutor jsExecutor;
|
||||
|
@ -80,8 +81,8 @@ public abstract class AbstractJavascriptTest extends AbstractAuthTest {
|
|||
public static final UserRepresentation unauthorizedUser;
|
||||
|
||||
static {
|
||||
testUser = UserBuilder.create().username("test-user@localhost").password("password").build();
|
||||
unauthorizedUser = UserBuilder.create().username("unauthorized").password("password").build();
|
||||
testUser = UserBuilder.create().username("test-user@localhost").password(USER_PASSWORD).build();
|
||||
unauthorizedUser = UserBuilder.create().username("unauthorized").password(USER_PASSWORD).build();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.keycloak.testsuite.ProfileAssume;
|
|||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.auth.page.account.Applications;
|
||||
import org.keycloak.testsuite.auth.page.login.OAuthGrant;
|
||||
import org.keycloak.testsuite.auth.page.login.UpdatePassword;
|
||||
import org.keycloak.testsuite.util.JavascriptBrowser;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
|
@ -77,6 +78,10 @@ public class JavascriptAdapterTest extends AbstractJavascriptTest {
|
|||
@JavascriptBrowser
|
||||
private OAuthGrant oAuthGrantPage;
|
||||
|
||||
@Page
|
||||
@JavascriptBrowser
|
||||
private UpdatePassword updatePasswordPage;
|
||||
|
||||
@Override
|
||||
protected RealmRepresentation updateRealm(RealmBuilder builder) {
|
||||
return builder.accessTokenLifespan(30 + TOKEN_LIFESPAN_LEEWAY).build();
|
||||
|
@ -660,4 +665,36 @@ public class JavascriptAdapterTest extends AbstractJavascriptTest {
|
|||
.init(defaultArguments(), this::assertSuccessfullyLoggedIn)
|
||||
.executeAsyncScript(refreshWithDeprecatedHandles, assertOutputContains("Success handle"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAIAFromJavascriptAdapterSuccess() {
|
||||
testExecutor.init(defaultArguments(), this::assertInitNotAuth)
|
||||
.login(JSObjectBuilder.create()
|
||||
.add("action", "UPDATE_PASSWORD")
|
||||
.build(), this::assertOnLoginPage)
|
||||
.loginForm(testUser);
|
||||
|
||||
updatePasswordPage.updatePasswords(USER_PASSWORD, USER_PASSWORD);
|
||||
|
||||
testExecutor.init(defaultArguments(), (driver1, output, events1) -> {
|
||||
assertSuccessfullyLoggedIn(driver1, output, events1);
|
||||
waitUntilElement(events1).text().contains("AIA status: success");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAIAFromJavascriptAdapterCancelled() {
|
||||
testExecutor.init(defaultArguments(), this::assertInitNotAuth)
|
||||
.login(JSObjectBuilder.create()
|
||||
.add("action", "UPDATE_PASSWORD")
|
||||
.build(), this::assertOnLoginPage)
|
||||
.loginForm(testUser);
|
||||
|
||||
updatePasswordPage.cancel();
|
||||
|
||||
testExecutor.init(defaultArguments(), (driver1, output, events1) -> {
|
||||
assertSuccessfullyLoggedIn(driver1, output, events1);
|
||||
waitUntilElement(events1).text().contains("AIA status: cancelled");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue