keycloak-uncached

Merge pull request #420 from patriot1burke/master admin

5/23/2014 8:03:25 PM

Changes

Details

diff --git a/core/src/main/java/org/keycloak/KeycloakPrincipal.java b/core/src/main/java/org/keycloak/KeycloakPrincipal.java
index 00848a2..ca05f79 100755
--- a/core/src/main/java/org/keycloak/KeycloakPrincipal.java
+++ b/core/src/main/java/org/keycloak/KeycloakPrincipal.java
@@ -9,11 +9,15 @@ import java.security.Principal;
  */
 public class KeycloakPrincipal implements Principal, Serializable {
     protected final String name;
-    protected final String surrogate;
+    protected final KeycloakSecurityContext context;
 
-    public KeycloakPrincipal(String name, String surrogate) {
+    public KeycloakPrincipal(String name, KeycloakSecurityContext context) {
         this.name = name;
-        this.surrogate = surrogate;
+        this.context = context;
+    }
+
+    public KeycloakSecurityContext getKeycloakSecurityContext() {
+        return context;
     }
 
     @Override
@@ -21,10 +25,6 @@ public class KeycloakPrincipal implements Principal, Serializable {
         return name;
     }
 
-    public String getSurrogate() {
-        return surrogate;
-    }
-
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -33,16 +33,13 @@ public class KeycloakPrincipal implements Principal, Serializable {
         KeycloakPrincipal that = (KeycloakPrincipal) o;
 
         if (!name.equals(that.name)) return false;
-        if (surrogate != null ? !surrogate.equals(that.surrogate) : that.surrogate != null) return false;
 
         return true;
     }
 
     @Override
     public int hashCode() {
-        int result = name.hashCode();
-        result = 31 * result + (surrogate != null ? surrogate.hashCode() : 0);
-        return result;
+        return name.hashCode();
     }
 
     @Override
diff --git a/core/src/main/java/org/keycloak/representations/AccessTokenResponse.java b/core/src/main/java/org/keycloak/representations/AccessTokenResponse.java
index 217f3e8..109c35c 100755
--- a/core/src/main/java/org/keycloak/representations/AccessTokenResponse.java
+++ b/core/src/main/java/org/keycloak/representations/AccessTokenResponse.java
@@ -27,6 +27,9 @@ public class AccessTokenResponse {
     @JsonProperty("not-before-policy")
     protected int notBeforePolicy;
 
+    @JsonProperty("session-state")
+    protected String sessionState;
+
     public String getToken() {
         return token;
     }
@@ -74,4 +77,12 @@ public class AccessTokenResponse {
     public void setNotBeforePolicy(int notBeforePolicy) {
         this.notBeforePolicy = notBeforePolicy;
     }
+
+    public String getSessionState() {
+        return sessionState;
+    }
+
+    public void setSessionState(String sessionState) {
+        this.sessionState = sessionState;
+    }
 }
diff --git a/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
index 516ae8e..8751a38 100755
--- a/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
@@ -17,6 +17,7 @@ public class OAuthClientRepresentation {
     protected ClaimRepresentation claims;
     protected Integer notBefore;
     protected Boolean publicClient;
+    protected Boolean directGrantsOnly;
 
 
     public String getId() {
@@ -98,4 +99,12 @@ public class OAuthClientRepresentation {
     public void setPublicClient(Boolean publicClient) {
         this.publicClient = publicClient;
     }
+
+    public Boolean isDirectGrantsOnly() {
+        return directGrantsOnly;
+    }
+
+    public void setDirectGrantsOnly(Boolean directGrantsOnly) {
+        this.directGrantsOnly = directGrantsOnly;
+    }
 }
diff --git a/core/src/main/java/org/keycloak/ServiceUrlConstants.java b/core/src/main/java/org/keycloak/ServiceUrlConstants.java
index 2d6387f..34b5f50 100755
--- a/core/src/main/java/org/keycloak/ServiceUrlConstants.java
+++ b/core/src/main/java/org/keycloak/ServiceUrlConstants.java
@@ -10,6 +10,7 @@ public interface ServiceUrlConstants {
     public static final String TOKEN_SERVICE_ACCESS_CODE_PATH = "/realms/{realm-name}/tokens/access/codes";
     public static final String TOKEN_SERVICE_REFRESH_PATH = "/realms/{realm-name}/tokens/refresh";
     public static final String TOKEN_SERVICE_LOGOUT_PATH = "/realms/{realm-name}/tokens/logout";
+    public static final String TOKEN_SERVICE_DIRECT_GRANT_PATH = "/realms/{realm-name}/tokens/grants/access";
     public static final String ACCOUNT_SERVICE_PATH = "/realms/{realm-name}/account";
     public static final String REALM_INFO_PATH = "/realms/{realm-name}";
 
diff --git a/examples/cors/angular-product-app/src/main/webapp/js/app.js b/examples/cors/angular-product-app/src/main/webapp/js/app.js
index 50b68bc..3e56205 100755
--- a/examples/cors/angular-product-app/src/main/webapp/js/app.js
+++ b/examples/cors/angular-product-app/src/main/webapp/js/app.js
@@ -10,10 +10,12 @@ var logout = function(){
 
 
 angular.element(document).ready(function ($http) {
+    console.log("*** here");
     var keycloakAuth = new Keycloak('keycloak.json');
     auth.loggedIn = false;
 
-    keycloakAuth.init('login-required').success(function () {
+    keycloakAuth.init({ onLoad: 'login-required' }).success(function () {
+        console.log('here login');
         auth.loggedIn = true;
         auth.authz = keycloakAuth;
         auth.logoutUrl = keycloakAuth.authServerUrl + "/realms/" + keycloakAuth.realm + "/tokens/logout?redirect_uri=http://localhost:8080/angular-product/index.html";
@@ -38,20 +40,20 @@ module.controller('GlobalCtrl', function($scope, $http) {
 
     };
     $scope.loadRoles = function() {
-        $http.query("http://localhost-auth:8080/auth/admin/realms/" + keycloakAuth.realm + "/roles").success(function(data) {
+        $http.get("http://localhost-auth:8080/auth/admin/realms/" + auth.authz.realm + "/roles").success(function(data) {
             $scope.roles = angular.fromJson(data);
 
         });
 
     };
     $scope.addRole = function() {
-        $http.post("http://localhost-auth:8080/auth/admin/realms/" + keycloakAuth.realm + "/roles", {name: 'stuff'}).success(function() {
+        $http.post("http://localhost-auth:8080/auth/admin/realms/" + auth.authz.realm + "/roles", {name: 'stuff'}).success(function() {
             $scope.loadRoles();
         });
 
     };
     $scope.deleteRole = function() {
-        $http.delete("http://localhost-auth:8080/auth/admin/realms/" + keycloakAuth.realm + "/roles/stuff").success(function() {
+        $http.delete("http://localhost-auth:8080/auth/admin/realms/" + auth.authz.realm + "/roles/stuff").success(function() {
             $scope.loadRoles();
         });
 
diff --git a/examples/demo-template/admin-access-app/pom.xml b/examples/demo-template/admin-access-app/pom.xml
new file mode 100755
index 0000000..36e57fc
--- /dev/null
+++ b/examples/demo-template/admin-access-app/pom.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.0-beta-1-SNAPSHOT</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.keycloak.example.demo</groupId>
+    <artifactId>admin-access-example</artifactId>
+    <packaging>war</packaging>
+    <name>Admin Access Example</name>
+    <description/>
+
+    <repositories>
+        <repository>
+            <id>jboss</id>
+            <name>jboss repo</name>
+            <url>http://repository.jboss.org/nexus/content/groups/public/</url>
+        </repository>
+    </repositories>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jboss.spec.javax.servlet</groupId>
+            <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-adapter-core</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>${keycloak.apache.httpcomponents.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>admin-access</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.jboss.as.plugins</groupId>
+                <artifactId>jboss-as-maven-plugin</artifactId>
+                <version>7.4.Final</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java b/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java
new file mode 100755
index 0000000..6f63140
--- /dev/null
+++ b/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java
@@ -0,0 +1,151 @@
+package org.keycloak.example;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.message.BasicNameValuePair;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.ServiceUrlConstants;
+import org.keycloak.adapters.HttpClientBuilder;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.IDToken;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.util.BasicAuthHelper;
+import org.keycloak.util.JsonSerialization;
+import org.keycloak.util.KeycloakUriBuilder;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AdminClient {
+
+    static class TypedList extends ArrayList<RoleRepresentation> {
+    }
+
+    public static class Failure extends Exception {
+        private int status;
+
+        public Failure(int status) {
+            this.status = status;
+        }
+
+        public int getStatus() {
+            return status;
+        }
+    }
+
+    public static AccessTokenResponse getToken() throws IOException {
+
+        HttpClient client = new HttpClientBuilder()
+                .disableTrustManager().build();
+
+
+        try {
+            HttpPost post = new HttpPost(KeycloakUriBuilder.fromUri("http://localhost:8080/auth")
+                    .path(ServiceUrlConstants.TOKEN_SERVICE_DIRECT_GRANT_PATH).build("demo"));
+            List <NameValuePair> formparams = new ArrayList <NameValuePair>();
+            formparams.add(new BasicNameValuePair("username", "admin"));
+            formparams.add(new BasicNameValuePair("password", "password"));
+            formparams.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, "admin-client"));
+            UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
+            post.setEntity(form);
+
+            HttpResponse response = client.execute(post);
+            int status = response.getStatusLine().getStatusCode();
+            HttpEntity entity = response.getEntity();
+            if (status != 200) {
+                throw new IOException("Bad status: " + status);
+            }
+            if (entity == null) {
+                throw new IOException("No Entity");
+            }
+            InputStream is = entity.getContent();
+            try {
+                ByteArrayOutputStream os = new ByteArrayOutputStream();
+                int c;
+                while ((c = is.read()) != -1) {
+                    os.write(c);
+                }
+                byte[] bytes = os.toByteArray();
+                String json = new String(bytes);
+                try {
+                    return JsonSerialization.readValue(json, AccessTokenResponse.class);
+                } catch (IOException e) {
+                    throw new IOException(json, e);
+                }
+            } finally {
+                try {
+                    is.close();
+                } catch (IOException ignored) {
+
+                }
+            }
+        } finally {
+            client.getConnectionManager().shutdown();
+        }
+    }
+
+    public static void logout(AccessTokenResponse res) throws IOException {
+
+        HttpClient client = new HttpClientBuilder()
+                .disableTrustManager().build();
+
+
+        try {
+            HttpGet get = new HttpGet(KeycloakUriBuilder.fromUri("http://localhost:8080/auth")
+                    .path(ServiceUrlConstants.TOKEN_SERVICE_LOGIN_PATH)
+                    .queryParam("session-state", res.getSessionState())
+                    .build("demo"));
+            HttpResponse response = client.execute(get);
+            HttpEntity entity = response.getEntity();
+            if (entity == null) {
+                return;
+            }
+            InputStream is = entity.getContent();
+            if (is != null) is.close();
+        } finally {
+            client.getConnectionManager().shutdown();
+        }
+    }
+
+    public static List<RoleRepresentation> getRealmRoles(AccessTokenResponse res) throws Failure {
+
+        HttpClient client = new HttpClientBuilder()
+                .disableTrustManager().build();
+        try {
+            HttpGet get = new HttpGet("http://localhost:8080/auth/admin/realms/demo/roles");
+            get.addHeader("Authorization", "Bearer " + res.getToken());
+            try {
+                HttpResponse response = client.execute(get);
+                if (response.getStatusLine().getStatusCode() != 200) {
+                    throw new Failure(response.getStatusLine().getStatusCode());
+                }
+                HttpEntity entity = response.getEntity();
+                InputStream is = entity.getContent();
+                try {
+                    return JsonSerialization.readValue(is, TypedList.class);
+                } finally {
+                    is.close();
+                }
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        } finally {
+            client.getConnectionManager().shutdown();
+        }
+    }
+}
diff --git a/examples/demo-template/admin-access-app/src/main/webapp/admin/admin.jsp b/examples/demo-template/admin-access-app/src/main/webapp/admin/admin.jsp
new file mode 100755
index 0000000..1107888
--- /dev/null
+++ b/examples/demo-template/admin-access-app/src/main/webapp/admin/admin.jsp
@@ -0,0 +1,30 @@
+<%@ page import="org.keycloak.representations.idm.RoleRepresentation" %>
+<%@ page import="org.keycloak.example.AdminClient" %>
+<%@ page import="org.keycloak.representations.AccessTokenResponse" %>
+<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
+         pageEncoding="ISO-8859-1" %>
+<html>
+<head>
+    <title>Admin Interface</title>
+</head>
+<body bgcolor="#E3F6CE">
+<h2>List of Realm Roles from Admin REST API Call</h2>
+<%
+    java.util.List<RoleRepresentation> list = null;
+    try {
+        AccessTokenResponse res = AdminClient.getToken();
+        list = AdminClient.getRealmRoles(res);
+        AdminClient.logout(res);
+    } catch (AdminClient.Failure failure) {
+        out.println("There was a failure processing request.  You either didn't configure Keycloak properly");
+        out.println("Status from database service invocation was: " + failure.getStatus());
+        return;
+    }
+    for (RoleRepresentation role : list) {
+        out.print("<p>");
+        out.print(role.getName());
+        out.println("</p>");
+
+    }
+%></body>
+</html>
\ No newline at end of file
diff --git a/examples/demo-template/admin-access-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/examples/demo-template/admin-access-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
new file mode 100755
index 0000000..9c1bac9
--- /dev/null
+++ b/examples/demo-template/admin-access-app/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -0,0 +1,9 @@
+<jboss-deployment-structure>
+    <deployment>
+        <dependencies>
+            <!-- the Demo code uses classes in these modules.  These are optional to import if you are not using
+                 Apache Http Client or the HttpClientBuilder that comes with the adapter core -->
+            <module name="org.apache.httpcomponents"/>
+        </dependencies>
+    </deployment>
+</jboss-deployment-structure>
\ No newline at end of file
diff --git a/examples/demo-template/admin-access-app/src/main/webapp/WEB-INF/web.xml b/examples/demo-template/admin-access-app/src/main/webapp/WEB-INF/web.xml
new file mode 100755
index 0000000..1c496ce
--- /dev/null
+++ b/examples/demo-template/admin-access-app/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,9 @@
+<?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>admin-access</module-name>
+
+</web-app>
diff --git a/examples/demo-template/admin-access-app/src/main/webapp/WEB-INF/web.xml.unconfigured b/examples/demo-template/admin-access-app/src/main/webapp/WEB-INF/web.xml.unconfigured
new file mode 100755
index 0000000..1c496ce
--- /dev/null
+++ b/examples/demo-template/admin-access-app/src/main/webapp/WEB-INF/web.xml.unconfigured
@@ -0,0 +1,9 @@
+<?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>admin-access</module-name>
+
+</web-app>
diff --git a/examples/demo-template/customer-app/src/main/java/org/keycloak/example/AdminClient.java b/examples/demo-template/customer-app/src/main/java/org/keycloak/example/AdminClient.java
new file mode 100755
index 0000000..f776c4c
--- /dev/null
+++ b/examples/demo-template/customer-app/src/main/java/org/keycloak/example/AdminClient.java
@@ -0,0 +1,67 @@
+package org.keycloak.example;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.HttpClientBuilder;
+import org.keycloak.representations.IDToken;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AdminClient {
+
+    static class TypedList extends ArrayList<RoleRepresentation> {
+    }
+
+    public static class Failure extends Exception {
+        private int status;
+
+        public Failure(int status) {
+            this.status = status;
+        }
+
+        public int getStatus() {
+            return status;
+        }
+    }
+
+    public static List<RoleRepresentation> getRealmRoles(HttpServletRequest req) throws Failure {
+        KeycloakSecurityContext session = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
+
+        HttpClient client = new HttpClientBuilder()
+                .disableTrustManager().build();
+        try {
+            HttpGet get = new HttpGet("http://localhost:8080/auth/admin/realms/demo/roles");
+            get.addHeader("Authorization", "Bearer " + session.getTokenString());
+            try {
+                HttpResponse response = client.execute(get);
+                if (response.getStatusLine().getStatusCode() != 200) {
+                    throw new Failure(response.getStatusLine().getStatusCode());
+                }
+                HttpEntity entity = response.getEntity();
+                InputStream is = entity.getContent();
+                try {
+                    return JsonSerialization.readValue(is, TypedList.class);
+                } finally {
+                    is.close();
+                }
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        } finally {
+            client.getConnectionManager().shutdown();
+        }
+    }
+}
diff --git a/examples/demo-template/customer-app/src/main/webapp/admin/admin.jsp b/examples/demo-template/customer-app/src/main/webapp/admin/admin.jsp
index 2c03e03..fa80b0b 100755
--- a/examples/demo-template/customer-app/src/main/webapp/admin/admin.jsp
+++ b/examples/demo-template/customer-app/src/main/webapp/admin/admin.jsp
@@ -1,3 +1,5 @@
+<%@ page import="org.keycloak.representations.idm.RoleRepresentation" %>
+<%@ page import="org.keycloak.example.AdminClient" %>
 <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
          pageEncoding="ISO-8859-1" %>
 <html>
@@ -8,5 +10,24 @@
 <h1>Customer Admin Interface</h1>
 User <b><%=request.getUserPrincipal().getName()%>
 </b> made this request.
-</body>
+<p>
+
+</p>
+<h2>Admin REST To Get Role List of Realm</h2>
+<%
+    java.util.List<RoleRepresentation> list = null;
+    try {
+        list = AdminClient.getRealmRoles(request);
+    } catch (AdminClient.Failure failure) {
+        out.println("There was a failure processing request.  You either didn't configure Keycloak properly");
+        out.println("Status from database service invocation was: " + failure.getStatus());
+        return;
+    }
+    for (RoleRepresentation role : list) {
+        out.print("<p>");
+        out.print(role.getName());
+        out.println("</p>");
+
+    }
+%></body>
 </html>
\ No newline at end of file
diff --git a/examples/demo-template/pom.xml b/examples/demo-template/pom.xml
index e66ed20..06edb1f 100755
--- a/examples/demo-template/pom.xml
+++ b/examples/demo-template/pom.xml
@@ -38,6 +38,7 @@
         <module>customer-app-cli</module>
         <module>customer-app-js</module>
         <module>product-app</module>
+        <module>admin-access-app</module>
         <module>angular-product-app</module>
         <module>database-service</module>
         <module>third-party</module>
diff --git a/examples/demo-template/README.md b/examples/demo-template/README.md
index 5f64ceb..2aa1894 100755
--- a/examples/demo-template/README.md
+++ b/examples/demo-template/README.md
@@ -6,6 +6,7 @@ The following examples requires Wildfly 8.0.0, JBoss EAP 6.x, or JBoss AS 7.1.1.
 * Transferring identity and role mappings via a special bearer token (Skeleton Key Token).
 * Bearer token authentication and authorization of JAX-RS services
 * Obtaining bearer tokens via the OAuth2 protocol
+* Interact with the Keycloak Admin REST Api
 
 There are multiple WAR projects.  These will all run on the same WildFly instance, but pretend each one is running on a different
 machine on the network or Internet.
@@ -13,6 +14,7 @@ machine on the network or Internet.
 * **customer-app-js** A pure HTML/Javascript application that does remote login using OAuth2 browser redirects with the auth server
 * **customer-app-cli** A pure CLI application that does remote login using OAuth2 browser redirects with the auth server
 * **product-app** A WAR application that does remote login using OAuth2 browser redirects with the auth server
+* **admin-access-app** A WAR application that does remote REST login to admin console to obtain a list of realm roles from Admin REST API
 * **database-service** JAX-RS services authenticated by bearer tokens only. The customer and product app invoke on it to get data
 * **third-party** Simple WAR that obtain a bearer token using OAuth2 using browser redirects to the auth-server.
 * **third-party-cdi** Simple CDI/JSF WAR that obtain a bearer token using OAuth2 using browser redirects to the auth-server.
@@ -184,6 +186,16 @@ The CLI example has two alternative methods for login. When a browser is availab
 temporary web server on a free port. If a browser is not available the URL to login is displayed on the CLI. The user can copy this URL to another computer that has a browser available. The code
 is displayed to the user after login and the user has to copy this code back to the application.
 
+Step 8: Admin REST API
+----------------------------------
+Keycloak has a Admin REST API.  This example shows an application making a remove direct login to Keycloak to obtain a token
+then using that token to access the Admin REST API.
+
+[http://localhost:8080/admin-access](http://localhost:8080/admin-access)
+
+If you are already logged in, you will not be asked for a username and password, but you will be redirected to
+an oauth grant page.  This page asks you if you want to grant certain permissions to the third-part app.
+
 Admin Console
 ==========================
 
diff --git a/examples/demo-template/testrealm.json b/examples/demo-template/testrealm.json
index 64abffe..a99aa9d 100755
--- a/examples/demo-template/testrealm.json
+++ b/examples/demo-template/testrealm.json
@@ -4,6 +4,7 @@
     "accessTokenLifespan": 3000,
     "accessCodeLifespan": 10,
     "accessCodeLifespanUserAction": 6000,
+    "passwordCredentialGrantAllowed": true,
     "sslNotRequired": true,
     "registrationAllowed": false,
     "social": false,
@@ -22,6 +23,17 @@
                 { "type" : "password",
                     "value" : "password" }
             ]
+        },
+        {
+            "username" : "admin",
+            "enabled": true,
+            "email" : "admin@admin.com",
+            "firstName": "Admin",
+            "lastName": "Burke",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
         }
     ],
     "roles" : {
@@ -40,6 +52,10 @@
         {
             "username": "bburke@redhat.com",
             "roles": ["user"]
+        },
+        {
+            "username": "admin",
+            "roles": ["user","admin"]
         }
     ],
     "scopeMappings": [
@@ -123,6 +139,13 @@
                 "http://localhost:8080/oauth-client-cdi/*"
             ],
             "secret": "password"
+        },
+        {
+            "name": "admin-client",
+            "enabled": true,
+            "publicClient": true,
+            "directGrantsOnly": true
+
         }
     ],
     "applicationRoleMappings": {
@@ -131,7 +154,22 @@
                 "username": "bburke@redhat.com",
                 "roles": ["manage-account"]
             }
+        ],
+        "realm-management": [
+            {
+                "username": "admin",
+                "roles": ["realm-admin"]
+            }
+        ]
+    },
+    "applicationScopeMappings": {
+        "realm-management": [
+            {
+                "client": "admin-client",
+                "roles": ["realm-admin"]
+            }
         ]
     }
 
+
 }
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
index 3438d6b..8053a5c 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
@@ -489,9 +489,6 @@ module.config([ '$routeProvider', function($routeProvider) {
                 },
                 applications : function(ApplicationListLoader) {
                     return ApplicationListLoader();
-                },
-                roles : function(RoleListLoader) {
-                    return RoleListLoader();
                 }
             },
             controller : 'ApplicationScopeMappingCtrl'
@@ -603,9 +600,6 @@ module.config([ '$routeProvider', function($routeProvider) {
                 },
                 applications : function(ApplicationListLoader) {
                     return ApplicationListLoader();
-                },
-                roles : function(RoleListLoader) {
-                    return RoleListLoader();
                 }
             },
             controller : 'OAuthClientScopeMappingCtrl'
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
index 0f9d457..abcaefc 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
@@ -198,7 +198,7 @@ module.controller('ApplicationInstallationCtrl', function($scope, realm, applica
 module.controller('ApplicationDetailCtrl', function($scope, realm, application, Application, $location, Dialog, Notifications) {
     console.log('ApplicationDetailCtrl');
 
-    $scope.clientTypes = [
+    $scope.accessTypes = [
         "confidential",
         "public",
         "bearer-only"
@@ -208,28 +208,27 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application, 
     $scope.create = !application.name;
     if (!$scope.create) {
         $scope.application= angular.copy(application);
-        $scope.clientType = $scope.clientTypes[0];
+        $scope.accessType = $scope.accessTypes[0];
         if (application.bearerOnly) {
-            $scope.clientType = $scope.clientTypes[2];
+            $scope.accessType = $scope.accessTypes[2];
         } else if (application.publicClient) {
-            $scope.clientType = $scope.clientTypes[1];
+            $scope.accessType = $scope.accessTypes[1];
         }
     } else {
         $scope.application = { enabled: true };
         $scope.application.webOrigins = [];
         $scope.application.redirectUris = [];
-        $scope.clientType = $scope.clientTypes[0];
+        $scope.accessType = $scope.accessTypes[0];
     }
 
-    $scope.changeClientType = function() {
-        console.log('Client Type: ' + $scope.clientType);
-        if ($scope.clientType == "confidential") {
+    $scope.changeAccessType = function() {
+        if ($scope.accessType == "confidential") {
             $scope.application.bearerOnly = false;
             $scope.application.publicClient = false;
-        } else if ($scope.clientType == "public") {
+        } else if ($scope.accessType == "public") {
             $scope.application.bearerOnly = false;
             $scope.application.publicClient = true;
-        } else if ($scope.clientType == "bearer-only") {
+        } else if ($scope.accessType == "bearer-only") {
             $scope.application.bearerOnly = true;
             $scope.application.publicClient = false;
         }
@@ -267,6 +266,10 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application, 
             Notifications.error("You must specify at least one redirect uri");
         } else {
             if ($scope.create) {
+                if ($scope.application.webOrigins.length == 0) {
+                    // let rest api put in default webOrigins
+                    $scope.application.webOrigins = null;
+                }
                 Application.save({
                     realm: realm.realm,
                     application: ''
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/oauth-clients.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/oauth-clients.js
index 6fb9744..9241e4b 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/oauth-clients.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/oauth-clients.js
@@ -77,16 +77,15 @@ module.controller('OAuthClientDetailCtrl', function($scope, realm, oauth, OAuthC
     $scope.realm = realm;
     $scope.create = !oauth.id;
 
-    $scope.clientTypes = [
+    $scope.accessTypes = [
         "confidential",
         "public"
     ];
 
-    $scope.changeClientType = function() {
-        console.log('Client Type: ' + $scope.clientType);
-        if ($scope.clientType == "confidential") {
+    $scope.changeAccessType = function() {
+        if ($scope.accessType == "confidential") {
             $scope.oauth.publicClient = false;
-        } else if ($scope.clientType == "public") {
+        } else if ($scope.accessType == "public") {
             $scope.oauth.publicClient = true;
         }
     };
@@ -94,15 +93,15 @@ module.controller('OAuthClientDetailCtrl', function($scope, realm, oauth, OAuthC
 
     if (!$scope.create) {
         $scope.oauth= angular.copy(oauth);
-        $scope.clientType = $scope.clientTypes[0];
+        $scope.accessType = $scope.accessTypes[0];
         if (oauth.publicClient) {
-            $scope.clientType = $scope.clientTypes[1];
+            $scope.accessType = $scope.accessTypes[1];
         }
     } else {
         $scope.oauth = { enabled: true };
         $scope.oauth.webOrigins = [];
         $scope.oauth.redirectUris = [];
-        $scope.clientType = $scope.clientTypes[0];
+        $scope.accessType = $scope.accessTypes[0];
     }
 
     $scope.$watch(function() {
@@ -133,7 +132,7 @@ module.controller('OAuthClientDetailCtrl', function($scope, realm, oauth, OAuthC
     }
 
     $scope.save = function() {
-        if (!$scope.oauth.redirectUris || $scope.oauth.redirectUris.length == 0) {
+        if (!$scope.oauth.directGrantsOnly && (!$scope.oauth.redirectUris || $scope.oauth.redirectUris.length == 0)) {
             Notifications.error("You must specify at least one redirect uri");
         } else {
             if ($scope.create) {
@@ -183,128 +182,89 @@ module.controller('OAuthClientDetailCtrl', function($scope, realm, oauth, OAuthC
 
 });
 
-module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm, oauth, roles, applications, OAuthClientRealmScopeMapping, OAuthClientApplicationScopeMapping, ApplicationRole) {
+module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm, oauth, applications,
+                                                          OAuthClientRealmScopeMapping, OAuthClientApplicationScopeMapping, ApplicationRole,
+                                                          OAuthClientAvailableRealmScopeMapping, OAuthClientAvailableApplicationScopeMapping,
+                                                          OAuthClientCompositeRealmScopeMapping, OAuthClientCompositeApplicationScopeMapping) {
     $scope.realm = realm;
     $scope.oauth = oauth;
-    $scope.realmRoles = angular.copy(roles);
     $scope.selectedRealmRoles = [];
     $scope.selectedRealmMappings = [];
     $scope.realmMappings = [];
     $scope.applications = applications;
     $scope.applicationRoles = [];
+    $scope.applicationComposite = [];
     $scope.selectedApplicationRoles = [];
     $scope.selectedApplicationMappings = [];
     $scope.applicationMappings = [];
+    $scope.dummymodel = [];
 
+    function updateRealmRoles() {
+        $scope.realmRoles = OAuthClientAvailableRealmScopeMapping.query({realm : realm.realm, oauth : oauth.id});
+        $scope.realmMappings = OAuthClientRealmScopeMapping.query({realm : realm.realm, oauth : oauth.id});
+        $scope.realmComposite = OAuthClientCompositeRealmScopeMapping.query({realm : realm.realm, oauth : oauth.id});
+    }
 
-
-    $scope.realmMappings = OAuthClientRealmScopeMapping.query({realm : realm.realm, oauth : oauth.id}, function(){
-        for (var i = 0; i < $scope.realmMappings.length; i++) {
-            var role = $scope.realmMappings[i];
-            for (var j = 0; j < $scope.realmRoles.length; j++) {
-                var realmRole = $scope.realmRoles[j];
-                if (realmRole.id == role.id) {
-                    var idx = $scope.realmRoles.indexOf(realmRole);
-                    if (idx != -1) {
-                        $scope.realmRoles.splice(idx, 1);
-                        break;
-                    }
-                }
-            }
+    function updateAppRoles() {
+        if ($scope.targetApp) {
+            console.debug($scope.targetApp.name);
+            $scope.applicationRoles = OAuthClientAvailableApplicationScopeMapping.query({realm : realm.realm, oauth : oauth.id, targetApp : $scope.targetApp.name});
+            $scope.applicationMappings = OAuthClientApplicationScopeMapping.query({realm : realm.realm, oauth : oauth.id, targetApp : $scope.targetApp.name});
+            $scope.applicationComposite = OAuthClientCompositeApplicationScopeMapping.query({realm : realm.realm, oauth : oauth.id, targetApp : $scope.targetApp.name});
+        } else {
+            $scope.applicationRoles = null;
+            $scope.applicationMappings = null;
+            $scope.applicationComposite = null;
         }
-    });
+    }
 
     $scope.addRealmRole = function() {
-        $http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/scope-mappings/realm',
-                $scope.selectedRealmRoles).success(function() {
-                for (var i = 0; i < $scope.selectedRealmRoles.length; i++) {
-                    var role = $scope.selectedRealmRoles[i];
-                    var idx = $scope.realmRoles.indexOf($scope.selectedRealmRoles[i]);
-                    if (idx != -1) {
-                        $scope.realmRoles.splice(idx, 1);
-                        $scope.realmMappings.push(role);
-                    }
-                }
-                $scope.selectRealmRoles = [];
-            });
+        $http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/scope-mappings/realm', $scope.selectedRealmRoles)
+            .success(updateRealmRoles);
     };
 
     $scope.deleteRealmRole = function() {
         $http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id +  '/scope-mappings/realm',
-            {data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function() {
-                for (var i = 0; i < $scope.selectedRealmMappings.length; i++) {
-                    var role = $scope.selectedRealmMappings[i];
-                    var idx = $scope.realmMappings.indexOf($scope.selectedRealmMappings[i]);
-                    if (idx != -1) {
-                        $scope.realmMappings.splice(idx, 1);
-                        $scope.realmRoles.push(role);
-                    }
-                }
-                $scope.selectedRealmMappings = [];
-            });
+            {data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}})
+            .success(updateRealmRoles);
     };
 
     $scope.addApplicationRole = function() {
         $http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id +  '/scope-mappings/applications/' + $scope.targetApp.name,
-                $scope.selectedApplicationRoles).success(function() {
-                for (var i = 0; i < $scope.selectedApplicationRoles.length; i++) {
-                    var role = $scope.selectedApplicationRoles[i];
-                    var idx = $scope.applicationRoles.indexOf($scope.selectedApplicationRoles[i]);
-                    if (idx != -1) {
-                        $scope.applicationRoles.splice(idx, 1);
-                        $scope.applicationMappings.push(role);
-                    }
-                }
-                $scope.selectedApplicationRoles = [];
-            });
+            $scope.selectedApplicationRoles).success(updateAppRoles);
     };
 
     $scope.deleteApplicationRole = function() {
         $http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id +  '/scope-mappings/applications/' + $scope.targetApp.name,
-            {data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function() {
-                for (var i = 0; i < $scope.selectedApplicationMappings.length; i++) {
-                    var role = $scope.selectedApplicationMappings[i];
-                    var idx = $scope.applicationMappings.indexOf($scope.selectedApplicationMappings[i]);
-                    if (idx != -1) {
-                        $scope.applicationMappings.splice(idx, 1);
-                        $scope.applicationRoles.push(role);
-                    }
-                }
-                $scope.selectedApplicationMappings = [];
-            });
+            {data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(updateAppRoles);
     };
 
-
     $scope.changeApplication = function() {
-        if ($scope.targetApp) {
-            $scope.applicationRoles = ApplicationRole.query({realm : realm.realm, application : $scope.targetApp.name}, function() {
-                    $scope.applicationMappings = OAuthClientApplicationScopeMapping.query({realm : realm.realm, oauth : oauth.id, targetApp : $scope.targetApp.name}, function(){
-                        for (var i = 0; i < $scope.applicationMappings.length; i++) {
-                            var role = $scope.applicationMappings[i];
-                            for (var j = 0; j < $scope.applicationRoles.length; j++) {
-                                var realmRole = $scope.applicationRoles[j];
-                                if (realmRole.id == role.id) {
-                                    var idx = $scope.applicationRoles.indexOf(realmRole);
-                                    if (idx != -1) {
-                                        $scope.applicationRoles.splice(idx, 1);
-                                        break;
-                                    }
-                                }
-                            }
-                        }
-                    });
-
-                }
-            );
-        } else {
-            $scope.targetApp = null;
-        }
+        updateAppRoles();
     };
 
+    $scope.addRealmRole = function() {
+        $http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id + '/scope-mappings/realm',
+            $scope.selectedRealmRoles).success(updateRealmRoles);
+    };
 
+    $scope.deleteRealmRole = function() {
+        $http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id +  '/scope-mappings/realm',
+            {data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(updateRealmRoles);
+    };
 
-});
+    $scope.addApplicationRole = function() {
+        $http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id +  '/scope-mappings/applications/' + $scope.targetApp.name,
+            $scope.selectedApplicationRoles).success(updateAppRoles);
+    };
 
+    $scope.deleteApplicationRole = function() {
+        $http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.id +  '/scope-mappings/applications/' + $scope.targetApp.name,
+            {data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(updateAppRoles);
+    };
+
+    updateRealmRoles();
+});
 
 module.controller('OAuthClientInstallationCtrl', function($scope, realm, installation, oauth, OAuthClientInstallation, $routeParams) {
     $scope.realm = realm;
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
index 3a48af6..979199a 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
@@ -764,6 +764,20 @@ module.factory('OAuthClientRealmScopeMapping', function($resource) {
     });
 });
 
+module.factory('OAuthClientCompositeRealmScopeMapping', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/oauth-clients/:oauth/scope-mappings/realm/composite', {
+        realm : '@realm',
+        oauth : '@oauth'
+    });
+});
+
+module.factory('OAuthClientAvailableRealmScopeMapping', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/oauth-clients/:oauth/scope-mappings/realm/available', {
+        realm : '@realm',
+        oauth : '@oauth'
+    });
+});
+
 module.factory('OAuthClientApplicationScopeMapping', function($resource) {
     return $resource(authUrl + '/admin/realms/:realm/oauth-clients/:oauth/scope-mappings/applications/:targetApp', {
         realm : '@realm',
@@ -772,6 +786,24 @@ module.factory('OAuthClientApplicationScopeMapping', function($resource) {
     });
 });
 
+module.factory('OAuthClientCompositeApplicationScopeMapping', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/oauth-clients/:oauth/scope-mappings/applications/:targetApp/composite', {
+        realm : '@realm',
+        oauth : '@oauth',
+        targetApp : '@targetApp'
+    });
+});
+
+module.factory('OAuthClientAvailableApplicationScopeMapping', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/oauth-clients/:oauth/scope-mappings/applications/:targetApp/available', {
+        realm : '@realm',
+        oauth : '@oauth',
+        targetApp : '@targetApp'
+    });
+});
+
+
+
 module.factory('OAuthClientInstallation', function($resource) {
     var url = authUrl + '/admin/realms/:realm/oauth-clients/:oauth/installation';
     var resource = $resource(authUrl + '/admin/realms/:realm/oauth-clients/:oauth/installation', {
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
index 9496bf3..4d795a8 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
@@ -42,13 +42,13 @@
                     </div>
                 </div>
                 <div class="form-group">
-                    <label class="col-sm-2 control-label" for="clientType">Client Type</label>
+                    <label class="col-sm-2 control-label" for="accessType">Access Type</label>
                     <div class="col-sm-4">
                         <div class="select-kc">
-                            <select id="clientType"
-                                    ng-change="changeClientType()"
-                                    ng-model="clientType"
-                                    ng-options="cType for cType in clientTypes">
+                            <select id="accessType"
+                                    ng-change="changeAccessType()"
+                                    ng-model="accessType"
+                                    ng-options="aType for aType in accessTypes">
                             </select>
                         </div>
                     </div>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-scope-mappings.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-scope-mappings.html
index 64fa1cc..ba1f809 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-scope-mappings.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-scope-mappings.html
@@ -49,9 +49,9 @@
                                     ng-model="selectedRealmMappings"
                                     ng-options="r.name for r in realmMappings">
                             </select>
-                            <div class="middle-buttons">
-                                -
-                            </div>
+                        </div>
+                        <div class="middle-buttons">
+                            -
                         </div>
                         <div class="select-title">
                             <label class="control-label" for="realm-composite">Effective Roles</label>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-detail.html
index f1ed7f8..c5154d8 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-detail.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-detail.html
@@ -41,18 +41,24 @@
                     </div>
                 </div>
                 <div class="form-group">
-                    <label class="col-sm-2 control-label" for="clientType">Client Type</label>
+                    <label class="col-sm-2 control-label" for="accessType">Access Type</label>
                     <div class="col-sm-4">
                         <div class="select-kc">
-                            <select id="clientType"
-                                    ng-change="changeClientType()"
-                                    ng-model="clientType"
-                                    ng-options="cType for cType in clientTypes">
+                            <select id="accessType"
+                                    ng-change="changeAccessType()"
+                                    ng-model="accessType"
+                                    ng-options="aType for aType in accessTypes">
                             </select>
                         </div>
                     </div>
                 </div>
-                <div class="form-group">
+                <div class="form-group clearfix block">
+                    <label class="col-sm-2 control-label" for="directGrantsOnly">Direct Grants Only</label>
+                    <div class="col-sm-4">
+                        <input ng-model="oauth.directGrantsOnly" name="directGrantsOnly" id="directGrantsOnly" onoffswitch />
+                    </div>
+                </div>
+                <div class="form-group" data-ng-hide="oauth.directGrantsOnly">
                     <label class="col-sm-2 control-label" for="newRedirectUri">Redirect URI <span class="required" data-ng-show="create">*</span></label>
                     <div class="col-sm-4 multiple" ng-repeat="redirectUri in oauth.redirectUris">
                         <div class="input-group kc-item-deletable">
@@ -75,7 +81,7 @@
                         </div>
                     </div>
                 </div>
-                <div class="form-group">
+                <div class="form-group" data-ng-hide="create">
                     <label class="col-sm-2 control-label" for="newWebOrigin">Web Origin</label>
                     <div class="col-sm-4 multiple" ng-repeat="webOrigin in oauth.webOrigins">
                         <div class="input-group kc-item-deletable">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-scope-mappings.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-scope-mappings.html
index 5a5e2ab..67bc416 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-scope-mappings.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-scope-mappings.html
@@ -48,6 +48,17 @@
                                     ng-options="r.name for r in realmMappings">
                             </select>
                         </div>
+                        <div class="middle-buttons">
+                            -
+                        </div>
+                        <div class="select-title">
+                            <label class="control-label" for="realm-composite">Effective Roles</label>
+                            <select id="realm-composite" class="form-control" multiple size=5
+                                    ng-disabled="true"
+                                    ng-model="dummymodel"
+                                    ng-options="r.name for r in realmComposite">
+                            </select>
+                        </div>
                     </div>
                 </div>
             </fieldset>
@@ -92,6 +103,17 @@
                                     ng-options="r.name for r in applicationMappings">
                             </select>
                         </div>
+                        <div class="middle-buttons">
+                            -
+                        </div>
+                        <div class="select-title">
+                            <label class="control-label" for="app-composite">Effective Roles</label>
+                            <select id="app-composite" class="form-control" multiple size=5
+                                    ng-disabled="true"
+                                    ng-model="dummymodel"
+                                    ng-options="r.name for r in applicationComposite">
+                            </select>
+                        </div>
                     </div>
                 </div>
             </fieldset>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html
index 089b190..96b6d5f 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-detail.html
@@ -67,7 +67,7 @@
                         </div>
                     </div>
                     <div class="form-group">
-                        <label for="passwordCredentialGrantAllowed" class="col-sm-2 control-label">Password Credential Grant</label>
+                        <label for="passwordCredentialGrantAllowed" class="col-sm-2 control-label">Direct Grant API</label>
                         <div class="col-sm-4">
                             <input ng-model="realm.passwordCredentialGrantAllowedpasswordCredentialGrantAllowed" name="passwordCredentialGrantAllowed" id="passwordCredentialGrantAllowed" onoffswitch />
                         </div>
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
index 4eb5f88..72a0493 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
@@ -88,8 +88,8 @@ public abstract class RequestAuthenticator {
     }
 
     protected void completeAuthentication(OAuthRequestAuthenticator oauth) {
-        final KeycloakPrincipal principal = new KeycloakPrincipal(oauth.getToken().getSubject(), null);
         RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken());
+        final KeycloakPrincipal principal = new KeycloakPrincipal(oauth.getToken().getSubject(), session);
         completeOAuthAuthentication(principal, session);
     }
 
@@ -98,8 +98,8 @@ public abstract class RequestAuthenticator {
     protected abstract boolean isCached();
 
     protected void completeAuthentication(BearerTokenRequestAuthenticator bearer) {
-        final KeycloakPrincipal principal = new KeycloakPrincipal(bearer.getToken().getSubject(), bearer.getSurrogate());
         RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, bearer.getTokenString(), bearer.getToken(), null, null, null);
+        final KeycloakPrincipal principal = new KeycloakPrincipal(bearer.getToken().getSubject(), session);
         completeBearerAuthentication(principal, session);
     }
 
diff --git a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
index 90af012..6b1d2d6 100755
--- a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
+++ b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilter.java
@@ -73,9 +73,8 @@ public class JaxrsBearerTokenFilter implements ContainerRequestFilter {
             AccessToken token = RSATokenVerifier.verifyToken(tokenString, realmPublicKey, realm);
             KeycloakSecurityContext skSession = new KeycloakSecurityContext(tokenString, token, null, null);
             ResteasyProviderFactory.pushContext(KeycloakSecurityContext.class, skSession);
-            String callerPrincipal = securityContext.getUserPrincipal() != null ? securityContext.getUserPrincipal().getName() : null;
 
-            final KeycloakPrincipal principal = new KeycloakPrincipal(token.getSubject(), callerPrincipal);
+            final KeycloakPrincipal principal = new KeycloakPrincipal(token.getSubject(), skSession);
             final boolean isSecure = securityContext.isSecure();
             final AccessToken.Access access;
             if (resourceName != null) {
diff --git a/model/api/src/main/java/org/keycloak/models/ClientModel.java b/model/api/src/main/java/org/keycloak/models/ClientModel.java
index d88812d..3784f9e 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientModel.java
@@ -54,6 +54,9 @@ public interface ClientModel {
     boolean isPublicClient();
     void setPublicClient(boolean flag);
 
+    boolean isDirectGrantsOnly();
+    void setDirectGrantsOnly(boolean flag);
+
     RealmModel getRealm();
 
     /**
diff --git a/model/api/src/main/java/org/keycloak/models/entities/OAuthClientEntity.java b/model/api/src/main/java/org/keycloak/models/entities/OAuthClientEntity.java
old mode 100644
new mode 100755
index 368fcf7..79a00b4
--- a/model/api/src/main/java/org/keycloak/models/entities/OAuthClientEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/OAuthClientEntity.java
@@ -4,4 +4,13 @@ package org.keycloak.models.entities;
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 public class OAuthClientEntity extends ClientEntity {
+    protected boolean directGrantsOnly;
+
+    public boolean isDirectGrantsOnly() {
+        return directGrantsOnly;
+    }
+
+    public void setDirectGrantsOnly(boolean directGrantsOnly) {
+        this.directGrantsOnly = directGrantsOnly;
+    }
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
index 6616c36..c2c684a 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
@@ -89,6 +89,16 @@ public class ApplicationAdapter extends ClientAdapter implements ApplicationMode
     }
 
     @Override
+    public boolean isDirectGrantsOnly() {
+        return false;  // applications can't be grant only
+    }
+
+    @Override
+    public void setDirectGrantsOnly(boolean flag) {
+        // applications can't be grant only
+    }
+
+    @Override
     public RoleModel getRole(String name) {
         TypedQuery<ApplicationRoleEntity> query = em.createNamedQuery("getAppRoleByName", ApplicationRoleEntity.class);
         query.setParameter("name", name);
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
index 99e5d1b..f16d955 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -18,7 +18,7 @@ import java.util.Set;
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public class ClientAdapter implements ClientModel {
+public abstract class ClientAdapter implements ClientModel {
     protected ClientEntity entity;
     protected RealmModel realm;
     protected EntityManager em;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java
index 2a57024..dbdf080 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java
@@ -15,4 +15,13 @@ import javax.persistence.NamedQuery;
 })
 @Entity
 public class OAuthClientEntity extends ClientEntity {
+    protected boolean directGrantsOnly;
+
+    public boolean isDirectGrantsOnly() {
+        return directGrantsOnly;
+    }
+
+    public void setDirectGrantsOnly(boolean directGrantsOnly) {
+        this.directGrantsOnly = directGrantsOnly;
+    }
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java
index cd78093..e6a62af 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java
@@ -15,8 +15,11 @@ import java.util.Set;
  */
 public class OAuthClientAdapter extends ClientAdapter implements OAuthClientModel {
 
+    protected final OAuthClientEntity oAuthClientEntity;
+
     public OAuthClientAdapter(RealmModel realm, OAuthClientEntity entity, EntityManager em) {
         super(realm, entity, em);
+        oAuthClientEntity = entity;
     }
 
     @Override
@@ -24,4 +27,14 @@ public class OAuthClientAdapter extends ClientAdapter implements OAuthClientMode
         entity.setName(id);
 
     }
+
+    @Override
+    public boolean isDirectGrantsOnly() {
+        return oAuthClientEntity.isDirectGrantsOnly();
+    }
+
+    @Override
+    public void setDirectGrantsOnly(boolean flag) {
+        oAuthClientEntity.setDirectGrantsOnly(flag);
+    }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
index b8dbd0a..68a2d72 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
@@ -98,6 +98,17 @@ public class ApplicationAdapter extends ClientAdapter<MongoApplicationEntity> im
     }
 
     @Override
+    public boolean isDirectGrantsOnly() {
+        return false;  // applications can't be grant only
+    }
+
+    @Override
+    public void setDirectGrantsOnly(boolean flag) {
+        // applications can't be grant only
+    }
+
+
+    @Override
     public RoleAdapter getRole(String name) {
         DBObject query = new QueryBuilder()
                 .and("name").is(name)
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
index 55c2bd9..9cde3e3 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java
@@ -18,7 +18,7 @@ import org.keycloak.models.mongo.keycloak.entities.MongoUserSessionEntity;
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class ClientAdapter<T extends MongoIdentifiableEntity> extends AbstractMongoAdapter<T> implements ClientModel {
+public abstract class ClientAdapter<T extends MongoIdentifiableEntity> extends AbstractMongoAdapter<T> implements ClientModel {
 
     protected final T clientEntity;
     private final RealmAdapter realm;
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
index d5d6532..11d5670 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
@@ -19,4 +19,14 @@ public class OAuthClientAdapter extends ClientAdapter<MongoOAuthClientEntity> im
         getMongoEntity().setName(id);
         updateMongoEntity();
     }
+
+    @Override
+    public boolean isDirectGrantsOnly() {
+        return getMongoEntity().isDirectGrantsOnly();
+    }
+
+    @Override
+    public void setDirectGrantsOnly(boolean flag) {
+        getMongoEntity().setDirectGrantsOnly(flag);
+    }
 }
diff --git a/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java b/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java
index 184de4e..56559cb 100755
--- a/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java
@@ -51,6 +51,7 @@ public class OAuthClientManager {
         if (rep.getName() != null) model.setClientId(rep.getName());
         if (rep.isEnabled() != null) model.setEnabled(rep.isEnabled());
         if (rep.isPublicClient() != null) model.setPublicClient(rep.isPublicClient());
+        if (rep.isDirectGrantsOnly() != null) model.setDirectGrantsOnly(rep.isDirectGrantsOnly());
         if (rep.getClaims() != null) {
             ClaimManager.setClaims(model, rep.getClaims());
         }
@@ -84,6 +85,7 @@ public class OAuthClientManager {
         rep.setName(model.getClientId());
         rep.setEnabled(model.isEnabled());
         rep.setPublicClient(model.isPublicClient());
+        rep.setDirectGrantsOnly(model.isDirectGrantsOnly());
         Set<String> redirectUris = model.getRedirectUris();
         if (redirectUris != null) {
             rep.setRedirectUris(new LinkedList<String>(redirectUris));
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 66a49e0..c897d77 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -114,10 +114,12 @@ public class RealmManager {
         RoleModel adminRole;
         if (realm.getName().equals(Config.getAdminRealm())) {
             adminRole = realm.getRole(AdminRoles.ADMIN);
-            realm.addScopeMapping(adminConsole, adminRole);
         } else {
-            // security roles are defined in application for the realm.
+            String realmAdminApplicationName = getRealmAdminApplicationName(realm);
+            ApplicationModel realmAdminApp = realm.getApplicationByName(realmAdminApplicationName);
+            adminRole = realmAdminApp.getRole(AdminRoles.REALM_ADMIN);
         }
+        realm.addScopeMapping(adminConsole, adminRole);
     }
 
     public String getMasterRealmAdminApplicationName(RealmModel realm) {
diff --git a/services/src/main/java/org/keycloak/services/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
index 89adfaf..daa7786 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -453,6 +453,7 @@ public class TokenManager {
                 String encodedToken = new JWSBuilder().jsonContent(accessToken).rsa256(realm.getPrivateKey());
                 res.setToken(encodedToken);
                 res.setTokenType("bearer");
+                res.setSessionState(accessToken.getSessionState());
                 if (accessToken.getExpiration() != 0) {
                     res.setExpiresIn(accessToken.getExpiration() - Time.currentTime());
                 }
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index 40af5a4..9f018c1 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -754,7 +754,11 @@ public class TokenService {
         }
         if ( (client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) {
             audit.error(Errors.NOT_ALLOWED);
-            return oauth.forwardToSecurityFailure("Bearer-only applications are not allowed to initiate login");
+            return oauth.forwardToSecurityFailure("Bearer-only applications are not allowed to initiate browser login");
+        }
+        if (client.isDirectGrantsOnly()) {
+            audit.error(Errors.NOT_ALLOWED);
+            return oauth.forwardToSecurityFailure("direct-grants-only clients are not allowed to initiate browser login");
         }
         redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
         if (redirect == null) {