keycloak-aplcache

Merge pull request #1786 from mhajas/master Add consent

11/5/2015 10:02:44 AM

Details

diff --git a/examples/js-console/src/main/webapp/WEB-INF/web.xml b/examples/js-console/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..a7460c2
--- /dev/null
+++ b/examples/js-console/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+
+    <module-name>js-console</module-name>
+</web-app>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyDemoExampleAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyDemoExampleAdapterTest.java
new file mode 100644
index 0000000..3f1aa7d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyDemoExampleAdapterTest.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-wildfly")
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyDemoExampleAdapterTest extends AbstractDemoExampleAdapterTest {
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java
index a196fc7..78c8f6f 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java
@@ -17,21 +17,22 @@
  */
 package org.keycloak.testsuite.admin;
 
+import org.jboss.logging.Logger;
+import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.admin.client.resource.UserResource;
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
 
 import javax.ws.rs.core.Response;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import org.jboss.logging.Logger;
-import org.keycloak.admin.client.resource.ClientResource;
-import org.keycloak.admin.client.resource.UserResource;
-import org.keycloak.representations.idm.CredentialRepresentation;
+
 import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
-import org.keycloak.representations.idm.RoleRepresentation;
-import org.keycloak.representations.idm.UserRepresentation;
 
 /**
  * Created by st on 28.05.15.
@@ -58,6 +59,15 @@ public class ApiUtil {
         return null;
     }
 
+    public static ClientResource findClientResourceByName(RealmResource realm, String name) {
+        for (ClientRepresentation c : realm.clients().findAll()) {
+            if (c.getName().equals(name)) {
+                return realm.clients().get(c.getId());
+            }
+        }
+        return null;
+    }
+
     public static ClientRepresentation findClientByClientId(RealmResource realm, String clientId) {
         ClientRepresentation client = null;
         for (ClientRepresentation c : realm.clients().findAll()) {
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Applications.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Applications.java
index 5623198..7e50272 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Applications.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Applications.java
@@ -17,14 +17,16 @@
  */
 package org.keycloak.testsuite.auth.page.account;
 
-import java.util.List;
-import javax.ws.rs.core.UriBuilder;
+import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
 
+import javax.ws.rs.core.UriBuilder;
+import java.util.List;
+
 /**
- *
  * @author Petr Mensik
+ * @author mhajas
  */
 public class Applications extends AccountManagement {
 
@@ -39,26 +41,42 @@ public class Applications extends AccountManagement {
     @FindBy(xpath = XPATH_APP_TABLE)
     protected WebElement appTable;
 
-    @FindBy(xpath = XPATH_APP_TABLE + "//a")
-    protected List<WebElement> applicationLinks;
-    
+    @FindBy(xpath = XPATH_APP_TABLE + "//tr")
+    private List<WebElement> applicationRows;
+
     public boolean containsApplication(String application) {
-        boolean contains = false;
-        for (WebElement appLink : applicationLinks) {
-            if (appLink.getText().equals(application)) {
-                contains = true;
-                break;
-            }
-        }
-        return contains;
+        return getRowForLinkText(application) != null;
     }
-    
+
     public void clickApplication(String application) {
-        for (WebElement appLink : applicationLinks) {
-            if (appLink.getText().equals(application)) {
-                appLink.click();
+        WebElement row = getRowForLinkText(application);
+        if (row == null) {
+            log.error("Application: " + application + " doesn't exist");
+            throw new IllegalArgumentException("Application: " + application + " doesn't exist");
+        }
+
+        row.findElement(By.xpath(".//a")).click();
+    }
+
+    public void revokeGrantForApplication(String application) {
+        WebElement row = getRowForLinkText(application);
+        if (row == null) {
+            log.error("Application: " + application + " doesn't exist");
+            throw new IllegalArgumentException("Application: " + application + " doesn't exist");
+        }
+
+        row.findElement(By.xpath("//button[@id='revoke-" + application + "']")).click();
+    }
+
+    private WebElement getRowForLinkText(String appLink) {
+        for (WebElement appRow : applicationRows) {
+            if (appRow.findElement(By.xpath(".//td")).getText().equals(appLink)) {
+                return appRow;
             }
         }
+
+        return null;
     }
 
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthRealm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthRealm.java
index e8a435c..f6ba6b4 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthRealm.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthRealm.java
@@ -1,15 +1,16 @@
 package org.keycloak.testsuite.auth.page;
 
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
 import org.keycloak.testsuite.auth.page.login.PageWithLoginUrl;
-import java.net.URI;
+
 import javax.ws.rs.core.UriBuilder;
-import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import java.net.URI;
 
 /**
  * Keycloak realm.
- * 
+ * <p>
  * URL: http://localhost:${auth.server.http.port}/auth/realms/{authRealm}
- * 
+ *
  * @author tkyjovsk
  */
 public class AuthRealm extends AuthServer implements PageWithLoginUrl {
@@ -22,7 +23,7 @@ public class AuthRealm extends AuthServer implements PageWithLoginUrl {
     public static final String EXAMPLE = "example";
 
     public static final String ADMIN = "admin";
-    
+
     public AuthRealm() {
         setUriParameter(AUTH_REALM, MASTER);
     }
@@ -46,7 +47,6 @@ public class AuthRealm extends AuthServer implements PageWithLoginUrl {
     }
 
     /**
-     *
      * @return OIDC Login URL for authRealm
      */
     @Override
@@ -55,4 +55,9 @@ public class AuthRealm extends AuthServer implements PageWithLoginUrl {
                 .build(getAuthRealm());
     }
 
+    public URI getOIDCLogoutUrl() {
+        return OIDCLoginProtocolService.logoutUrl(UriBuilder.fromPath(getAuthRoot()))
+                .build(getAuthRealm());
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/OAuthGrant.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/OAuthGrant.java
new file mode 100644
index 0000000..e328014
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/OAuthGrant.java
@@ -0,0 +1,50 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.auth.page.login;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OAuthGrant extends LoginActions {
+
+    @FindBy(css = "input[name=\"accept\"]")
+    private WebElement acceptButton;
+    @FindBy(css = "input[name=\"cancel\"]")
+    private WebElement cancelButton;
+
+
+    public void accept() {
+        acceptButton.click();
+    }
+
+    public void cancel() {
+        cancelButton.click();
+    }
+
+    @Override
+    public boolean isCurrent() {
+        return driver.getTitle().equals("OAuth Grant");
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettingsForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettingsForm.java
index 41cdf71..589e68e 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettingsForm.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettingsForm.java
@@ -1,15 +1,17 @@
 package org.keycloak.testsuite.console.page.clients;
 
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
 import java.util.ArrayList;
 import java.util.List;
-import org.keycloak.representations.idm.ClientRepresentation;
+
 import static org.keycloak.testsuite.auth.page.login.Login.OIDC;
 import static org.keycloak.testsuite.util.WaitUtils.pause;
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.support.FindBy;
 
 /**
- *
  * @author tkyjovsk
  */
 public class ClientSettingsForm extends CreateClientForm {
@@ -26,6 +28,9 @@ public class ClientSettingsForm extends CreateClientForm {
     @FindBy(xpath = ".//i[contains(@data-ng-click, 'deleteWebOrigin')]")
     private List<WebElement> deleteWebOriginIcons;
 
+    @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='consentRequired']]")
+    private OnOffSwitch consentRequired;
+
     public void setBaseUrl(String baseUrl) {
         setInputValue(baseUrlInput, baseUrl);
     }
@@ -88,4 +93,8 @@ public class ClientSettingsForm extends CreateClientForm {
         return values;
     }
 
+    public void setConsentRequired(boolean value) {
+        consentRequired.setOn(value);
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
index d31d390..4d0eed9 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
@@ -1,34 +1,71 @@
 package org.keycloak.testsuite.adapter.example;
 
-import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
 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.*;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
-import org.keycloak.testsuite.auth.page.account.Account;
-import static org.keycloak.testsuite.util.IOUtil.loadRealm;
-import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
 import org.keycloak.testsuite.adapter.page.CustomerPortalExample;
 import org.keycloak.testsuite.adapter.page.DatabaseServiceExample;
 import org.keycloak.testsuite.adapter.page.ProductPortalExample;
-import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.auth.page.account.Account;
+import org.keycloak.testsuite.auth.page.account.Applications;
+import org.keycloak.testsuite.auth.page.login.OAuthGrant;
+import org.keycloak.testsuite.console.page.clients.ClientSettings;
+import org.keycloak.testsuite.console.page.clients.Clients;
+import org.keycloak.testsuite.console.page.events.Config;
+import org.keycloak.testsuite.console.page.events.LoginEvents;
 import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
 
 public abstract class AbstractDemoExampleAdapterTest extends AbstractExampleAdapterTest {
 
     @Page
-    private CustomerPortalExample customerPortalExample;
+    private CustomerPortalExample customerPortalExamplePage;
+
     @Page
-    private ProductPortalExample productPortalExample;
+    private ProductPortalExample productPortalExamplePage;
+
     @Page
-    private DatabaseServiceExample databaseServiceExample;
+    private DatabaseServiceExample databaseServiceExamplePage;
 
     @Page
-    private Account testRealmAccount;
+    private Account testRealmAccountPage;
+
+    @Page
+    private Clients clientsPage;
+
+    @Page
+    private ClientSettings clientSettingsPage;
+
+    @Page
+    private Config configPage;
+
+    @Page
+    private LoginEvents loginEventsPage;
+
+    @Page
+    private OAuthGrant oAuthGrantPage;
+
+    @Page
+    private Applications applicationsPage;
 
     @Deployment(name = CustomerPortalExample.DEPLOYMENT_NAME)
     private static WebArchive customerPortalExample() throws IOException {
@@ -56,27 +93,31 @@ public abstract class AbstractDemoExampleAdapterTest extends AbstractExampleAdap
         super.setDefaultPageUriParameters();
         testRealmPage.setAuthRealm(DEMO);
         testRealmLoginPage.setAuthRealm(DEMO);
-        testRealmAccount.setAuthRealm(DEMO);
+        testRealmAccountPage.setAuthRealm(DEMO);
+        clientsPage.setConsoleRealm(DEMO);
+        configPage.setConsoleRealm(DEMO);
+        loginEventsPage.setConsoleRealm(DEMO);
+        applicationsPage.setAuthRealm(DEMO);
     }
 
     @Before
     public void beforeDemoExampleTest() {
-        customerPortalExample.navigateTo();
+        customerPortalExamplePage.navigateTo();
         driver.manage().deleteAllCookies();
-        productPortalExample.navigateTo();
+        productPortalExamplePage.navigateTo();
         driver.manage().deleteAllCookies();
     }
 
     @Test
     public void customerPortalListingTest() {
 
-        customerPortalExample.navigateTo();
-        customerPortalExample.customerListing();
+        customerPortalExamplePage.navigateTo();
+        customerPortalExamplePage.customerListing();
 
         testRealmLoginPage.form().login("bburke@redhat.com", "password");
 
-        assertCurrentUrlStartsWith(customerPortalExample);
-        customerPortalExample.waitForCustomerListingHeader();
+        assertCurrentUrlStartsWith(customerPortalExamplePage);
+        customerPortalExamplePage.waitForCustomerListingHeader();
 
         Assert.assertTrue(driver.getPageSource().contains("Username: bburke@redhat.com"));
         Assert.assertTrue(driver.getPageSource().contains("Bill Burke"));
@@ -86,72 +127,140 @@ public abstract class AbstractDemoExampleAdapterTest extends AbstractExampleAdap
     @Test
     public void customerPortalSessionTest() {
 
-        customerPortalExample.navigateTo();
-        customerPortalExample.customerSession();
+        customerPortalExamplePage.navigateTo();
+        customerPortalExamplePage.customerSession();
 
         testRealmLoginPage.form().login("bburke@redhat.com", "password");
 
-        assertCurrentUrlStartsWith(customerPortalExample);
+        assertCurrentUrlStartsWith(customerPortalExamplePage);
 
-        customerPortalExample.waitForCustomerSessionHeader();
+        customerPortalExamplePage.waitForCustomerSessionHeader();
         Assert.assertTrue(driver.getPageSource().contains("You visited this page"));
     }
 
     @Test
     public void productPortalListingTest() {
 
-        productPortalExample.navigateTo();
-        productPortalExample.productListing();
+        productPortalExamplePage.navigateTo();
+        productPortalExamplePage.productListing();
 
         testRealmLoginPage.form().login("bburke@redhat.com", "password");
 
-        assertCurrentUrlStartsWith(productPortalExample);
-        productPortalExample.waitForProductListingHeader();
+        assertCurrentUrlStartsWith(productPortalExamplePage);
+        productPortalExamplePage.waitForProductListingHeader();
 
         Assert.assertTrue(driver.getPageSource().contains("iphone"));
         Assert.assertTrue(driver.getPageSource().contains("ipad"));
         Assert.assertTrue(driver.getPageSource().contains("ipod"));
 
-        productPortalExample.goToCustomers();
+        productPortalExamplePage.goToCustomers();
     }
 
     @Test
     public void goToProductPortalWithOneLoginTest() {
 
-        productPortalExample.navigateTo();
-        productPortalExample.productListing();
+        productPortalExamplePage.navigateTo();
+        productPortalExamplePage.productListing();
 
         testRealmLoginPage.form().login("bburke@redhat.com", "password");
 
-        assertCurrentUrlStartsWith(productPortalExample);
-        productPortalExample.waitForProductListingHeader();
-        productPortalExample.goToCustomers();
+        assertCurrentUrlStartsWith(productPortalExamplePage);
+        productPortalExamplePage.waitForProductListingHeader();
+        productPortalExamplePage.goToCustomers();
 
-        assertCurrentUrlStartsWith(customerPortalExample);
-        customerPortalExample.customerListing();
-        customerPortalExample.goToProducts();
-        assertCurrentUrlStartsWith(productPortalExample);
+        assertCurrentUrlStartsWith(customerPortalExamplePage);
+        customerPortalExamplePage.customerListing();
+        customerPortalExamplePage.goToProducts();
+        assertCurrentUrlStartsWith(productPortalExamplePage);
     }
 
     @Test
     public void logoutFromAllAppsTest() {
 
-        productPortalExample.navigateTo();
-        productPortalExample.productListing();
+        productPortalExamplePage.navigateTo();
+        productPortalExamplePage.productListing();
 
         testRealmLoginPage.form().login("bburke@redhat.com", "password");
 
-        assertCurrentUrlStartsWith(productPortalExample);
-        productPortalExample.waitForProductListingHeader();
+        assertCurrentUrlStartsWith(productPortalExamplePage);
+        productPortalExamplePage.waitForProductListingHeader();
+
+        if (isRelative()) { //KEYCLOAK-1546
+            productPortalExamplePage.logOut();
+        } else {
+            driver.navigate().to(testRealmPage.getOIDCLogoutUrl() + "?redirect_uri=" + productPortalExamplePage);
+        }
 
-        productPortalExample.logOut();
-        assertCurrentUrlStartsWith(productPortalExample);
-        productPortalExample.productListing();
+        assertCurrentUrlStartsWith(productPortalExamplePage);
+        productPortalExamplePage.productListing();
 
-        customerPortalExample.navigateTo();
-        customerPortalExample.customerListing();
+        customerPortalExamplePage.navigateTo();
+        customerPortalExamplePage.customerListing();
         testRealmLoginPage.form().login("bburke@redhat.com", "password");
 
-        customerPortalExample.logOut();
+        customerPortalExamplePage.logOut();
+    }
+
+    @Test
+    public void grantServerBasedApp() {
+        clientsPage.navigateTo();
+        loginPage.form().login(adminUser);
+
+        ClientResource clientResource = ApiUtil.findClientResourceByClientId(testRealmResource(), "customer-portal");
+        ClientRepresentation client = clientResource.toRepresentation();
+        client.setConsentRequired(true);
+        clientResource.update(client);
+
+        RealmRepresentation realm = testRealmResource().toRepresentation();
+        realm.setEventsEnabled(true);
+        realm.setEnabledEventTypes(Arrays.asList("REVOKE_GRANT", "LOGIN"));
+        testRealmResource().update(realm);
+
+        customerPortalExamplePage.navigateTo();
+        customerPortalExamplePage.customerSession();
+
+        loginPage.form().login("bburke@redhat.com", "password");
+
+        assertTrue(oAuthGrantPage.isCurrent());
+
+        oAuthGrantPage.accept();
+
+        assertTrue(driver.getPageSource().contains("Your hostname:"));
+        assertTrue(driver.getPageSource().contains("You visited this page"));
+
+        applicationsPage.navigateTo();
+        applicationsPage.revokeGrantForApplication("customer-portal");
+
+        customerPortalExamplePage.navigateTo();
+        customerPortalExamplePage.customerSession();
+
+        assertTrue(oAuthGrantPage.isCurrent());
+
+        loginEventsPage.navigateTo();
+        loginEventsPage.table().filter();
+        loginEventsPage.table().filterForm().addEventType("REVOKE_GRANT");
+        loginEventsPage.table().update();
+
+        List<WebElement> resultList = loginEventsPage.table().rows();
+
+        assertEquals(2, resultList.size());
+
+        resultList.get(0).findElement(By.xpath(".//td[text()='REVOKE_GRANT']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='account']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='revoked_client']/../td[text()='customer-portal']"));
+
+        loginEventsPage.table().reset();
+        loginEventsPage.table().filterForm().addEventType("LOGIN");
+        loginEventsPage.table().update();
+        resultList = loginEventsPage.table().rows();
+
+        assertEquals(7, resultList.size());
+
+        resultList.get(0).findElement(By.xpath(".//td[text()='LOGIN']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='customer-portal']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='username']/../td[text()='bburke@redhat.com']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='consent']/../td[text()='consent_granted']"));
     }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
index 61d8efa..ab63862 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
@@ -1,26 +1,59 @@
 package org.keycloak.testsuite.adapter.example;
 
-import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
 import org.jboss.arquillian.container.test.api.Deployment;
 import org.jboss.arquillian.graphene.page.Page;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
-import static org.junit.Assert.assertTrue;
 import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
-import static org.keycloak.testsuite.util.IOUtil.loadRealm;
-import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
 import org.keycloak.testsuite.adapter.page.JSConsoleExample;
+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.console.page.clients.ClientSettings;
+import org.keycloak.testsuite.console.page.clients.Clients;
+import org.keycloak.testsuite.console.page.events.Config;
+import org.keycloak.testsuite.console.page.events.LoginEvents;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.keycloak.testsuite.auth.page.AuthRealm.EXAMPLE;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
 import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
 import static org.keycloak.testsuite.util.WaitUtils.pause;
 
 public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampleAdapterTest {
 
     @Page
-    private JSConsoleExample jsConsoleExample;
+    private JSConsoleExample jsConsoleExamplePage;
+
+    @Page
+    private Clients clientsPage;
+
+    @Page
+    private ClientSettings clientSettingsPage;
+
+    @Page
+    private Config configPage;
+
+    @Page
+    private LoginEvents loginEventsPage;
+
+    @Page
+    private OAuthGrant oAuthGrantPage;
+
+    @Page
+    private Applications applicationsPage;
 
     public static int TOKEN_LIFESPAN_LEEWAY = 3; // seconds
 
@@ -34,7 +67,7 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
         RealmRepresentation jsConsoleRealm = loadRealm(new File(EXAMPLES_HOME_DIR + "/js-console/example-realm.json"));
 
         fixClientUrisUsingDeploymentUrl(jsConsoleRealm,
-                JSConsoleExample.CLIENT_ID, jsConsoleExample.buildUri().toASCIIString());
+                JSConsoleExample.CLIENT_ID, jsConsoleExamplePage.buildUri().toASCIIString());
 
         jsConsoleRealm.setAccessTokenLifespan(30 + TOKEN_LIFESPAN_LEEWAY); // seconds
 
@@ -49,85 +82,157 @@ public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampl
 
     @Test
     public void testJSConsoleAuth() {
-        jsConsoleExample.navigateTo();
-        assertCurrentUrlStartsWith(jsConsoleExample);
+        jsConsoleExamplePage.navigateTo();
+        assertCurrentUrlStartsWith(jsConsoleExamplePage);
 
         pause(1000);
 
-        jsConsoleExample.logIn();
+        jsConsoleExamplePage.logIn();
         testRealmLoginPage.form().login("user", "invalid-password");
-        assertCurrentUrlDoesntStartWith(jsConsoleExample);
+        assertCurrentUrlDoesntStartWith(jsConsoleExamplePage);
 
         testRealmLoginPage.form().login("invalid-user", "password");
-        assertCurrentUrlDoesntStartWith(jsConsoleExample);
+        assertCurrentUrlDoesntStartWith(jsConsoleExamplePage);
 
         testRealmLoginPage.form().login("user", "password");
-        assertCurrentUrlStartsWith(jsConsoleExample);
+        assertCurrentUrlStartsWith(jsConsoleExamplePage);
         assertTrue(driver.getPageSource().contains("Init Success (Authenticated)"));
         assertTrue(driver.getPageSource().contains("Auth Success"));
 
         pause(1000);
 
-        jsConsoleExample.logOut();
-        assertCurrentUrlStartsWith(jsConsoleExample);
+        jsConsoleExamplePage.logOut();
+        assertCurrentUrlStartsWith(jsConsoleExamplePage);
         assertTrue(driver.getPageSource().contains("Init Success (Not Authenticated)"));
     }
 
     @Test
     public void testRefreshToken() {
-        jsConsoleExample.navigateTo();
-        assertCurrentUrlStartsWith(jsConsoleExample);
+        jsConsoleExamplePage.navigateTo();
+        assertCurrentUrlStartsWith(jsConsoleExamplePage);
 
-        jsConsoleExample.refreshToken();
+        jsConsoleExamplePage.refreshToken();
         assertTrue(driver.getPageSource().contains("Failed to refresh token"));
 
-        jsConsoleExample.logIn();
+        jsConsoleExamplePage.logIn();
         testRealmLoginPage.form().login("user", "password");
-        assertCurrentUrlStartsWith(jsConsoleExample);
+        assertCurrentUrlStartsWith(jsConsoleExamplePage);
         assertTrue(driver.getPageSource().contains("Auth Success"));
 
-        jsConsoleExample.refreshToken();
+        jsConsoleExamplePage.refreshToken();
         assertTrue(driver.getPageSource().contains("Auth Refresh Success"));
     }
 
     @Test
     public void testRefreshTokenIfUnder30s() {
-        jsConsoleExample.navigateTo();
-        assertCurrentUrlStartsWith(jsConsoleExample);
+        jsConsoleExamplePage.navigateTo();
+        assertCurrentUrlStartsWith(jsConsoleExamplePage);
 
-        jsConsoleExample.refreshToken();
+        jsConsoleExamplePage.refreshToken();
         assertTrue(driver.getPageSource().contains("Failed to refresh token"));
 
-        jsConsoleExample.logIn();
+        jsConsoleExamplePage.logIn();
         testRealmLoginPage.form().login("user", "password");
-        assertCurrentUrlStartsWith(jsConsoleExample);
+        assertCurrentUrlStartsWith(jsConsoleExamplePage);
         assertTrue(driver.getPageSource().contains("Auth Success"));
 
-        jsConsoleExample.refreshTokenIfUnder30s();
+        jsConsoleExamplePage.refreshTokenIfUnder30s();
         assertTrue(driver.getPageSource().contains("Token not refreshed, valid for"));
 
         pause((TOKEN_LIFESPAN_LEEWAY + 2) * 1000);
 
-        jsConsoleExample.refreshTokenIfUnder30s();
+        jsConsoleExamplePage.refreshTokenIfUnder30s();
         assertTrue(driver.getPageSource().contains("Auth Refresh Success"));
     }
-    
-    @Test 
+
+    @Test
     public void testGetProfile() {
-        jsConsoleExample.navigateTo();
-        assertCurrentUrlStartsWith(jsConsoleExample);
-        
-        jsConsoleExample.getProfile();
+        jsConsoleExamplePage.navigateTo();
+        assertCurrentUrlStartsWith(jsConsoleExamplePage);
+
+        jsConsoleExamplePage.getProfile();
         assertTrue(driver.getPageSource().contains("Failed to load profile"));
-        
-        jsConsoleExample.logIn();
+
+        jsConsoleExamplePage.logIn();
         testRealmLoginPage.form().login("user", "password");
-        assertCurrentUrlStartsWith(jsConsoleExample);
+        assertCurrentUrlStartsWith(jsConsoleExamplePage);
         assertTrue(driver.getPageSource().contains("Auth Success"));
 
-        jsConsoleExample.getProfile();
+        jsConsoleExamplePage.getProfile();
         assertTrue(driver.getPageSource().contains("Failed to load profile"));
         assertTrue(driver.getPageSource().contains("\"username\": \"user\""));
     }
 
+    @Test
+    public void grantBrowserBasedApp() {
+        testRealmPage.setAuthRealm(EXAMPLE);
+        testRealmLoginPage.setAuthRealm(EXAMPLE);
+        clientsPage.setConsoleRealm(EXAMPLE);
+        configPage.setConsoleRealm(EXAMPLE);
+        loginEventsPage.setConsoleRealm(EXAMPLE);
+        applicationsPage.setAuthRealm(EXAMPLE);
+
+        jsConsoleExamplePage.navigateTo();
+        driver.manage().deleteAllCookies();
+
+        clientsPage.navigateTo();
+
+        loginPage.form().login("admin", "admin");
+
+        ClientResource clientResource = ApiUtil.findClientResourceByClientId(testRealmResource(), "js-console");
+        ClientRepresentation client = clientResource.toRepresentation();
+        client.setConsentRequired(true);
+        clientResource.update(client);
+
+        RealmRepresentation realm = testRealmResource().toRepresentation();
+        realm.setEventsEnabled(true);
+        realm.setEnabledEventTypes(Arrays.asList("REVOKE_GRANT", "LOGIN"));
+        testRealmResource().update(realm);
+
+        jsConsoleExamplePage.navigateTo();
+        jsConsoleExamplePage.logIn();
+
+        testRealmLoginPage.form().login("user", "password");
+
+        assertTrue(oAuthGrantPage.isCurrent());
+        oAuthGrantPage.accept();
+
+        assertTrue(driver.getPageSource().contains("Init Success (Authenticated)"));
+
+        applicationsPage.navigateTo();
+        applicationsPage.revokeGrantForApplication("js-console");
+
+        jsConsoleExamplePage.navigateTo();
+        jsConsoleExamplePage.logIn();
+
+        assertTrue(oAuthGrantPage.isCurrent());
+
+        loginEventsPage.navigateTo();
+        loginEventsPage.table().filter();
+        loginEventsPage.table().filterForm().addEventType("REVOKE_GRANT");
+        loginEventsPage.table().update();
+
+        List<WebElement> resultList = loginEventsPage.table().rows();
+
+        assertEquals(2, resultList.size());
+
+        resultList.get(0).findElement(By.xpath(".//td[text()='REVOKE_GRANT']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='account']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='revoked_client']/../td[text()='js-console']"));
+
+        loginEventsPage.table().reset();
+        loginEventsPage.table().filterForm().addEventType("LOGIN");
+        loginEventsPage.table().update();
+        resultList = loginEventsPage.table().rows();
+
+        assertEquals(7, resultList.size());
+
+        resultList.get(0).findElement(By.xpath(".//td[text()='LOGIN']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='Client']/../td[text()='js-console']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='IP Address']/../td[text()='127.0.0.1']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='username']/../td[text()='user']"));
+        resultList.get(0).findElement(By.xpath(".//td[text()='consent']/../td[text()='consent_granted']"));
+    }
+
 }