keycloak-aplcache

Changes

Details

diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/AssertEventsServletFilter.java b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/AssertEventsServletFilter.java
index 6d0d4ed..1f4724d 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/AssertEventsServletFilter.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/AssertEventsServletFilter.java
@@ -44,6 +44,11 @@ public class AssertEventsServletFilter implements Filter {
     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
         HttpServletRequest req = (HttpServletRequest) servletRequest;
 
+        if ("/clear-event-queue".equals(req.getRequestURI().substring(req.getContextPath().length()))) {
+            EventsListenerProvider.getInstance().clear();
+            return;
+        }
+
         if ("/event-queue".equals(req.getRequestURI().substring(req.getContextPath().length()))) {
             BlockingQueue<Event> events = EventsListenerProvider.getInstance();
             HttpServletResponse resp = (HttpServletResponse) servletResponse;
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsServer.java b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsServer.java
index 0b04ee3..e18be4b 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsServer.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/events/EventsServer.java
@@ -69,6 +69,7 @@ public class EventsServer {
         FilterInfo filter = Servlets.filter("EventsFilter", AssertEventsServletFilter.class);
         di.addFilter(filter);
         di.addFilterUrlMapping("EventsFilter", "/event-queue", DispatcherType.REQUEST);
+        di.addFilterUrlMapping("EventsFilter", "/clear-event-queue", DispatcherType.REQUEST);
 
         DeploymentManager manager = container.addDeployment(di);
         manager.deploy();
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java
new file mode 100644
index 0000000..52e5858
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2016 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.rest;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.services.resource.RealmResourceProvider;
+import org.keycloak.services.resources.RealmsResource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriBuilder;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class TestApplicationResourceProvider implements RealmResourceProvider {
+
+    private KeycloakSession session;
+
+    public TestApplicationResourceProvider(KeycloakSession session) {
+        this.session = session;
+    }
+
+    @GET
+    @Produces(MediaType.TEXT_HTML)
+    @Path("/{action}")
+    public String get(@PathParam("action") String action) {
+        //String requestUri = session.getContext().getUri().getRequestUri().toString();
+
+        String title = "APP_REQUEST";
+         if (action.equals("auth")) {
+            title = "AUTH_RESPONSE";
+        } else if (action.equals("logout")) {
+            title = "LOGOUT_REQUEST";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("<html><head><title>" + title + "</title></head><body>");
+        UriBuilder base = UriBuilder.fromUri("http://localhost:8180/auth");
+        sb.append("<a href=\"" + RealmsResource.accountUrl(base).build("test").toString() + "\" id=\"account\">account</a>");
+
+        sb.append("</body></html>");
+        return sb.toString();
+    }
+
+    @Override
+    public Object getResource() {
+        return this;
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java
new file mode 100644
index 0000000..e795420
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 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.rest;
+
+import org.keycloak.Config.Scope;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.services.resource.RealmResourceProvider;
+import org.keycloak.services.resource.RealmResourceProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class TestApplicationResourceProviderFactory implements RealmResourceProviderFactory {
+
+    @Override
+    public RealmResourceProvider create(KeycloakSession session) {
+        return new TestApplicationResourceProvider(session);
+    }
+
+    @Override
+    public void init(Scope config) {
+    }
+
+    @Override
+    public void postInit(KeycloakSessionFactory factory) {
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public String getId() {
+        return "app";
+    }
+
+}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory
index 57d5390..9a97746 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory
+++ b/testsuite/integration-arquillian/servers/auth-server/services/event-queue/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory
@@ -15,4 +15,5 @@
 # limitations under the License.
 #
 
-org.keycloak.testsuite.rest.TimeOffsetResourceProviderFactory
\ No newline at end of file
+org.keycloak.testsuite.rest.TimeOffsetResourceProviderFactory
+org.keycloak.testsuite.rest.TestApplicationResourceProviderFactory
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java
index 8e4c92a..f66fdf8 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java
@@ -57,7 +57,7 @@ public class AssertEvents {
     static final String DEFAULT_REALM = "test";
     static final String DEFAULT_USERNAME = "test-user@localhost";
 
-    String defaultRedirectUri = "http://localhost:8081/app/auth";
+    String defaultRedirectUri = "http://localhost:8180/auth/realms/master/app/auth";
     String defaultEventsQueueUri = "http://localhost:8092";
 
     private RealmResource realmResource;
@@ -96,7 +96,23 @@ public class AssertEvents {
     }
 
     public void clear() {
-        realmResource.clearEvents();
+        CloseableHttpClient httpclient = HttpClients.createDefault();
+        try {
+            HttpPost post = new HttpPost(defaultEventsQueueUri + "/clear-event-queue");
+            CloseableHttpResponse response = httpclient.execute(post);
+            if (response.getStatusLine().getStatusCode() != 200) {
+                throw new RuntimeException("Failed to clear events from " + post.getURI() + ": " + response.getStatusLine().toString());
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        finally {
+            try {
+                httpclient.close();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
     }
 
     public ExpectedEvent expectRequiredAction(EventType event) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/admin/ApiUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/admin/ApiUtil.java
new file mode 100644
index 0000000..824728a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/admin/ApiUtil.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2016 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.legacy.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 javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.Response.StatusType;
+
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+
+import org.keycloak.representations.idm.GroupRepresentation;
+
+/**
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ApiUtil {
+
+    private static final Logger log = Logger.getLogger(ApiUtil.class);
+
+    public static String getCreatedId(Response response) {
+        URI location = response.getLocation();
+        if (!response.getStatusInfo().equals(Status.CREATED)) {
+            StatusType statusInfo = response.getStatusInfo();
+            throw new RuntimeException("Create method returned status " +
+                    statusInfo.getReasonPhrase() + " (Code: " + statusInfo.getStatusCode() + "); expected status: Created (201)");
+        }
+        if (location == null) {
+            return null;
+        }
+        String path = location.getPath();
+        return path.substring(path.lastIndexOf('/') + 1);
+    }
+
+    public static ClientResource findClientResourceByClientId(RealmResource realm, String clientId) {
+        for (ClientRepresentation c : realm.clients().findAll()) {
+            if (c.getClientId().equals(clientId)) {
+                return realm.clients().get(c.getId());
+            }
+        }
+        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 ClientResource findClientByClientId(RealmResource realm, String clientId) {
+        for (ClientRepresentation c : realm.clients().findAll()) {
+            if (c.getClientId().equals(clientId)) {
+                return realm.clients().get(c.getId());
+            }
+        }
+        return null;
+    }
+
+    public static UserRepresentation findUserByUsername(RealmResource realm, String username) {
+        UserRepresentation user = null;
+        List<UserRepresentation> ur = realm.users().search(username, null, null);
+        if (ur.size() == 1) {
+            user = ur.get(0);
+        }
+        return user;
+    }
+
+    public static String createUserWithAdminClient(RealmResource realm, UserRepresentation user) {
+        Response response = realm.users().create(user);
+        String createdId = getCreatedId(response);
+        response.close();
+        return createdId;
+    }
+
+    public static String createUserAndResetPasswordWithAdminClient(RealmResource realm, UserRepresentation user, String password) {
+        String id = createUserWithAdminClient(realm, user);
+        resetUserPassword(realm.users().get(id), password, false);
+        return id;
+    }
+
+    public static void resetUserPassword(UserResource userResource, String newPassword, boolean temporary) {
+        CredentialRepresentation newCredential = new CredentialRepresentation();
+        newCredential.setType(PASSWORD);
+        newCredential.setValue(newPassword);
+        newCredential.setTemporary(temporary);
+        userResource.resetPassword(newCredential);
+    }
+
+    public static void assignClientRoles(RealmResource realm, String userId, String clientName, String... roles) {
+        String realmName = realm.toRepresentation().getRealm();
+        String clientId = "";
+        for (ClientRepresentation clientRepresentation : realm.clients().findAll()) {
+            if (clientRepresentation.getClientId().equals(clientName)) {
+                clientId = clientRepresentation.getId();
+            }
+        }
+
+        if (!clientId.isEmpty()) {
+            ClientResource clientResource = realm.clients().get(clientId);
+
+            List<RoleRepresentation> roleRepresentations = new ArrayList<>();
+            for (String roleName : roles) {
+                RoleRepresentation role = clientResource.roles().get(roleName).toRepresentation();
+                roleRepresentations.add(role);
+            }
+
+            UserResource userResource = realm.users().get(userId);
+            log.debug("assigning roles: " + Arrays.toString(roles) + " to user: \""
+                    + userResource.toRepresentation().getUsername() + "\" of client: \""
+                    + clientName + "\" in realm: \"" + realmName + "\"");
+            userResource.roles().clientLevel(clientId).add(roleRepresentations);
+        } else {
+            log.warn("client with name " + clientName + "doesn't exist in realm " + realmName);
+        }
+    }
+
+    public static boolean groupContainsSubgroup(GroupRepresentation group, GroupRepresentation subgroup) {
+        boolean contains = false;
+        for (GroupRepresentation sg : group.getSubGroups()) {
+            if (subgroup.getId().equals(sg.getId())) {
+                contains = true;
+                break;
+            }
+        }
+        return contains;
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/admin/ClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/admin/ClientTest.java
new file mode 100644
index 0000000..41ab967
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/admin/ClientTest.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2016 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.legacy.admin;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ProtocolMappersResource;
+import org.keycloak.models.Constants;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
+import org.keycloak.representations.idm.*;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.keycloak.testsuite.legacy.LegacyOAuthClient;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ClientTest extends LegacyAbstractClientTest {
+
+    @Test
+    public void getClients() {
+        assertNames(realm.clients().findAll(), "account", "realm-management", "security-admin-console", "broker", Constants.ADMIN_CLI_CLIENT_ID);
+    }
+
+    private ClientRepresentation createClient() {
+        ClientRepresentation rep = new ClientRepresentation();
+        rep.setClientId("my-app");
+        rep.setDescription("my-app description");
+        rep.setEnabled(true);
+        Response response = realm.clients().create(rep);
+        response.close();
+        String id = ApiUtil.getCreatedId(response);
+        rep.setId(id);
+        return rep;
+    }
+
+    @Test
+    public void createClientVerify() {
+        String id = createClient().getId();
+
+        assertNotNull(realm.clients().get(id));
+        assertNames(realm.clients().findAll(), "account", "realm-management", "security-admin-console", "broker", "my-app", Constants.ADMIN_CLI_CLIENT_ID);
+    }
+
+    @Test
+    public void removeClient() {
+        String id = createClient().getId();
+
+        realm.clients().get(id).remove();
+    }
+
+    @Test
+    public void getClientRepresentation() {
+        String id = createClient().getId();
+
+        ClientRepresentation rep = realm.clients().get(id).toRepresentation();
+        assertEquals(id, rep.getId());
+        assertEquals("my-app", rep.getClientId());
+        assertTrue(rep.isEnabled());
+    }
+
+    /**
+     * See <a href="https://issues.jboss.org/browse/KEYCLOAK-1918">KEYCLOAK-1918</a>
+     */
+    @Test
+    public void getClientDescription() {
+        String id = createClient().getId();
+
+        ClientRepresentation rep = realm.clients().get(id).toRepresentation();
+        assertEquals(id, rep.getId());
+        assertEquals("my-app description", rep.getDescription());
+    }
+
+    @Test
+    public void getClientSessions() throws Exception {
+        LegacyOAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
+        assertEquals(200, response.getStatusCode());
+
+        LegacyOAuthClient.AuthorizationCodeResponse codeResponse = oauth.doLogin("test-user@localhost", "password");
+
+        LegacyOAuthClient.AccessTokenResponse response2 = oauth.doAccessTokenRequest(codeResponse.getCode(), "password");
+        assertEquals(200, response2.getStatusCode());
+
+        ClientResource app = ApiUtil.findClientByClientId(keycloak.realm("test"), "test-app");
+
+        assertEquals(2, (long) app.getApplicationSessionCount().get("count"));
+
+        List<UserSessionRepresentation> userSessions = app.getUserSessions(0, 100);
+        assertEquals(2, userSessions.size());
+        assertEquals(1, userSessions.get(0).getClients().size());
+    }
+
+    @Test
+    // KEYCLOAK-1110
+    public void deleteDefaultRole() {
+        ClientRepresentation rep = new ClientRepresentation();
+        rep.setClientId("my-app");
+        rep.setEnabled(true);
+        Response response = realm.clients().create(rep);
+        response.close();
+        String id = ApiUtil.getCreatedId(response);
+
+        RoleRepresentation role = new RoleRepresentation("test", "test", false);
+        realm.clients().get(id).roles().create(role);
+
+        rep = realm.clients().get(id).toRepresentation();
+        rep.setDefaultRoles(new String[] { "test" });
+        realm.clients().get(id).update(rep);
+
+        assertArrayEquals(new String[] { "test" }, realm.clients().get(id).toRepresentation().getDefaultRoles());
+
+        realm.clients().get(id).roles().deleteRole("test");
+
+        assertNull(realm.clients().get(id).toRepresentation().getDefaultRoles());
+    }
+
+    @Test
+    public void testProtocolMappers() {
+        createClient();
+        ProtocolMappersResource mappersResource = ApiUtil.findClientByClientId(realm, "my-app").getProtocolMappers();
+
+        protocolMappersTest(mappersResource);
+    }
+
+    @Test
+    public void updateClient() {
+        ClientRepresentation client = createClient();
+
+        ClientRepresentation newClient = new ClientRepresentation();
+        newClient.setId(client.getId());
+        newClient.setClientId(client.getClientId());
+        newClient.setBaseUrl("http://baseurl");
+
+        realm.clients().get(client.getId()).update(newClient);
+
+        ClientRepresentation storedClient = realm.clients().get(client.getId()).toRepresentation();
+
+        assertClient(client, storedClient);
+
+        newClient.setSecret("new-secret");
+
+        realm.clients().get(client.getId()).update(newClient);
+
+        storedClient = realm.clients().get(client.getId()).toRepresentation();
+        assertClient(client, storedClient);
+    }
+
+    public static void protocolMappersTest(ProtocolMappersResource mappersResource) {
+        // assert default mappers found
+        List<ProtocolMapperRepresentation> protocolMappers = mappersResource.getMappers();
+
+        String emailMapperId = null;
+        String usernameMapperId = null;
+        String fooMapperId = null;
+        for (ProtocolMapperRepresentation mapper : protocolMappers) {
+            if (mapper.getName().equals(OIDCLoginProtocolFactory.EMAIL)) {
+                emailMapperId = mapper.getId();
+            } else if (mapper.getName().equals(OIDCLoginProtocolFactory.USERNAME)) {
+                usernameMapperId = mapper.getId();
+            } else if (mapper.getName().equals("foo")) {
+                fooMapperId = mapper.getId();
+            }
+        }
+
+        assertNotNull(emailMapperId);
+        assertNotNull(usernameMapperId);
+        assertNull(fooMapperId);
+
+        // Create foo mapper
+        ProtocolMapperRepresentation fooMapper = new ProtocolMapperRepresentation();
+        fooMapper.setName("foo");
+        fooMapper.setProtocol("fooProtocol");
+        fooMapper.setProtocolMapper("fooMapper");
+        fooMapper.setConsentRequired(true);
+        Response response = mappersResource.createMapper(fooMapper);
+        String location = response.getLocation().toString();
+        fooMapperId = location.substring(location.lastIndexOf("/") + 1);
+        response.close();
+
+        fooMapper = mappersResource.getMapperById(fooMapperId);
+        assertEquals(fooMapper.getName(), "foo");
+
+        // Update foo mapper
+        fooMapper.setProtocolMapper("foo-mapper-updated");
+        mappersResource.update(fooMapperId, fooMapper);
+
+        fooMapper = mappersResource.getMapperById(fooMapperId);
+        assertEquals(fooMapper.getProtocolMapper(), "foo-mapper-updated");
+
+        // Remove foo mapper
+        mappersResource.delete(fooMapperId);
+        try {
+            mappersResource.getMapperById(fooMapperId);
+            fail("Not expected to find deleted mapper");
+        } catch (NotFoundException nfe) {
+        }
+    }
+
+    public static void assertClient(ClientRepresentation client, ClientRepresentation storedClient) {
+        if (client.getClientId() != null) Assert.assertEquals(client.getClientId(), storedClient.getClientId());
+        if (client.getName() != null) Assert.assertEquals(client.getName(), storedClient.getName());
+        if (client.isEnabled() != null) Assert.assertEquals(client.isEnabled(), storedClient.isEnabled());
+        if (client.isBearerOnly() != null) Assert.assertEquals(client.isBearerOnly(), storedClient.isBearerOnly());
+        if (client.isPublicClient() != null) Assert.assertEquals(client.isPublicClient(), storedClient.isPublicClient());
+        if (client.isFullScopeAllowed() != null) Assert.assertEquals(client.isFullScopeAllowed(), storedClient.isFullScopeAllowed());
+        if (client.getRootUrl() != null) Assert.assertEquals(client.getRootUrl(), storedClient.getRootUrl());
+        if (client.getAdminUrl() != null) Assert.assertEquals(client.getAdminUrl(), storedClient.getAdminUrl());
+        if (client.getBaseUrl() != null) Assert.assertEquals(client.getBaseUrl(), storedClient.getBaseUrl());
+        if (client.isSurrogateAuthRequired() != null) Assert.assertEquals(client.isSurrogateAuthRequired(), storedClient.isSurrogateAuthRequired());
+        if (client.getClientAuthenticatorType() != null) Assert.assertEquals(client.getClientAuthenticatorType(), storedClient.getClientAuthenticatorType());
+
+        if (client.getNotBefore() != null) {
+            Assert.assertEquals(client.getNotBefore(), storedClient.getNotBefore());
+        }
+        if (client.getDefaultRoles() != null) {
+            Set<String> set = new HashSet<String>();
+            for (String val : client.getDefaultRoles()) {
+                set.add(val);
+            }
+            Set<String> storedSet = new HashSet<String>();
+            for (String val : storedClient.getDefaultRoles()) {
+                storedSet.add(val);
+            }
+
+            Assert.assertEquals(set, storedSet);
+        }
+
+        List<String> redirectUris = client.getRedirectUris();
+        if (redirectUris != null) {
+            Set<String> set = new HashSet<String>();
+            for (String val : client.getRedirectUris()) {
+                set.add(val);
+            }
+            Set<String> storedSet = new HashSet<String>();
+            for (String val : storedClient.getRedirectUris()) {
+                storedSet.add(val);
+            }
+
+            Assert.assertEquals(set, storedSet);
+        }
+
+        List<String> webOrigins = client.getWebOrigins();
+        if (webOrigins != null) {
+            Set<String> set = new HashSet<String>();
+            for (String val : client.getWebOrigins()) {
+                set.add(val);
+            }
+            Set<String> storedSet = new HashSet<String>();
+            for (String val : storedClient.getWebOrigins()) {
+                storedSet.add(val);
+            }
+
+            Assert.assertEquals(set, storedSet);
+        }
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/admin/LegacyAbstractClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/admin/LegacyAbstractClientTest.java
new file mode 100644
index 0000000..02fa735
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/admin/LegacyAbstractClientTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.legacy.admin;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.After;
+import org.junit.Before;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.legacy.LegacyAdapter;
+import org.keycloak.util.JsonSerialization;
+
+import static org.junit.Assert.assertArrayEquals;
+
+/**
+ * This class adapts the functionality from the old testsuite to make tests
+ * easier to port.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public abstract class LegacyAbstractClientTest extends LegacyAdapter  {
+    protected static final String REALM_NAME = "admin-client-test";
+
+    protected RealmResource realm;
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        super.addTestRealms(testRealms);
+
+        RealmRepresentation adminRealmRep = new RealmRepresentation();
+        adminRealmRep.setRealm(REALM_NAME);
+        adminRealmRep.setEnabled(true);
+        Map<String, String> config = new HashMap<>();
+        config.put("from", "auto@keycloak.org");
+        config.put("host", "localhost");
+        config.put("port", "3025");
+        adminRealmRep.setSmtpServer(config);
+        testRealms.add(adminRealmRep);
+    }
+
+    @Before
+    public void setRealm() {
+        realm = adminClient.realm(REALM_NAME);
+    }
+
+    // old testsuite expects this realm to be removed at the end of the test
+    // not sure if it really matters
+    @After
+    public void after() {
+        for (RealmRepresentation r : keycloak.realms().findAll()) {
+            if (r.getRealm().equals(REALM_NAME)) {
+                removeRealm(r);
+            }
+        }
+    }
+
+    // Taken from Keycloak class in old testsuite.
+    // So, code in old testsuite calling this looks like Keycloak.loadJson(.....)
+    public static <T> T loadJson(InputStream is, Class<T> type) {
+        try {
+            return JsonSerialization.readValue(is, type);
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to parse json", e);
+        }
+    }
+
+    public static <T> void assertNames(List<T> actual, String... expected) {
+        Arrays.sort(expected);
+        String[] actualNames = names(actual);
+        assertArrayEquals("Expected: " + Arrays.toString(expected) + ", was: " + Arrays.toString(actualNames), expected, actualNames);
+    }
+
+    public static <T> List<T> sort(List<T> list) {
+        Collections.sort(list, new Comparator<Object>() {
+            @Override
+            public int compare(Object o1, Object o2) {
+                return name(o1).compareTo(name(o2));
+            }
+        });
+        return list;
+    }
+
+    public static <T> String[] names(List<T> list) {
+        String[] names = new String[list.size()];
+        for (int i = 0; i < list.size(); i++) {
+            names[i] = name(list.get(i));
+        }
+        Arrays.sort(names);
+        return names;
+    }
+
+    public static String name(Object o1) {
+        if (o1 instanceof RealmRepresentation) {
+            return ((RealmRepresentation) o1).getRealm();
+        } else if (o1 instanceof ClientRepresentation) {
+            return ((ClientRepresentation) o1).getClientId();
+        } else if (o1 instanceof IdentityProviderRepresentation) {
+            return ((IdentityProviderRepresentation) o1).getAlias();
+        }
+        throw new IllegalArgumentException();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/LegacyAdapter.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/LegacyAdapter.java
new file mode 100644
index 0000000..578f59a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/LegacyAdapter.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.legacy;
+
+import java.util.List;
+import org.junit.Before;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+
+import static org.keycloak.testsuite.legacy.admin.LegacyAbstractClientTest.loadJson;
+
+/**
+ * Allows migration of legacy tests with a minimum of changes to the tests themselves.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class LegacyAdapter extends AbstractKeycloakTest {
+
+    protected Keycloak keycloak;
+    protected LegacyOAuthClient oauth;
+    private String testRealmPublicKey;
+
+    private ClientRepresentation findTestApp(List<ClientRepresentation> clients) {
+        for (ClientRepresentation client : clients) {
+            if (client.getClientId().equals("test-app")) return client;
+        }
+
+        return null;
+    }
+
+    public static RealmRepresentation findRealm(String realmName, List<RealmRepresentation> testRealms) {
+        for (RealmRepresentation realm : testRealms) {
+            if (realm.getRealm().equals(realmName)) return realm;
+        }
+
+        return null;
+    }
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        RealmRepresentation rep = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
+        this.testRealmPublicKey = rep.getPublicKey();
+
+        /* Implement this old behavior by changing the representation before the realm gets loaded
+        keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
+            }
+        });*/
+        findTestApp(rep.getClients()).setDirectAccessGrantsEnabled(true);
+
+        testRealms.add(rep);
+    }
+
+    @Before
+    public void setUpKeycloakAlias() {
+        keycloak = adminClient;
+    }
+
+    @Before
+    public void setUpOAuthClient() {
+        oauth = new LegacyOAuthClient(driver, testRealmPublicKey);
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/LegacyConstants.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/LegacyConstants.java
new file mode 100644
index 0000000..9e57abb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/LegacyConstants.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 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.legacy;
+
+import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class LegacyConstants {
+
+    public static String SERVER_ROOT = AuthServerTestEnricher.getAuthServerContextRoot();
+    public static String AUTH_SERVER_ROOT = SERVER_ROOT + "/auth";
+    public static String APP_ROOT = AUTH_SERVER_ROOT + "/realms/master/app";
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/LegacyOAuthClient.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/LegacyOAuthClient.java
new file mode 100644
index 0000000..14c7143
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/LegacyOAuthClient.java
@@ -0,0 +1,641 @@
+/*
+ * Copyright 2016 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.legacy;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+import org.junit.Assert;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.RSATokenVerifier;
+import org.keycloak.common.VerificationException;
+import org.keycloak.common.util.PemUtils;
+import org.keycloak.constants.AdapterConstants;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.RefreshToken;
+import org.keycloak.util.BasicAuthHelper;
+import org.keycloak.util.JsonSerialization;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class LegacyOAuthClient {
+
+    private WebDriver driver;
+
+    private String baseUrl = LegacyConstants.AUTH_SERVER_ROOT;
+
+    private String realm = "test";
+
+    private String clientId = "test-app";
+
+    private String redirectUri = LegacyConstants.APP_ROOT + "/auth";
+
+    private String state = "mystate";
+
+    private String scope;
+
+    private String uiLocales = null;
+
+    private PublicKey realmPublicKey;
+
+    private String clientSessionState;
+
+    private String clientSessionHost;
+
+    public LegacyOAuthClient(WebDriver driver, String publicKey) {
+        this.driver = driver;
+
+        try {
+            realmPublicKey = PemUtils.decodePublicKey(publicKey);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to retrieve realm public key", e);
+        }
+    }
+
+    public AuthorizationCodeResponse doLogin(String username, String password) {
+        openLoginForm();
+        String src = driver.getPageSource();
+        driver.findElement(By.id("username")).sendKeys(username);
+        driver.findElement(By.id("password")).sendKeys(password);
+        driver.findElement(By.name("login")).click();
+
+        return new AuthorizationCodeResponse(this);
+    }
+
+    public void doLoginGrant(String username, String password) {
+        openLoginForm();
+
+        driver.findElement(By.id("username")).sendKeys(username);
+        driver.findElement(By.id("password")).sendKeys(password);
+        driver.findElement(By.name("login")).click();
+    }
+
+    public AccessTokenResponse doAccessTokenRequest(String code, String password) {
+        CloseableHttpClient client = new DefaultHttpClient();
+        try {
+            HttpPost post = new HttpPost(getAccessTokenUrl());
+
+            List<NameValuePair> parameters = new LinkedList<NameValuePair>();
+            parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.AUTHORIZATION_CODE));
+
+            if (code != null) {
+                parameters.add(new BasicNameValuePair(OAuth2Constants.CODE, code));
+            }
+            if (redirectUri != null) {
+                parameters.add(new BasicNameValuePair(OAuth2Constants.REDIRECT_URI, redirectUri));
+            }
+            if (clientId != null && password != null) {
+                String authorization = BasicAuthHelper.createHeader(clientId, password);
+                post.setHeader("Authorization", authorization);
+            } else if (clientId != null) {
+                parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, clientId));
+            }
+
+            if (clientSessionState != null) {
+                parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_STATE, clientSessionState));
+            }
+
+            if (clientSessionHost != null) {
+                parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_HOST, clientSessionHost));
+            }
+
+            UrlEncodedFormEntity formEntity = null;
+            try {
+                formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException(e);
+            }
+            post.setEntity(formEntity);
+
+            try {
+                return new AccessTokenResponse(client.execute(post));
+            } catch (Exception e) {
+                throw new RuntimeException("Failed to retrieve access token", e);
+            }
+        } finally {
+            closeClient(client);
+        }
+    }
+
+    public String introspectAccessTokenWithClientCredential(String clientId, String clientSecret, String tokenToIntrospect) {
+        return introspectTokenWithClientCredential(clientId, clientSecret, "access_token", tokenToIntrospect);
+    }
+
+    public String introspectRefreshTokenWithClientCredential(String clientId, String clientSecret, String tokenToIntrospect) {
+        return introspectTokenWithClientCredential(clientId, clientSecret, "refresh_token", tokenToIntrospect);
+    }
+
+    public String introspectTokenWithClientCredential(String clientId, String clientSecret, String tokenType, String tokenToIntrospect) {
+        CloseableHttpClient client = new DefaultHttpClient();
+        try {
+            HttpPost post = new HttpPost(getTokenIntrospectionUrl());
+
+            String authorization = BasicAuthHelper.createHeader(clientId, clientSecret);
+            post.setHeader("Authorization", authorization);
+
+            List<NameValuePair> parameters = new LinkedList<>();
+
+            parameters.add(new BasicNameValuePair("token", tokenToIntrospect));
+            parameters.add(new BasicNameValuePair("token_type_hint", tokenType));
+
+            UrlEncodedFormEntity formEntity;
+
+            try {
+                formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException(e);
+            }
+
+            post.setEntity(formEntity);
+
+            try {
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+                client.execute(post).getEntity().writeTo(out);
+
+                return new String(out.toByteArray());
+            } catch (Exception e) {
+                throw new RuntimeException("Failed to retrieve access token", e);
+            }
+        } finally {
+            closeClient(client);
+        }
+    }
+
+    public AccessTokenResponse doGrantAccessTokenRequest(String clientSecret, String username,  String password) throws Exception {
+        return doGrantAccessTokenRequest(realm, username, password, null, clientId, clientSecret);
+    }
+
+    public AccessTokenResponse doGrantAccessTokenRequest(String clientSecret, String username,  String password, String otp) throws Exception {
+        return doGrantAccessTokenRequest(realm, username, password, otp, clientId, clientSecret);
+    }
+
+    public AccessTokenResponse doGrantAccessTokenRequest(String realm, String username, String password, String totp,
+                                                         String clientId, String clientSecret) throws Exception {
+        CloseableHttpClient client = new DefaultHttpClient();
+        try {
+            HttpPost post = new HttpPost(getResourceOwnerPasswordCredentialGrantUrl(realm));
+
+            List<NameValuePair> parameters = new LinkedList<NameValuePair>();
+            parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD));
+            parameters.add(new BasicNameValuePair("username", username));
+            parameters.add(new BasicNameValuePair("password", password));
+            if (totp != null) {
+                parameters.add(new BasicNameValuePair("totp", totp));
+
+            }
+            if (clientSecret != null) {
+                String authorization = BasicAuthHelper.createHeader(clientId, clientSecret);
+                post.setHeader("Authorization", authorization);
+            } else {
+                parameters.add(new BasicNameValuePair("client_id", clientId));
+
+            }
+
+            if (clientSessionState != null) {
+                parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_STATE, clientSessionState));
+            }
+            if (clientSessionHost != null) {
+                parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_HOST, clientSessionHost));
+            }
+            if (scope != null) {
+                parameters.add(new BasicNameValuePair(OAuth2Constants.SCOPE, scope));
+            }
+
+            UrlEncodedFormEntity formEntity;
+            try {
+                formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException(e);
+            }
+            post.setEntity(formEntity);
+
+            return new AccessTokenResponse(client.execute(post));
+        } finally {
+            closeClient(client);
+        }
+    }
+
+    public AccessTokenResponse doClientCredentialsGrantAccessTokenRequest(String clientSecret) throws Exception {
+        CloseableHttpClient client = new DefaultHttpClient();
+        try {
+            HttpPost post = new HttpPost(getServiceAccountUrl());
+
+            String authorization = BasicAuthHelper.createHeader(clientId, clientSecret);
+            post.setHeader("Authorization", authorization);
+
+            List<NameValuePair> parameters = new LinkedList<NameValuePair>();
+            parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS));
+
+            if (scope != null) {
+                parameters.add(new BasicNameValuePair(OAuth2Constants.SCOPE, scope));
+            }
+
+            UrlEncodedFormEntity formEntity;
+            try {
+                formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException(e);
+            }
+            post.setEntity(formEntity);
+
+            return new AccessTokenResponse(client.execute(post));
+        } finally {
+            closeClient(client);
+        }
+    }
+
+
+    public HttpResponse doLogout(String refreshToken, String clientSecret) throws IOException {
+        CloseableHttpClient client = new DefaultHttpClient();
+        try {
+            HttpPost post = new HttpPost(getLogoutUrl(null, null));
+
+            List<NameValuePair> parameters = new LinkedList<NameValuePair>();
+            if (refreshToken != null) {
+                parameters.add(new BasicNameValuePair(OAuth2Constants.REFRESH_TOKEN, refreshToken));
+            }
+            if (clientId != null && clientSecret != null) {
+                String authorization = BasicAuthHelper.createHeader(clientId, clientSecret);
+                post.setHeader("Authorization", authorization);
+            } else if (clientId != null) {
+                parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, clientId));
+            }
+
+            UrlEncodedFormEntity formEntity;
+            try {
+                formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException(e);
+            }
+            post.setEntity(formEntity);
+
+            return client.execute(post);
+        } finally {
+            closeClient(client);
+        }
+    }
+
+    public AccessTokenResponse doRefreshTokenRequest(String refreshToken, String password) {
+        CloseableHttpClient client = new DefaultHttpClient();
+        try {
+            HttpPost post = new HttpPost(getRefreshTokenUrl());
+
+            List<NameValuePair> parameters = new LinkedList<NameValuePair>();
+            parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.REFRESH_TOKEN));
+
+            if (refreshToken != null) {
+                parameters.add(new BasicNameValuePair(OAuth2Constants.REFRESH_TOKEN, refreshToken));
+            }
+            if (clientId != null && password != null) {
+                String authorization = BasicAuthHelper.createHeader(clientId, password);
+                post.setHeader("Authorization", authorization);
+            } else if (clientId != null) {
+                parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, clientId));
+            }
+
+            if (clientSessionState != null) {
+                parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_STATE, clientSessionState));
+            }
+            if (clientSessionHost != null) {
+                parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_HOST, clientSessionHost));
+            }
+
+            UrlEncodedFormEntity formEntity;
+            try {
+                formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException(e);
+            }
+            post.setEntity(formEntity);
+
+            try {
+                return new AccessTokenResponse(client.execute(post));
+            } catch (Exception e) {
+                throw new RuntimeException("Failed to retrieve access token", e);
+            }
+        } finally {
+            closeClient(client);
+        }
+    }
+
+    public void closeClient(CloseableHttpClient client) {
+        try {
+            client.close();
+        } catch (IOException ioe) {
+            throw new RuntimeException(ioe);
+        }
+    }
+
+    public AccessToken verifyToken(String token) {
+        try {
+            return RSATokenVerifier.verifyToken(token, realmPublicKey, baseUrl + "/realms/" + realm);
+        } catch (VerificationException e) {
+            throw new RuntimeException("Failed to verify token", e);
+        }
+    }
+
+    public RefreshToken verifyRefreshToken(String refreshToken) {
+        try {
+            JWSInput jws = new JWSInput(refreshToken);
+            if (!RSAProvider.verify(jws, realmPublicKey)) {
+                throw new RuntimeException("Invalid refresh token");
+            }
+            return jws.readJsonContent(RefreshToken.class);
+        } catch (Exception e) {
+            throw new RuntimeException("Invalid refresh token", e);
+        }
+    }
+
+    public String getClientId() {
+        return clientId;
+    }
+
+    public String getCurrentRequest() {
+        return driver.getCurrentUrl().substring(0, driver.getCurrentUrl().indexOf('?'));
+    }
+
+    public URI getCurrentUri() {
+        try {
+            return new URI(driver.getCurrentUrl());
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Map<String, String> getCurrentQuery() {
+        Map<String, String> m = new HashMap<String, String>();
+        List<NameValuePair> pairs = URLEncodedUtils.parse(getCurrentUri(), "UTF-8");
+        for (NameValuePair p : pairs) {
+            m.put(p.getName(), p.getValue());
+        }
+        return m;
+    }
+
+    public void openLoginForm() {
+        driver.navigate().to(getLoginFormUrl());
+    }
+
+    public void openLogout() {
+        UriBuilder b = OIDCLoginProtocolService.logoutUrl(UriBuilder.fromUri(baseUrl));
+        if (redirectUri != null) {
+            b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri);
+        }
+        driver.navigate().to(b.build(realm).toString());
+    }
+
+    public String getRedirectUri() {
+        return redirectUri;
+    }
+
+    public String getLoginFormUrl() {
+        UriBuilder b = OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(LegacyConstants.SERVER_ROOT + "/auth"));
+        b.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE);
+        if (clientId != null) {
+            b.queryParam(OAuth2Constants.CLIENT_ID, clientId);
+        }
+        if (redirectUri != null) {
+            b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri);
+        }
+        if (state != null) {
+            b.queryParam(OAuth2Constants.STATE, state);
+        }
+        if(uiLocales != null){
+            b.queryParam(OAuth2Constants.UI_LOCALES_PARAM, uiLocales);
+        }
+        if (scope != null) {
+            b.queryParam(OAuth2Constants.SCOPE, scope);
+        }
+        return b.build(realm).toString();
+    }
+
+    public String getAccessTokenUrl() {
+        UriBuilder b = OIDCLoginProtocolService.tokenUrl(UriBuilder.fromUri(baseUrl));
+        return b.build(realm).toString();
+    }
+
+    public String getTokenIntrospectionUrl() {
+        UriBuilder b = OIDCLoginProtocolService.tokenIntrospectionUrl(UriBuilder.fromUri(baseUrl));
+        return b.build(realm).toString();
+    }
+
+    public String getLogoutUrl(String redirectUri, String sessionState) {
+        UriBuilder b = OIDCLoginProtocolService.logoutUrl(UriBuilder.fromUri(baseUrl));
+        if (redirectUri != null) {
+            b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri);
+        }
+        if (sessionState != null) {
+            b.queryParam("session_state", sessionState);
+        }
+        return b.build(realm).toString();
+    }
+
+    public String getResourceOwnerPasswordCredentialGrantUrl() {
+        UriBuilder b = OIDCLoginProtocolService.tokenUrl(UriBuilder.fromUri(baseUrl));
+        return b.build(realm).toString();
+    }
+
+    public String getResourceOwnerPasswordCredentialGrantUrl(String realm) {
+        UriBuilder b = OIDCLoginProtocolService.tokenUrl(UriBuilder.fromUri(baseUrl));
+        return b.build(realm).toString();
+    }
+
+    public String getServiceAccountUrl() {
+        return getResourceOwnerPasswordCredentialGrantUrl();
+    }
+
+    public String getRefreshTokenUrl() {
+        UriBuilder b = OIDCLoginProtocolService.tokenUrl(UriBuilder.fromUri(baseUrl));
+        return b.build(realm).toString();
+    }
+
+    public LegacyOAuthClient realm(String realm) {
+        this.realm = realm;
+        return this;
+    }
+    public LegacyOAuthClient realmPublicKey(PublicKey key) {
+        this.realmPublicKey = key;
+        return this;
+    }
+
+    public LegacyOAuthClient clientId(String clientId) {
+        this.clientId = clientId;
+        return this;
+    }
+
+    public LegacyOAuthClient redirectUri(String redirectUri) {
+        this.redirectUri = redirectUri;
+        return this;
+    }
+
+    public LegacyOAuthClient state(String state) {
+        this.state = state;
+        return this;
+    }
+
+    public LegacyOAuthClient scope(String scope) {
+        this.scope = scope;
+        return this;
+    }
+
+    public LegacyOAuthClient uiLocales(String uiLocales){
+        this.uiLocales = uiLocales;
+        return this;
+    }
+
+    public LegacyOAuthClient clientSessionState(String client_session_state) {
+        this.clientSessionState = client_session_state;
+        return this;
+    }
+
+    public LegacyOAuthClient clientSessionHost(String client_session_host) {
+        this.clientSessionHost = client_session_host;
+        return this;
+    }
+
+    public String getRealm() {
+        return realm;
+    }
+
+    public static class AuthorizationCodeResponse {
+
+        private boolean isRedirected;
+        private String code;
+        private String state;
+        private String error;
+
+        public AuthorizationCodeResponse(LegacyOAuthClient client) {
+            isRedirected = client.getCurrentRequest().equals(client.getRedirectUri());
+            code = client.getCurrentQuery().get(OAuth2Constants.CODE);
+            state = client.getCurrentQuery().get(OAuth2Constants.STATE);
+            error = client.getCurrentQuery().get(OAuth2Constants.ERROR);
+        }
+
+        public boolean isRedirected() {
+            return isRedirected;
+        }
+
+        public String getCode() {
+            return code;
+        }
+
+        public String getState() {
+            return state;
+        }
+
+        public String getError() {
+            return error;
+        }
+
+    }
+
+    public static class AccessTokenResponse {
+        private int statusCode;
+
+        private String accessToken;
+        private String tokenType;
+        private int expiresIn;
+        private int refreshExpiresIn;
+        private String refreshToken;
+
+        private String error;
+        private String errorDescription;
+
+        public AccessTokenResponse(HttpResponse response) throws Exception {
+            statusCode = response.getStatusLine().getStatusCode();
+            if (!"application/json".equals(response.getHeaders("Content-Type")[0].getValue())) {
+                Assert.fail("Invalid content type");
+            }
+
+            String s = IOUtils.toString(response.getEntity().getContent());
+            Map responseJson = JsonSerialization.readValue(s, Map.class);
+
+            if (statusCode == 200) {
+                accessToken = (String)responseJson.get("access_token");
+                tokenType = (String)responseJson.get("token_type");
+                expiresIn = (Integer)responseJson.get("expires_in");
+                refreshExpiresIn = (Integer)responseJson.get("refresh_expires_in");
+
+                if (responseJson.containsKey(OAuth2Constants.REFRESH_TOKEN)) {
+                    refreshToken = (String)responseJson.get(OAuth2Constants.REFRESH_TOKEN);
+                }
+            } else {
+                error = (String)responseJson.get(OAuth2Constants.ERROR);
+                errorDescription = responseJson.containsKey(OAuth2Constants.ERROR_DESCRIPTION) ? (String)responseJson.get(OAuth2Constants.ERROR_DESCRIPTION) : null;
+            }
+        }
+
+        public String getAccessToken() {
+            return accessToken;
+        }
+
+        public String getError() {
+            return error;
+        }
+
+        public String getErrorDescription() {
+            return errorDescription;
+        }
+
+        public int getExpiresIn() {
+            return expiresIn;
+        }
+
+        public int getRefreshExpiresIn() {
+            return refreshExpiresIn;
+        }
+
+        public int getStatusCode() {
+            return statusCode;
+        }
+
+        public String getRefreshToken() {
+            return refreshToken;
+        }
+
+        public String getTokenType() {
+            return tokenType;
+        }
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/oauth/TokenIntrospectionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/oauth/TokenIntrospectionTest.java
new file mode 100755
index 0000000..f405944
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/legacy/oauth/TokenIntrospectionTest.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2016 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.legacy.oauth;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.common.util.Time;
+import org.keycloak.testsuite.util.KeycloakModelUtils;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.EventRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.oidc.TokenMetadataRepresentation;
+import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.legacy.LegacyAdapter;
+import org.keycloak.testsuite.legacy.LegacyOAuthClient.AccessTokenResponse;
+//import org.keycloak.testsuite.pages.LoginPage;
+//import org.keycloak.testsuite.rule.KeycloakRule;
+//import org.keycloak.testsuite.rule.WebResource;
+//import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.util.JsonSerialization;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class TokenIntrospectionTest extends LegacyAdapter {
+
+   /* @ClassRule >>> now implemented in addTestRealms()
+    public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
+
+        @Override
+                                                                                           vvv "test" realm
+        public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+            vvv Done in LegacyAdapter vvv
+            appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true);
+
+            ClientModel confApp = KeycloakModelUtils.createClient(appRealm, "confidential-cli");
+            confApp.setSecret("secret1");
+            new ClientManager(manager).enableServiceAccount(confApp);
+            ClientModel pubApp = KeycloakModelUtils.createClient(appRealm, "public-cli");
+            pubApp.setPublicClient(true);
+            {
+                UserModel user = manager.getSession().users().addUser(appRealm, KeycloakModelUtils.generateId(), "no-permissions", false, false);
+                user.updateCredential(UserCredentialModel.password("password"));
+                user.setEnabled(true);
+                RoleModel role = appRealm.getRole("user");
+                user.grantRole(role);
+            }
+
+            keycloak = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", Constants.ADMIN_CLI_CLIENT_ID);
+        }
+
+    }); */
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        super.addTestRealms(testRealms);
+        RealmRepresentation appRealm = findRealm("test", testRealms);
+        ClientRepresentation confApp = KeycloakModelUtils.createClient(appRealm, "confidential-cli");
+        confApp.setSecret("secret1");
+        confApp.setServiceAccountsEnabled(Boolean.TRUE);
+
+        ClientRepresentation pubApp = KeycloakModelUtils.createClient(appRealm, "public-cli");
+        pubApp.setPublicClient(Boolean.TRUE);
+
+        UserRepresentation user = new UserRepresentation();
+        user.setUsername("no-permissions");
+        CredentialRepresentation credential = new CredentialRepresentation();
+        credential.setType("password");
+        credential.setValue("password");
+        List<CredentialRepresentation> creds = new ArrayList<>();
+        creds.add(credential);
+        user.setCredentials(creds);
+        user.setEnabled(Boolean.TRUE);
+        List<String> realmRoles = new ArrayList<>();
+        realmRoles.add("user");
+        user.setRealmRoles(realmRoles);
+        appRealm.getUsers().add(user);
+    }
+
+    private AssertEvents events;
+
+    @Before
+    public void setUpAssertEvents() throws Exception {
+        events = new AssertEvents(this);
+    }
+
+    @Test
+    public void testConfidentialClientCredentialsBasicAuthentication() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+        ObjectMapper objectMapper = new ObjectMapper();
+        JsonNode jsonNode = objectMapper.readTree(tokenResponse);
+
+        assertTrue(jsonNode.get("active").asBoolean());
+        assertEquals("test-user@localhost", jsonNode.get("username").asText());
+        assertEquals("test-app", jsonNode.get("client_id").asText());
+        assertTrue(jsonNode.has("exp"));
+        assertTrue(jsonNode.has("iat"));
+        assertTrue(jsonNode.has("nbf"));
+        assertTrue(jsonNode.has("sub"));
+        assertTrue(jsonNode.has("aud"));
+        assertTrue(jsonNode.has("iss"));
+        assertTrue(jsonNode.has("jti"));
+
+        TokenMetadataRepresentation rep = objectMapper.readValue(tokenResponse, TokenMetadataRepresentation.class);
+
+        assertTrue(rep.isActive());
+        assertEquals("test-user@localhost", rep.getUserName());
+        assertEquals("test-app", rep.getClientId());
+        assertEquals(jsonNode.get("exp").asInt(), rep.getExpiration());
+        assertEquals(jsonNode.get("iat").asInt(), rep.getIssuedAt());
+        assertEquals(jsonNode.get("nbf").asInt(), rep.getNotBefore());
+        assertEquals(jsonNode.get("sub").asText(), rep.getSubject());
+        assertEquals(jsonNode.get("aud").asText(), rep.getAudience()[0]);
+        assertEquals(jsonNode.get("iss").asText(), rep.getIssuer());
+        assertEquals(jsonNode.get("jti").asText(), rep.getId());
+
+        events.clear();
+    }
+
+    @Test
+    public void testInvalidClientCredentials() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "bad_credential", accessTokenResponse.getAccessToken());
+
+        assertEquals("{\"error_description\":\"Authentication failed.\",\"error\":\"invalid_request\"}", tokenResponse);
+
+        events.clear();
+    }
+
+    @Test
+    public void testIntrospectRefreshToken() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        EventRepresentation loginEvent = events.expectLogin().assertEvent();
+        String sessionId = loginEvent.getSessionId();
+        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+        ObjectMapper objectMapper = new ObjectMapper();
+        JsonNode jsonNode = objectMapper.readTree(tokenResponse);
+
+        assertTrue(jsonNode.get("active").asBoolean());
+        assertEquals(sessionId, jsonNode.get("session_state").asText());
+        assertEquals("test-app", jsonNode.get("client_id").asText());
+        assertTrue(jsonNode.has("exp"));
+        assertTrue(jsonNode.has("iat"));
+        assertTrue(jsonNode.has("nbf"));
+        assertTrue(jsonNode.has("sub"));
+        assertTrue(jsonNode.has("aud"));
+        assertTrue(jsonNode.has("iss"));
+        assertTrue(jsonNode.has("jti"));
+
+        TokenMetadataRepresentation rep = objectMapper.readValue(tokenResponse, TokenMetadataRepresentation.class);
+
+        assertTrue(rep.isActive());
+        assertEquals("test-app", rep.getClientId());
+        assertEquals(jsonNode.get("session_state").asText(), rep.getSessionState());
+        assertEquals(jsonNode.get("exp").asInt(), rep.getExpiration());
+        assertEquals(jsonNode.get("iat").asInt(), rep.getIssuedAt());
+        assertEquals(jsonNode.get("nbf").asInt(), rep.getNotBefore());
+        assertEquals(jsonNode.get("iss").asText(), rep.getIssuer());
+        assertEquals(jsonNode.get("jti").asText(), rep.getId());
+
+        events.clear();
+    }
+
+    @Test
+    public void testPublicClientCredentialsNotAllowed() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("public-cli", "it_doesnt_matter", accessTokenResponse.getAccessToken());
+
+        assertEquals("{\"error_description\":\"Client not allowed.\",\"error\":\"invalid_request\"}", tokenResponse);
+
+        events.clear();
+    }
+
+    @Test
+    public void testInactiveAccessToken() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+        String inactiveAccessToken = "eyJhbGciOiJSUzI1NiJ9.eyJub25jZSI6IjczMGZjNjQ1LTBlMDQtNDE3Yi04MDY0LTkyYWIyY2RjM2QwZSIsImp0aSI6ImU5ZGU1NjU2LWUzMjctNDkxNC1hNjBmLTI1MzJlYjBiNDk4OCIsImV4cCI6MTQ1MjI4MTAwMCwibmJmIjowLCJpYXQiOjE0NTIyODA3MDAsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9hdXRoL3JlYWxtcy9leGFtcGxlIiwiYXVkIjoianMtY29uc29sZSIsInN1YiI6IjFkNzQ0MDY5LWYyOTgtNGU3Yy1hNzNiLTU1YzlhZjgzYTY4NyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImpzLWNvbnNvbGUiLCJzZXNzaW9uX3N0YXRlIjoiNzc2YTA0OTktODNjNC00MDhkLWE5YjctYTZiYzQ5YmQ3MThjIiwiY2xpZW50X3Nlc3Npb24iOiJjN2Y5ODczOC05MDhlLTQxOWYtYTdkNC1kODYxYjRhYTI3NjkiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsidXNlciJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJ2aWV3LXByb2ZpbGUiXX19LCJuYW1lIjoiU2FtcGxlIFVzZXIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyIiwiZ2l2ZW5fbmFtZSI6IlNhbXBsZSIsImZhbWlseV9uYW1lIjoiVXNlciIsImVtYWlsIjoic2FtcGxlLXVzZXJAZXhhbXBsZSJ9.YyPV74j9CqOG2Jmq692ZZpqycjNpUgtYVRfQJccS_FU84tGVXoKKsXKYeY2UJ1Y_bPiYG1I1J6JSXC8XqgQijCG7Nh7oK0yN74JbRN58HG75fvg6K9BjR6hgJ8mHT8qPrCux2svFucIMIZ180eoBoRvRstkidOhl_mtjT_i31fU";
+        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", inactiveAccessToken);
+        ObjectMapper objectMapper = new ObjectMapper();
+        JsonNode jsonNode = objectMapper.readTree(tokenResponse);
+
+        assertFalse(jsonNode.get("active").asBoolean());
+
+        TokenMetadataRepresentation rep = objectMapper.readValue(tokenResponse, TokenMetadataRepresentation.class);
+
+        assertFalse(rep.isActive());
+        assertNull(rep.getUserName());
+        assertNull(rep.getClientId());
+        assertNull(rep.getSubject());
+
+        events.clear();
+    }
+
+    @Test
+    public void testIntrospectAccessToken() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        EventRepresentation loginEvent = events.expectLogin().assertEvent();
+        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+        TokenMetadataRepresentation rep = JsonSerialization.readValue(tokenResponse, TokenMetadataRepresentation.class);
+
+        assertTrue(rep.isActive());
+        assertEquals("test-user@localhost", rep.getUserName());
+        assertEquals("test-app", rep.getClientId());
+        assertEquals(loginEvent.getUserId(), rep.getSubject());
+
+        events.clear();
+    }
+
+    @Test
+    public void testIntrospectAccessTokenSessionInvalid() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+        oauth.doLogout(accessTokenResponse.getRefreshToken(), "password");
+
+        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+        TokenMetadataRepresentation rep = JsonSerialization.readValue(tokenResponse, TokenMetadataRepresentation.class);
+
+        assertFalse(rep.isActive());
+        assertNull(rep.getUserName());
+        assertNull(rep.getClientId());
+        assertNull(rep.getSubject());
+
+        events.clear();
+    }
+
+    @Test
+    public void testIntrospectAccessTokenUserDisabled() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+
+        EventRepresentation loginEvent = events.expectLogin().assertEvent();
+
+        UserRepresentation userRep = new UserRepresentation();
+        try {
+            userRep.setEnabled(false);
+            keycloak.realm(oauth.getRealm()).users().get(loginEvent.getUserId()).update(userRep);
+
+            String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+            TokenMetadataRepresentation rep = JsonSerialization.readValue(tokenResponse, TokenMetadataRepresentation.class);
+
+            assertFalse(rep.isActive());
+            assertNull(rep.getUserName());
+            assertNull(rep.getClientId());
+            assertNull(rep.getSubject());
+
+            events.clear();
+        } finally {
+            userRep.setEnabled(true);
+            keycloak.realm(oauth.getRealm()).users().get(loginEvent.getUserId()).update(userRep);
+        }
+    }
+
+    @Test
+    public void testIntrospectAccessTokenExpired() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+
+        try {
+            Time.setOffset(keycloak.realm(oauth.getRealm()).toRepresentation().getAccessTokenLifespan() + 1);
+
+            String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+            TokenMetadataRepresentation rep = JsonSerialization.readValue(tokenResponse, TokenMetadataRepresentation.class);
+
+            assertFalse(rep.isActive());
+            assertNull(rep.getUserName());
+            assertNull(rep.getClientId());
+            assertNull(rep.getSubject());
+
+            events.clear();
+        } finally {
+            Time.setOffset(0);
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/KeycloakModelUtils.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/KeycloakModelUtils.java
new file mode 100644
index 0000000..eda966d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/KeycloakModelUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.util;
+
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+
+import static org.keycloak.models.utils.KeycloakModelUtils.getDefaultClientAuthenticatorType;
+
+/**
+ * This is a client-side version of KeycloakModelUtils that uses the adminClient to
+ * manipulate the model.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class KeycloakModelUtils {
+
+    public static ClientRepresentation createClient(RealmRepresentation realm, String name) {
+        ClientRepresentation app = new ClientRepresentation();
+        app.setName(name);
+        app.setClientId(name);
+        realm.getClients().add(app);
+        app.setClientAuthenticatorType(getDefaultClientAuthenticatorType());
+        generateSecret(app);
+        app.setFullScopeAllowed(true);
+
+        return app;
+    }
+
+    public static CredentialRepresentation generateSecret(ClientRepresentation client) {
+        UserCredentialModel secret = UserCredentialModel.generateSecret();
+        client.setSecret(secret.getValue());
+        return ModelToRepresentation.toRepresentation(secret);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json
index 845adda..beb1d85 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json
@@ -101,11 +101,11 @@
     {
       "clientId": "test-app",
       "enabled": true,
-      "baseUrl": "http://localhost:8081/app",
+      "baseUrl": "http://localhost:8180/auth/realms/master/app",
       "redirectUris": [
-        "http://localhost:8081/app/*"
+        "http://localhost:8180/auth/realms/master/app/*"
       ],
-      "adminUrl": "http://localhost:8081/app/logout",
+      "adminUrl": "http://localhost:8180/auth/realms/master/app/logout",
       "secret": "password"
     },
     {
@@ -114,7 +114,7 @@
       "consentRequired": true,
 
       "redirectUris": [
-        "http://localhost:8081/app/*"
+        "http://localhost:8180/app/*"
       ],
       "secret": "password"
     }