keycloak-aplcache

[KEYCLOAK-3169] - UMA 2.0 (#4368) * [KEYCLOAK-3169] - UMA

2/28/2018 4:53:10 AM

Changes

authz/client/src/main/java/org/keycloak/authorization/client/representation/AuthorizationRequest.java 48(+0 -48)

authz/client/src/main/java/org/keycloak/authorization/client/representation/EntitlementRequest.java 84(+0 -84)

authz/client/src/main/java/org/keycloak/authorization/client/representation/EntitlementResponse.java 42(+0 -42)

authz/client/src/main/java/org/keycloak/authorization/client/representation/ErrorResponse.java 60(+0 -60)

authz/client/src/main/java/org/keycloak/authorization/client/representation/PermissionRequest.java 79(+0 -79)

authz/client/src/main/java/org/keycloak/authorization/client/representation/RegistrationResponse.java 49(+0 -49)

authz/client/src/main/java/org/keycloak/authorization/client/resource/EntitlementResource.java 43(+0 -43)

services/src/main/java/org/keycloak/authorization/authorization/representation/AuthorizationRequest.java 50(+0 -50)

services/src/main/java/org/keycloak/authorization/authorization/representation/AuthorizationResponse.java 43(+0 -43)

services/src/main/java/org/keycloak/authorization/config/Configuration.java 269(+0 -269)

services/src/main/java/org/keycloak/authorization/entitlement/EntitlementService.java 334(+0 -334)

services/src/main/java/org/keycloak/authorization/entitlement/representation/EntitlementRequest.java 78(+0 -78)

services/src/main/java/org/keycloak/authorization/entitlement/representation/EntitlementResponse.java 42(+0 -42)

Details

diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
index 15aa1e1..96fbe5d 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
@@ -18,7 +18,6 @@
 package org.keycloak.adapters.authorization;
 
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -65,54 +64,58 @@ public abstract class AbstractPolicyEnforcer {
             return createEmptyAuthorizationContext(true);
         }
 
+        Request request = httpFacade.getRequest();
+        String path = getPath(request);
+        PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
         KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
 
-        if (securityContext != null) {
-            AccessToken accessToken = securityContext.getToken();
+        if (securityContext == null) {
+            if (pathConfig != null) {
+                challenge(pathConfig, getRequiredScopes(pathConfig, request), httpFacade);
+            }
+            return createEmptyAuthorizationContext(false);
+        }
 
-            if (accessToken != null) {
-                Request request = httpFacade.getRequest();
-                String path = getPath(request);
-                PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
+        AccessToken accessToken = securityContext.getToken();
 
-                LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
+        if (accessToken != null) {
+            LOGGER.debugf("Checking permissions for path [%s] with config [%s].", request.getURI(), pathConfig);
 
-                if (pathConfig == null) {
-                    if (EnforcementMode.PERMISSIVE.equals(enforcementMode)) {
-                        return createAuthorizationContext(accessToken, null);
-                    }
+            if (pathConfig == null) {
+                if (EnforcementMode.PERMISSIVE.equals(enforcementMode)) {
+                    return createAuthorizationContext(accessToken, null);
+                }
 
-                    LOGGER.debugf("Could not find a configuration for path [%s]", path);
+                LOGGER.debugf("Could not find a configuration for path [%s]", path);
 
-                    if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
-                        return createAuthorizationContext(accessToken, null);
-                    }
+                if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
+                    return createAuthorizationContext(accessToken, null);
+                }
 
-                    handleAccessDenied(httpFacade);
+                handleAccessDenied(httpFacade);
 
-                    return createEmptyAuthorizationContext(false);
-                }
+                return createEmptyAuthorizationContext(false);
+            }
 
-                if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) {
-                    return createEmptyAuthorizationContext(true);
-                }
+            if (EnforcementMode.DISABLED.equals(pathConfig.getEnforcementMode())) {
+                return createEmptyAuthorizationContext(true);
+            }
 
-                MethodConfig methodConfig = getRequiredScopes(pathConfig, request);
+            MethodConfig methodConfig = getRequiredScopes(pathConfig, request);
 
-                if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
-                    try {
-                        return createAuthorizationContext(accessToken, pathConfig);
-                    } catch (Exception e) {
-                        throw new RuntimeException("Error processing path [" + pathConfig.getPath() + "].", e);
-                    }
+            if (isAuthorized(pathConfig, methodConfig, accessToken, httpFacade)) {
+                try {
+                    return createAuthorizationContext(accessToken, pathConfig);
+                } catch (Exception e) {
+                    throw new RuntimeException("Error processing path [" + pathConfig.getPath() + "].", e);
                 }
+            }
 
-                LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
+            LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
 
-                if (!challenge(pathConfig, methodConfig, httpFacade)) {
-                    LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
-                    handleAccessDenied(httpFacade);
-                }
+            if (!challenge(pathConfig, methodConfig, httpFacade)) {
+                LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
+                handleAccessDenied(httpFacade);
             }
         }
 
@@ -139,7 +142,7 @@ public abstract class AbstractPolicyEnforcer {
         boolean hasPermission = false;
 
         for (Permission permission : permissions) {
-            if (permission.getResourceSetId() != null) {
+            if (permission.getResourceId() != null) {
                 if (isResourcePermission(actualPathConfig, permission)) {
                     hasPermission = true;
 
@@ -292,6 +295,6 @@ public abstract class AbstractPolicyEnforcer {
     }
 
     private boolean matchResourcePermission(PathConfig actualPathConfig, Permission permission) {
-        return permission.getResourceSetId().equals(actualPathConfig.getId());
+        return permission.getResourceId().equals(actualPathConfig.getId());
     }
 }
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java
index 172c745..9e29735 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/BearerTokenPolicyEnforcer.java
@@ -23,11 +23,11 @@ import org.jboss.logging.Logger;
 import org.keycloak.adapters.OIDCHttpFacade;
 import org.keycloak.adapters.spi.HttpFacade;
 import org.keycloak.authorization.client.AuthzClient;
-import org.keycloak.authorization.client.representation.PermissionRequest;
 import org.keycloak.authorization.client.resource.PermissionResource;
 import org.keycloak.authorization.client.resource.ProtectionResource;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -42,45 +42,40 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
 
     @Override
     protected boolean challenge(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) {
-        if (getEnforcerConfig().getUserManagedAccess() != null) {
-            challengeUmaAuthentication(pathConfig, methodConfig, facade);
-        } else {
-            challengeEntitlementAuthentication(facade);
-        }
-        return true;
-    }
-
-    private void challengeEntitlementAuthentication(OIDCHttpFacade facade) {
         HttpFacade.Response response = facade.getResponse();
         AuthzClient authzClient = getAuthzClient();
-        String clientId = authzClient.getConfiguration().getResource();
-        String  authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/entitlement";
-        response.setStatus(401);
-        response.setHeader("WWW-Authenticate", "KC_ETT realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\"");
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("Sending Entitlement challenge");
+        String ticket = getPermissionTicket(pathConfig, methodConfig, authzClient);
+
+        if (ticket == null) {
+            response.setStatus(403);
+            return true;
         }
-    }
 
-    private void challengeUmaAuthentication(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade facade) {
-        HttpFacade.Response response = facade.getResponse();
-        AuthzClient authzClient = getAuthzClient();
-        String ticket = getPermissionTicket(pathConfig, methodConfig, authzClient);
-        String clientId = authzClient.getConfiguration().getResource();
-        String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/authorize";
+        String realm = authzClient.getConfiguration().getRealm();
+        String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString();
         response.setStatus(401);
-        response.setHeader("WWW-Authenticate", "UMA realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\",ticket=\"" + ticket + "\"");
+        StringBuilder wwwAuthenticate = new StringBuilder("UMA realm=\"").append(realm).append("\"").append(",as_uri=\"").append(authorizationServerUri).append("\"");
+
+        if (ticket != null) {
+            wwwAuthenticate.append(",ticket=\"").append(ticket).append("\"");
+        }
+
+        response.setHeader("WWW-Authenticate", wwwAuthenticate.toString());
         if (LOGGER.isDebugEnabled()) {
             LOGGER.debug("Sending UMA challenge");
         }
+        return true;
     }
 
     private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient) {
-        ProtectionResource protection = authzClient.protection();
-        PermissionResource permission = protection.permission();
-        PermissionRequest permissionRequest = new PermissionRequest();
-        permissionRequest.setResourceSetId(pathConfig.getId());
-        permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
-        return permission.forResource(permissionRequest).getTicket();
+        if (getEnforcerConfig().getUserManagedAccess() != null) {
+            ProtectionResource protection = authzClient.protection();
+            PermissionResource permission = protection.permission();
+            PermissionRequest permissionRequest = new PermissionRequest();
+            permissionRequest.setResourceId(pathConfig.getId());
+            permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
+            return permission.create(permissionRequest).getTicket();
+        }
+        return null;
     }
 }
\ No newline at end of file
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
index 65fdc1e..0732ee9 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
@@ -19,25 +19,23 @@ package org.keycloak.adapters.authorization;
 
 import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.Set;
 
 import org.jboss.logging.Logger;
+import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.adapters.KeycloakDeployment;
 import org.keycloak.adapters.OIDCHttpFacade;
 import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
 import org.keycloak.adapters.spi.HttpFacade;
 import org.keycloak.authorization.client.AuthorizationDeniedException;
 import org.keycloak.authorization.client.AuthzClient;
-import org.keycloak.authorization.client.representation.AuthorizationRequest;
-import org.keycloak.authorization.client.representation.AuthorizationResponse;
-import org.keycloak.authorization.client.representation.EntitlementRequest;
-import org.keycloak.authorization.client.representation.EntitlementResponse;
-import org.keycloak.authorization.client.representation.PermissionRequest;
-import org.keycloak.authorization.client.representation.PermissionResponse;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.Permission;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
+import org.keycloak.representations.idm.authorization.PermissionResponse;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -90,6 +88,12 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
 
     @Override
     protected void handleAccessDenied(OIDCHttpFacade facade) {
+        KeycloakSecurityContext securityContext = facade.getSecurityContext();
+
+        if (securityContext == null) {
+            return;
+        }
+
         String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo();
         HttpFacade.Response response = facade.getResponse();
 
@@ -103,45 +107,41 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
 
     private AccessToken requestAuthorizationToken(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, OIDCHttpFacade httpFacade) {
         try {
-            String accessToken = httpFacade.getSecurityContext().getTokenString();
+            KeycloakSecurityContext securityContext = httpFacade.getSecurityContext();
+            String accessTokenString = securityContext.getTokenString();
             AuthzClient authzClient = getAuthzClient();
             KeycloakDeployment deployment = getPolicyEnforcer().getDeployment();
+            PermissionRequest permissionRequest = new PermissionRequest();
 
-            if (getEnforcerConfig().getUserManagedAccess() != null) {
-                LOGGER.debug("Obtaining authorization for authenticated user.");
-                PermissionRequest permissionRequest = new PermissionRequest();
-
-                permissionRequest.setResourceSetId(pathConfig.getId());
-                permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
-
-                PermissionResponse permissionResponse = authzClient.protection().permission().forResource(permissionRequest);
-                AuthorizationRequest authzRequest = new AuthorizationRequest(permissionResponse.getTicket());
-                AuthorizationResponse authzResponse = authzClient.authorization(accessToken).authorize(authzRequest);
+            permissionRequest.setResourceId(pathConfig.getId());
+            permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
 
-                if (authzResponse != null) {
-                    return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
-                }
+            AccessToken accessToken = securityContext.getToken();
+            AuthorizationRequest authzRequest;
 
-                return null;
+            if (getEnforcerConfig().getUserManagedAccess() != null) {
+                PermissionResponse permissionResponse = authzClient.protection().permission().create(permissionRequest);
+                authzRequest = new AuthorizationRequest();
+                authzRequest.setTicket(permissionResponse.getTicket());
             } else {
-                LOGGER.debug("Obtaining entitlements for authenticated user.");
-                AccessToken token = httpFacade.getSecurityContext().getToken();
-
-                if (token.getAuthorization() == null) {
-                    EntitlementResponse authzResponse = authzClient.entitlement(accessToken).getAll(authzClient.getConfiguration().getResource());
-                    return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
-                } else {
-                    EntitlementRequest request = new EntitlementRequest();
-                    PermissionRequest permissionRequest = new PermissionRequest();
-                    permissionRequest.setResourceSetId(pathConfig.getId());
-                    permissionRequest.setResourceSetName(pathConfig.getName());
-                    permissionRequest.setScopes(new HashSet<>(pathConfig.getScopes()));
-                    LOGGER.debugf("Sending entitlements request: resource_set_id [%s], resource_set_name [%s], scopes [%s].", permissionRequest.getResourceSetId(), permissionRequest.getResourceSetName(), permissionRequest.getScopes());
-                    request.addPermission(permissionRequest);
-                    EntitlementResponse authzResponse = authzClient.entitlement(accessToken).get(authzClient.getConfiguration().getResource(), request);
-                    return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
+                authzRequest = new AuthorizationRequest();
+                if (accessToken.getAuthorization() != null) {
+                    authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
                 }
             }
+
+            if (accessToken.getAuthorization() != null) {
+                authzRequest.setRpt(accessTokenString);
+            }
+
+            LOGGER.debug("Obtaining authorization for authenticated user.");
+            AuthorizationResponse authzResponse = authzClient.authorization(accessTokenString).authorize(authzRequest);
+
+            if (authzResponse != null) {
+                return AdapterRSATokenVerifier.verifyToken(authzResponse.getToken(), deployment);
+            }
+
+            return null;
         } catch (AuthorizationDeniedException e) {
             LOGGER.debug("Authorization denied", e);
             return null;
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
index c8bce94..8e83de1 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PathMatcher.java
@@ -18,8 +18,8 @@
 package org.keycloak.adapters.authorization;
 
 import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.representation.ResourceRepresentation;
@@ -221,11 +221,11 @@ class PathMatcher {
     private PathConfig resolvePathConfig(PathConfig originalConfig, String path) {
         if (originalConfig.hasPattern()) {
             ProtectedResource resource = this.authzClient.protection().resource();
-            Set<String> search = resource.findByFilter("uri=" + path);
+            List<ResourceRepresentation> search = resource.findByUri(path);
 
             if (!search.isEmpty()) {
                 // resource does exist on the server, cache it
-                ResourceRepresentation targetResource = resource.findById(search.iterator().next()).getResourceDescription();
+                ResourceRepresentation targetResource = search.get(0);
                 PathConfig config = PolicyEnforcer.createPathConfig(targetResource);
 
                 config.setScopes(originalConfig.getScopes());
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
index 7f21eef..fe8aa1a 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
@@ -17,6 +17,15 @@
  */
 package org.keycloak.adapters.authorization;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
 import org.jboss.logging.Logger;
 import org.keycloak.AuthorizationContext;
 import org.keycloak.adapters.KeycloakDeployment;
@@ -25,7 +34,6 @@ import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.ClientAuthenticator;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.RegistrationResponse;
 import org.keycloak.authorization.client.representation.ResourceRepresentation;
 import org.keycloak.authorization.client.representation.ScopeRepresentation;
 import org.keycloak.authorization.client.resource.ProtectedResource;
@@ -34,14 +42,6 @@ import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.PathConfig;
 import org.keycloak.representations.idm.authorization.Permission;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -58,10 +58,15 @@ public class PolicyEnforcer {
     public PolicyEnforcer(KeycloakDeployment deployment, AdapterConfig adapterConfig) {
         this.deployment = deployment;
         this.enforcerConfig = adapterConfig.getPolicyEnforcerConfig();
-        this.authzClient = AuthzClient.create(new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient()), new ClientAuthenticator() {
+        Configuration configuration = new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient());
+        this.authzClient = AuthzClient.create(configuration, new ClientAuthenticator() {
             @Override
-            public void configureClientCredentials(HashMap<String, String> requestParams, HashMap<String, String> requestHeaders) {
-                ClientCredentialsProviderUtils.setClientCredentials(PolicyEnforcer.this.deployment, requestHeaders, requestParams);
+            public void configureClientCredentials(Map<String, List<String>> requestParams, Map<String, String> requestHeaders) {
+                Map<String, String> formparams = new HashMap<>();
+                ClientCredentialsProviderUtils.setClientCredentials(PolicyEnforcer.this.deployment, requestHeaders, formparams);
+                for (Entry<String, String> param : formparams.entrySet()) {
+                    requestParams.put(param.getKey(), Arrays.asList(param.getValue()));
+                }
             }
         });
         this.pathMatcher = new PathMatcher(this.authzClient);
@@ -142,26 +147,34 @@ public class PolicyEnforcer {
         Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
 
         for (PathConfig pathConfig : enforcerConfig.getPaths()) {
-            Set<String> search;
+            ResourceRepresentation resource;
             String resourceName = pathConfig.getName();
             String path = pathConfig.getPath();
 
             if (resourceName != null) {
                 LOGGER.debugf("Trying to find resource with name [%s] for path [%s].", resourceName, path);
-                search = protectedResource.findByFilter("name=" + resourceName);
+                resource = protectedResource.findByName(resourceName);
             } else {
                 LOGGER.debugf("Trying to find resource with uri [%s] for path [%s].", path, path);
-                search = protectedResource.findByFilter("uri=" + path);
+                List<ResourceRepresentation> resources = protectedResource.findByUri(path);
+
+                if (resources.size() == 1) {
+                    resource = resources.get(0);
+                } else if (resources.size() > 1) {
+                    throw new RuntimeException("Multiple resources found with the same uri");
+                } else {
+                    resource = null;
+                }
             }
 
-            if (search.isEmpty()) {
+            if (resource == null) {
                 if (enforcerConfig.isCreateResources()) {
                     LOGGER.debugf("Creating resource on server for path [%s].", pathConfig);
-                    ResourceRepresentation resource = new ResourceRepresentation();
+                    ResourceRepresentation representation = new ResourceRepresentation();
 
-                    resource.setName(resourceName);
-                    resource.setType(pathConfig.getType());
-                    resource.setUri(path);
+                    representation.setName(resourceName);
+                    representation.setType(pathConfig.getType());
+                    representation.setUri(path);
 
                     HashSet<ScopeRepresentation> scopes = new HashSet<>();
 
@@ -173,16 +186,16 @@ public class PolicyEnforcer {
                         scopes.add(scope);
                     }
 
-                    resource.setScopes(scopes);
+                    representation.setScopes(scopes);
 
-                    RegistrationResponse registrationResponse = protectedResource.create(resource);
+                    ResourceRepresentation registrationResponse = protectedResource.create(representation);
 
                     pathConfig.setId(registrationResponse.getId());
                 } else {
                     throw new RuntimeException("Could not find matching resource on server with uri [" + path + "] or name [" + resourceName + "]. Make sure you have created a resource on the server that matches with the path configuration.");
                 }
             } else {
-                pathConfig.setId(search.iterator().next());
+                pathConfig.setId(resource.getId());
             }
 
             PathConfig existingPath = null;
@@ -210,8 +223,7 @@ public class PolicyEnforcer {
         Map<String, PathConfig> paths = Collections.synchronizedMap(new HashMap<String, PathConfig>());
 
         for (String id : protectedResource.findAll()) {
-            RegistrationResponse response = protectedResource.findById(id);
-            ResourceRepresentation resourceDescription = response.getResourceDescription();
+            ResourceRepresentation resourceDescription = protectedResource.findById(id);
 
             if (resourceDescription.getUri() != null) {
                 PathConfig pathConfig = createPathConfig(resourceDescription);
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
index bb68409..966f8c0 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
@@ -174,6 +174,10 @@ public class BearerTokenRequestAuthenticator {
 
             @Override
             public boolean challenge(HttpFacade facade) {
+                if (deployment.getPolicyEnforcer() != null) {
+                    deployment.getPolicyEnforcer().enforce(OIDCHttpFacade.class.cast(facade));
+                    return true;
+                }
                 OIDCAuthenticationError error = new OIDCAuthenticationError(reason, description);
                 facade.getRequest().setError(error);
                 facade.getResponse().addHeader("WWW-Authenticate", challenge);
diff --git a/adapters/oidc/js/src/main/resources/keycloak-authz.js b/adapters/oidc/js/src/main/resources/keycloak-authz.js
index 843b11d..5237273 100644
--- a/adapters/oidc/js/src/main/resources/keycloak-authz.js
+++ b/adapters/oidc/js/src/main/resources/keycloak-authz.js
@@ -18,14 +18,14 @@
 
 (function( window, undefined ) {
 
-    var KeycloakAuthorization = function (keycloak) {
+    var KeycloakAuthorization = function (keycloak, options) {
         var _instance = this;
         this.rpt = null;
 
         this.init = function () {
             var request = new XMLHttpRequest();
 
-            request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/.well-known/uma-configuration');
+            request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/.well-known/uma2-configuration');
             request.onreadystatechange = function () {
                 if (request.readyState == 4) {
                     if (request.status == 200) {
@@ -47,68 +47,61 @@
          * necessary information to ask a Keycloak server for authorization data using both UMA and Entitlement protocol,
          * depending on how the policy enforcer at the resource server was configured.
          */
-        this.authorize = function (wwwAuthenticateHeader) {
+        this.authorize = function (authorizationRequest) {
             this.then = function (onGrant, onDeny, onError) {
-                if (wwwAuthenticateHeader.indexOf('UMA') != -1) {
-                    var params = wwwAuthenticateHeader.split(',');
-
-                    for (i = 0; i < params.length; i++) {
-                        var param = params[i].split('=');
-
-                        if (param[0] == 'ticket') {
-                            var request = new XMLHttpRequest();
-
-                            request.open('POST', _instance.config.rpt_endpoint, true);
-                            request.setRequestHeader('Content-Type', 'application/json')
-                            request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
-
-                            request.onreadystatechange = function () {
-                                if (request.readyState == 4) {
-                                    var status = request.status;
-
-                                    if (status >= 200 && status < 300) {
-                                        var rpt = JSON.parse(request.responseText).rpt;
-                                        _instance.rpt = rpt;
-                                        onGrant(rpt);
-                                    } else if (status == 403) {
-                                        if (onDeny) {
-                                            onDeny();
-                                        } else {
-                                            console.error('Authorization request was denied by the server.');
-                                        }
-                                    } else {
-                                        if (onError) {
-                                            onError();
-                                        } else {
-                                            console.error('Could not obtain authorization data from server.');
-                                        }
-                                    }
+                if (authorizationRequest && authorizationRequest.ticket) {
+                    var request = new XMLHttpRequest();
+
+                    request.open('POST', _instance.config.token_endpoint, true);
+                    request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+                    request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
+
+                    request.onreadystatechange = function () {
+                        if (request.readyState == 4) {
+                            var status = request.status;
+
+                            if (status >= 200 && status < 300) {
+                                var rpt = JSON.parse(request.responseText).access_token;
+                                _instance.rpt = rpt;
+                                onGrant(rpt);
+                            } else if (status == 403) {
+                                if (onDeny) {
+                                    onDeny();
+                                } else {
+                                    console.error('Authorization request was denied by the server.');
                                 }
-                            };
-
-                            var ticket = param[1].substring(1, param[1].length - 1).trim();
-
-                            request.send(JSON.stringify(
-                                {
-                                    ticket: ticket,
-                                    rpt: _instance.rpt
+                            } else {
+                                if (onError) {
+                                    onError();
+                                } else {
+                                    console.error('Could not obtain authorization data from server.');
                                 }
-                            ));
+                            }
                         }
+                    };
+
+                    var params = "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket&client_id=" + keycloak.clientId + "&ticket=" + authorizationRequest.ticket;
+
+                    if (authorizationRequest.submitRequest != undefined) {
+                        params += "&submit_request=" + authorizationRequest.submitRequest;
                     }
-                } else if (wwwAuthenticateHeader.indexOf('KC_ETT') != -1) {
-                    var params = wwwAuthenticateHeader.substring('KC_ETT'.length).trim().split(',');
-                    var clientId = null;
 
-                    for (i = 0; i < params.length; i++) {
-                        var param = params[i].split('=');
+                    var metadata = authorizationRequest.metadata;
 
-                        if (param[0] == 'realm') {
-                            clientId = param[1].substring(1, param[1].length - 1).trim();
+                    if (metadata) {
+                        if (metadata.responseIncludeResourceName) {
+                            params += "&response_include_resource_name=" + metadata.responseIncludeResourceName;
                         }
+                        if (metadata.responsePermissionsLimit) {
+                            params += "&response_permissions_limit=" + metadata.responsePermissionsLimit;
+                        }
+                    }
+
+                    if (_instance.rpt && (authorizationRequest.incrementalAuthorization == undefined || authorizationRequest.incrementalAuthorization)) {
+                        params += "&rpt=" + _instance.rpt;
                     }
 
-                    _instance.entitlement(clientId).then(onGrant, onDeny, onError);
+                    request.send(params);
                 }
             };
 
@@ -116,20 +109,22 @@
         };
 
         /**
-         * Obtains all entitlements from a Keycloak Server based on a give resourceServerId.
+         * Obtains all entitlements from a Keycloak Server based on a given resourceServerId.
          */
-        this.entitlement = function (resourceSeververId, entitlementRequest     ) {
+        this.entitlement = function (resourceServerId, authorizationRequest) {
             this.then = function (onGrant, onDeny, onError) {
                 var request = new XMLHttpRequest();
 
-
+                request.open('POST', _instance.config.token_endpoint, true);
+                request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+                request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
 
                 request.onreadystatechange = function () {
                     if (request.readyState == 4) {
                         var status = request.status;
 
                         if (status >= 200 && status < 300) {
-                            var rpt = JSON.parse(request.responseText).rpt;
+                            var rpt = JSON.parse(request.responseText).access_token;
                             _instance.rpt = rpt;
                             onGrant(rpt);
                         } else if (status == 403) {
@@ -148,19 +143,62 @@
                     }
                 };
 
-                var erJson = null
+                if (!authorizationRequest) {
+                    authorizationRequest = {};
+                }
+
+                var params = "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket&client_id=" + keycloak.clientId;
 
-                if(entitlementRequest) {
-                    request.open('POST', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/authz/entitlement/' + resourceSeververId, true);
-                    request.setRequestHeader("Content-type", "application/json");
-                    erJson = JSON.stringify(entitlementRequest)
-                } else {
-                    request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/authz/entitlement/' + resourceSeververId, true);
+                if (authorizationRequest.claimToken) {
+                    params += "&claim_token=" + authorizationRequest.claimToken;
+
+                    if (authorizationRequest.claimTokenFormat) {
+                        params += "&claim_token_format=" + authorizationRequest.claimTokenFormat;
+                    }
                 }
 
-                request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
-                request.send(erJson);
+                params += "&audience=" + resourceServerId;
+
+                var permissions = authorizationRequest.permissions;
+
+                if (!permissions) {
+                    permissions = [];
+                }
+
+                for (i = 0; i < permissions.length; i++) {
+                    var resource = permissions[i];
+                    var permission = resource.id;
+
+                    if (resource.scopes && resource.scopes.length > 0) {
+                        permission += "#";
+                        for (j = 0; j < resource.scopes.length; j++) {
+                            var scope = resource.scopes[j];
+                            if (permission.indexOf('#') != permission.length - 1) {
+                                permission += ",";
+                            }
+                            permission += scope;
+                        }
+                    }
+
+                    params += "&permission=" + permission;
+                }
+
+                var metadata = authorizationRequest.metadata;
+
+                if (metadata) {
+                    if (metadata.responseIncludeResourceName) {
+                        params += "&response_include_resource_name=" + metadata.responseIncludeResourceName;
+                    }
+                    if (metadata.responsePermissionsLimit) {
+                        params += "&response_permissions_limit=" + metadata.responsePermissionsLimit;
+                    }
+                }
+
+                if (_instance.rpt) {
+                    params += "&rpt=" + _instance.rpt;
+                }
 
+                request.send(params);
             };
 
             return this;
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java b/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
index 1113fd2..1bf5c86 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/AuthzClient.java
@@ -17,32 +17,40 @@
  */
 package org.keycloak.authorization.client;
 
+import java.io.IOException;
+import java.io.InputStream;
+
 import org.keycloak.authorization.client.representation.ServerConfiguration;
 import org.keycloak.authorization.client.resource.AuthorizationResource;
-import org.keycloak.authorization.client.resource.EntitlementResource;
 import org.keycloak.authorization.client.resource.ProtectionResource;
 import org.keycloak.authorization.client.util.Http;
-import org.keycloak.jose.jws.JWSInput;
-import org.keycloak.representations.AccessToken;
+import org.keycloak.authorization.client.util.TokenCallable;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.util.JsonSerialization;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.util.concurrent.Callable;
-
 /**
  * <p>This is class serves as an entry point for clients looking for access to Keycloak Authorization Services.
  *
+ * <p>When creating a new instances make sure you have a Keycloak Server running at the location specified in the client
+ * configuration. The client tries to obtain server configuration by invoking the UMA Discovery Endpoint, usually available
+ * from the server at <i>http(s)://{server}:{port}/auth/realms/{realm}/.well-known/uma-configuration</i>.
+ *
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class AuthzClient {
 
     private final Http http;
-    private Callable<String> patSupplier;
-
-    public static AuthzClient create() {
+    private TokenCallable patSupplier;
+
+    /**
+     * <p>Creates a new instance.
+     *
+     * <p>This method expects a <code>keycloak.json</code> in the classpath, otherwise an exception will be thrown.
+     *
+     * @return a new instance
+     * @throws RuntimeException in case there is no <code>keycloak.json</code> file in the classpath or the file could not be parsed
+     */
+    public static AuthzClient create() throws RuntimeException {
         InputStream configStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("keycloak.json");
 
         if (configStream == null) {
@@ -56,65 +64,118 @@ public class AuthzClient {
         }
     }
 
+    /**
+     * <p>Creates a new instance.
+     *
+     * @param configuration the client configuration
+     * @return a new instance
+     */
     public static AuthzClient create(Configuration configuration) {
         return new AuthzClient(configuration, configuration.getClientAuthenticator());
     }
 
+    /**
+     * <p>Creates a new instance.
+     *
+     * @param configuration the client configuration
+     * @param authenticator the client authenticator
+     * @return a new instance
+     */
     public static AuthzClient create(Configuration configuration, ClientAuthenticator authenticator) {
         return new AuthzClient(configuration, authenticator);
     }
 
     private final ServerConfiguration serverConfiguration;
-    private final Configuration deployment;
-
-    private AuthzClient(Configuration configuration, ClientAuthenticator authenticator) {
-        if (configuration == null) {
-            throw new IllegalArgumentException("Client configuration can not be null.");
-        }
-
-        String configurationUrl = configuration.getAuthServerUrl();
-
-        if (configurationUrl == null) {
-            throw new IllegalArgumentException("Configuration URL can not be null.");
-        }
-
-        configurationUrl += "/realms/" + configuration.getRealm() + "/.well-known/uma-configuration";
-
-        this.deployment = configuration;
-
-        this.http = new Http(configuration, authenticator != null ? authenticator : configuration.getClientAuthenticator());
-
-        try {
-            this.serverConfiguration = this.http.<ServerConfiguration>get(URI.create(configurationUrl))
-                    .response().json(ServerConfiguration.class)
-                    .execute();
-        } catch (Exception e) {
-            throw new RuntimeException("Could not obtain configuration from server [" + configurationUrl + "].", e);
-        }
-
-        this.http.setServerConfiguration(this.serverConfiguration);
+    private final Configuration configuration;
+
+    /**
+     * <p>Creates a {@link ProtectionResource} instance which can be used to access the Protection API.
+     *
+     * <p>When using this method, the PAT (the access token with the uma_protection scope) is obtained for the client
+     * itself, using any of the supported credential types (client/secret, jwt, etc).
+     *
+     * @return a {@link ProtectionResource}
+     */
+    public ProtectionResource protection() {
+        return new ProtectionResource(this.http, this.serverConfiguration, createPatSupplier());
     }
 
-    private AuthzClient(Configuration configuration) {
-        this(configuration, null);
+    /**
+     * <p>Creates a {@link ProtectionResource} instance which can be used to access the Protection API.
+     *
+     * @param the PAT (the access token with the uma_protection scope)
+     * @return a {@link ProtectionResource}
+     */
+    public ProtectionResource protection(final String accessToken) {
+        return new ProtectionResource(this.http, this.serverConfiguration, new TokenCallable(http, configuration, serverConfiguration) {
+            @Override
+            public String call() {
+                return accessToken;
+            }
+
+            @Override
+            protected boolean isRetry() {
+                return false;
+            }
+        });
     }
 
-    public ProtectionResource protection() {
-        return new ProtectionResource(this.http, createPatSupplier());
+    /**
+     * <p>Creates a {@link ProtectionResource} instance which can be used to access the Protection API.
+     *
+     * <p>When using this method, the PAT (the access token with the uma_protection scope) is obtained for a given user.
+     *
+     * @return a {@link ProtectionResource}
+     */
+    public ProtectionResource protection(String userName, String password) {
+        return new ProtectionResource(this.http, this.serverConfiguration, createPatSupplier(userName, password));
     }
 
-    public AuthorizationResource authorization(String accesstoken) {
-        return new AuthorizationResource(this.http, accesstoken);
+    /**
+     * <p>Creates a {@link AuthorizationResource} instance which can be used to obtain permissions from the server.
+     *
+     * @return a {@link AuthorizationResource}
+     */
+    public AuthorizationResource authorization() {
+        return new AuthorizationResource(configuration, serverConfiguration, this.http, null);
     }
 
-    public AuthorizationResource authorization(String userName, String password) {
-        return new AuthorizationResource(this.http, obtainAccessToken(userName, password).getToken());
+    /**
+     * <p>Creates a {@link AuthorizationResource} instance which can be used to obtain permissions from the server.
+     *
+     * @param accessToken the Access Token that will be used as a bearer to access the token endpoint
+     * @return a {@link AuthorizationResource}
+     */
+    public AuthorizationResource authorization(final String accessToken) {
+        return new AuthorizationResource(configuration, serverConfiguration, this.http, new TokenCallable(http, configuration, serverConfiguration) {
+            @Override
+            public String call() {
+                return accessToken;
+            }
+
+            @Override
+            protected boolean isRetry() {
+                return false;
+            }
+        });
     }
 
-    public EntitlementResource entitlement(String eat) {
-        return new EntitlementResource(this.http, eat);
+    /**
+     * <p>Creates a {@link AuthorizationResource} instance which can be used to obtain permissions from the server.
+     *
+     * @param userName an ID Token or Access Token representing an identity and/or access context
+     * @param password
+     * @return a {@link AuthorizationResource}
+     */
+    public AuthorizationResource authorization(final String userName, final String password) {
+        return new AuthorizationResource(configuration, serverConfiguration, this.http, createRefreshableAccessTokenSupplier(userName, password));
     }
 
+    /**
+     * Obtains an access token using the client credentials.
+     *
+     * @return an {@link AccessTokenResponse}
+     */
     public AccessTokenResponse obtainAccessToken() {
         return this.http.<AccessTokenResponse>post(this.serverConfiguration.getTokenEndpoint())
                 .authentication()
@@ -124,6 +185,11 @@ public class AuthzClient {
                 .execute();
     }
 
+    /**
+     * Obtains an access token using the resource owner credentials.
+     *
+     * @return an {@link AccessTokenResponse}
+     */
     public AccessTokenResponse obtainAccessToken(String userName, String password) {
         return this.http.<AccessTokenResponse>post(this.serverConfiguration.getTokenEndpoint())
                 .authentication()
@@ -133,47 +199,64 @@ public class AuthzClient {
                 .execute();
     }
 
+    /**
+     * Returns the configuration obtained from the server at the UMA Discovery Endpoint.
+     *
+     * @return the {@link ServerConfiguration}
+     */
     public ServerConfiguration getServerConfiguration() {
         return this.serverConfiguration;
     }
 
+    /**
+     * Obtains the client configuration
+     *
+     * @return the {@link Configuration}
+     */
     public Configuration getConfiguration() {
-        return this.deployment;
+        return this.configuration;
     }
 
-    private Callable<String> createPatSupplier() {
+    private AuthzClient(Configuration configuration, ClientAuthenticator authenticator) {
+        if (configuration == null) {
+            throw new IllegalArgumentException("Client configuration can not be null.");
+        }
+
+        String configurationUrl = configuration.getAuthServerUrl();
+
+        if (configurationUrl == null) {
+            throw new IllegalArgumentException("Configuration URL can not be null.");
+        }
+
+        configurationUrl += "/realms/" + configuration.getRealm() + "/.well-known/uma2-configuration";
+
+        this.configuration = configuration;
+
+        this.http = new Http(configuration, authenticator != null ? authenticator : configuration.getClientAuthenticator());
+
+        try {
+            this.serverConfiguration = this.http.<ServerConfiguration>get(configurationUrl)
+                    .response().json(ServerConfiguration.class)
+                    .execute();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not obtain configuration from server [" + configurationUrl + "].", e);
+        }
+
+        this.http.setServerConfiguration(this.serverConfiguration);
+    }
+
+    private TokenCallable createPatSupplier(String userName, String password) {
         if (patSupplier == null) {
-            patSupplier = new Callable<String>() {
-                AccessTokenResponse clientToken = obtainAccessToken();
-
-                @Override
-                public String call() {
-                    String token = clientToken.getToken();
-
-                    try {
-                        AccessToken accessToken = JsonSerialization.readValue(new JWSInput(token).getContent(), AccessToken.class);
-
-                        if (accessToken.isActive()) {
-                            return token;
-                        }
-
-                        clientToken = http.<AccessTokenResponse>post(serverConfiguration.getTokenEndpoint())
-                                .authentication().client()
-                                .form()
-                                .param("grant_type", "refresh_token")
-                                .param("refresh_token", clientToken.getRefreshToken())
-                                .response()
-                                .json(AccessTokenResponse.class)
-                                .execute();
-                    } catch (Exception e) {
-                        patSupplier = null;
-                        throw new RuntimeException(e);
-                    }
-
-                    return clientToken.getToken();
-                }
-            };
+            patSupplier = createRefreshableAccessTokenSupplier(userName, password);
         }
         return patSupplier;
     }
-}
+
+    private TokenCallable createPatSupplier() {
+        return createPatSupplier(null, null);
+    }
+
+    private TokenCallable createRefreshableAccessTokenSupplier(final String userName, final String password) {
+        return new TokenCallable(userName, password, http, configuration, serverConfiguration);
+    }
+}
\ No newline at end of file
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthenticator.java b/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthenticator.java
index 076c2db..d9077e5 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthenticator.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthenticator.java
@@ -17,11 +17,12 @@
  */
 package org.keycloak.authorization.client;
 
-import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public interface ClientAuthenticator {
-    void configureClientCredentials(HashMap<String, String> requestParams, HashMap<String, String> requestHeaders);
+    void configureClientCredentials(Map<String, List<String>> requestParams, Map<String, String> requestHeaders);
 }
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/Configuration.java b/authz/client/src/main/java/org/keycloak/authorization/client/Configuration.java
index 647891f..7e6802e 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/Configuration.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/Configuration.java
@@ -17,14 +17,14 @@
  */
 package org.keycloak.authorization.client;
 
-import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import org.apache.http.client.HttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.keycloak.representations.adapters.config.AdapterConfig;
 import org.keycloak.util.BasicAuthHelper;
-import com.fasterxml.jackson.annotation.JsonIgnore;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -34,10 +34,22 @@ public class Configuration extends AdapterConfig {
     @JsonIgnore
     private HttpClient httpClient;
 
+    @JsonIgnore
+    private ClientAuthenticator clientAuthenticator = createDefaultClientAuthenticator();
+
     public Configuration() {
 
     }
 
+    /**
+     * Creates a new instance.
+     *
+     * @param authServerUrl the server's URL. E.g.: http://{server}:{port}/auth.(not {@code null})
+     * @param realm the realm name (not {@code null})
+     * @param clientId the client id (not {@code null})
+     * @param clientCredentials a map with the client credentials (not {@code null})
+     * @param httpClient the {@link HttpClient} instance that should be used when sending requests to the server, or {@code null} if a default instance should be created
+     */
     public Configuration(String authServerUrl, String realm, String clientId, Map<String, Object> clientCredentials, HttpClient httpClient) {
         this.authServerUrl = authServerUrl;
         setAuthServerUrl(authServerUrl);
@@ -47,29 +59,34 @@ public class Configuration extends AdapterConfig {
         this.httpClient = httpClient;
     }
 
-    @JsonIgnore
-    private ClientAuthenticator clientAuthenticator = new ClientAuthenticator() {
-        @Override
-        public void configureClientCredentials(HashMap<String, String> requestParams, HashMap<String, String> requestHeaders) {
-            String secret = (String) getCredentials().get("secret");
-
-            if (secret == null) {
-                throw new RuntimeException("Client secret not provided.");
-            }
-
-            requestHeaders.put("Authorization", BasicAuthHelper.createHeader(getResource(), secret));
-        }
-    };
-
     public HttpClient getHttpClient() {
         if (this.httpClient == null) {
             this.httpClient = HttpClients.createDefault();
         }
-
         return httpClient;
     }
 
-    public ClientAuthenticator getClientAuthenticator() {
+    ClientAuthenticator getClientAuthenticator() {
         return this.clientAuthenticator;
     }
+
+    /**
+     * Creates a default client authenticator which uses HTTP BASIC and client id and secret to authenticate the client.
+     *
+     * @return the default client authenticator
+     */
+    private ClientAuthenticator createDefaultClientAuthenticator() {
+        return new ClientAuthenticator() {
+            @Override
+            public void configureClientCredentials(Map<String, List<String>> requestParams, Map<String, String> requestHeaders) {
+                String secret = (String) getCredentials().get("secret");
+
+                if (secret == null) {
+                    throw new RuntimeException("Client secret not provided.");
+                }
+
+                requestHeaders.put("Authorization", BasicAuthHelper.createHeader(getResource(), secret));
+            }
+        };
+    }
 }
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/ResourceRepresentation.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ResourceRepresentation.java
index 65ec87c..c2e8f8a 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/representation/ResourceRepresentation.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ResourceRepresentation.java
@@ -17,14 +17,14 @@
  */
 package org.keycloak.authorization.client.representation;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
-
 import java.net.URI;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
+
 /**
  * <p>One or more resources that the resource server manages as a set of protected resources.
  *
@@ -38,13 +38,17 @@ public class ResourceRepresentation {
     private String id;
 
     private String name;
+    private String displayName;
     private String uri;
     private String type;
+
+    @JsonProperty("resource_scopes")
     private Set<ScopeRepresentation> scopes;
 
     @JsonProperty("icon_uri")
     private String iconUri;
     private String owner;
+    private Boolean ownerManagedAccess;
 
     /**
      * Creates a new instance.
@@ -106,6 +110,10 @@ public class ResourceRepresentation {
         return this.name;
     }
 
+    public String getDisplayName() {
+        return displayName;
+    }
+
     public String getUri() {
         return this.uri;
     }
@@ -129,6 +137,10 @@ public class ResourceRepresentation {
         this.name = name;
     }
 
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
     public void setUri(String uri) {
         this.uri = uri;
     }
@@ -153,6 +165,14 @@ public class ResourceRepresentation {
         this.owner = owner;
     }
 
+    public void setOwnerManagedAccess(Boolean ownerManagedAccess) {
+        this.ownerManagedAccess = ownerManagedAccess;
+    }
+
+    public Boolean getOwnerManagedAccess() {
+        return ownerManagedAccess;
+    }
+
     public void addScope(ScopeRepresentation scopeRepresentation) {
         if (this.scopes == null) {
             this.scopes = new HashSet<>();
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/ServerConfiguration.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ServerConfiguration.java
index 6716165..f708e52 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/representation/ServerConfiguration.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ServerConfiguration.java
@@ -1,13 +1,12 @@
 /*
- * JBoss, Home of Professional Open Source
- *
- * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2018 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
+ * 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,
@@ -17,219 +16,194 @@
  */
 package org.keycloak.authorization.client.representation;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.List;
 
-import java.net.URI;
-import java.util.Set;
+import com.fasterxml.jackson.annotation.JsonProperty;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class ServerConfiguration {
 
-    private String version;
-    private URI issuer;
+    @JsonProperty("issuer")
+    private String issuer;
 
-    @JsonProperty("pat_profiles_supported")
-    private Set<String> patProfiles;
+    @JsonProperty("authorization_endpoint")
+    private String authorizationEndpoint;
 
-    @JsonProperty("pat_grant_types_supported")
-    private Set<String> patGrantTypes;
+    @JsonProperty("token_endpoint")
+    private String tokenEndpoint;
 
-    @JsonProperty("aat_profiles_supported")
-    private Set<String> aatProfiles;
+    @JsonProperty("token_introspection_endpoint")
+    private String tokenIntrospectionEndpoint;
 
-    @JsonProperty("aat_grant_types_supported")
-    private Set<String> aatGrantTypes;
+    @JsonProperty("userinfo_endpoint")
+    private String userinfoEndpoint;
 
-    @JsonProperty("rpt_profiles_supported")
-    private Set<String> rptProfiles;
+    @JsonProperty("end_session_endpoint")
+    private String logoutEndpoint;
 
-    @JsonProperty("claim_token_profiles_supported")
-    private Set<String> claimTokenProfiles;
+    @JsonProperty("jwks_uri")
+    private String jwksUri;
 
-    @JsonProperty("dynamic_client_endpoint")
-    private URI dynamicClientEndpoint;
+    @JsonProperty("check_session_iframe")
+    private String checkSessionIframe;
 
-    @JsonProperty("token_endpoint")
-    private URI tokenEndpoint;
+    @JsonProperty("grant_types_supported")
+    private List<String> grantTypesSupported;
 
-    @JsonProperty("authorization_endpoint")
-    private URI authorizationEndpoint;
+    @JsonProperty("response_types_supported")
+    private List<String> responseTypesSupported;
 
-    @JsonProperty("requesting_party_claims_endpoint")
-    private URI requestingPartyClaimsEndpoint;
+    @JsonProperty("subject_types_supported")
+    private List<String> subjectTypesSupported;
 
-    @JsonProperty("resource_set_registration_endpoint")
-    private URI resourceSetRegistrationEndpoint;
+    @JsonProperty("id_token_signing_alg_values_supported")
+    private List<String> idTokenSigningAlgValuesSupported;
 
-    @JsonProperty("introspection_endpoint")
-    private URI introspectionEndpoint;
+    @JsonProperty("userinfo_signing_alg_values_supported")
+    private List<String> userInfoSigningAlgValuesSupported;
 
-    @JsonProperty("permission_registration_endpoint")
-    private URI permissionRegistrationEndpoint;
+    @JsonProperty("request_object_signing_alg_values_supported")
+    private List<String> requestObjectSigningAlgValuesSupported;
 
-    @JsonProperty("rpt_endpoint")
-    private URI rptEndpoint;
+    @JsonProperty("response_modes_supported")
+    private List<String> responseModesSupported;
 
-    /**
-     * Non-standard, Keycloak specific configuration options
-     */
-    private String realm;
+    @JsonProperty("registration_endpoint")
+    private String registrationEndpoint;
 
-    private String realmPublicKey;
+    @JsonProperty("token_endpoint_auth_methods_supported")
+    private List<String> tokenEndpointAuthMethodsSupported;
 
-    private URI serverUrl;
+    @JsonProperty("token_endpoint_auth_signing_alg_values_supported")
+    private List<String> tokenEndpointAuthSigningAlgValuesSupported;
 
-    public String getVersion() {
-        return this.version;
-    }
+    @JsonProperty("claims_supported")
+    private List<String> claimsSupported;
 
-    void setVersion(final String version) {
-        this.version = version;
-    }
+    @JsonProperty("claim_types_supported")
+    private List<String> claimTypesSupported;
 
-    public URI getIssuer() {
-        return this.issuer;
-    }
+    @JsonProperty("claims_parameter_supported")
+    private Boolean claimsParameterSupported;
 
-    void setIssuer(final URI issuer) {
-        this.issuer = issuer;
-    }
+    @JsonProperty("scopes_supported")
+    private List<String> scopesSupported;
 
-    public Set<String> getPatProfiles() {
-        return this.patProfiles;
-    }
+    @JsonProperty("request_parameter_supported")
+    private Boolean requestParameterSupported;
 
-    void setPatProfiles(final Set<String> patProfiles) {
-        this.patProfiles = patProfiles;
-    }
-
-    public Set<String> getPatGrantTypes() {
-        return this.patGrantTypes;
-    }
+    @JsonProperty("request_uri_parameter_supported")
+    private Boolean requestUriParameterSupported;
 
-    void setPatGrantTypes(final Set<String> patGrantTypes) {
-        this.patGrantTypes = patGrantTypes;
-    }
-
-    public Set<String> getAatProfiles() {
-        return this.aatProfiles;
-    }
+    @JsonProperty("resource_registration_endpoint")
+    private String resourceRegistrationEndpoint;
 
-    void setAatProfiles(final Set<String> aatProfiles) {
-        this.aatProfiles = aatProfiles;
-    }
-
-    public Set<String> getAatGrantTypes() {
-        return this.aatGrantTypes;
-    }
-
-    void setAatGrantTypes(final Set<String> aatGrantTypes) {
-        this.aatGrantTypes = aatGrantTypes;
-    }
+    @JsonProperty("permission_endpoint")
+    private String permissionEndpoint;
 
-    public Set<String> getRptProfiles() {
-        return this.rptProfiles;
+    public String getIssuer() {
+        return issuer;
     }
 
-    void setRptProfiles(final Set<String> rptProfiles) {
-        this.rptProfiles = rptProfiles;
+    public String getAuthorizationEndpoint() {
+        return authorizationEndpoint;
     }
 
-    public Set<String> getClaimTokenProfiles() {
-        return this.claimTokenProfiles;
+    public String getTokenEndpoint() {
+        return tokenEndpoint;
     }
 
-    void setClaimTokenProfiles(final Set<String> claimTokenProfiles) {
-        this.claimTokenProfiles = claimTokenProfiles;
+    public String getTokenIntrospectionEndpoint() {
+        return tokenIntrospectionEndpoint;
     }
 
-    public URI getDynamicClientEndpoint() {
-        return this.dynamicClientEndpoint;
+    public String getUserinfoEndpoint() {
+        return userinfoEndpoint;
     }
 
-    void setDynamicClientEndpoint(final URI dynamicClientEndpoint) {
-        this.dynamicClientEndpoint = dynamicClientEndpoint;
+    public String getLogoutEndpoint() {
+        return logoutEndpoint;
     }
 
-    public URI getTokenEndpoint() {
-        return this.tokenEndpoint;
+    public String getJwksUri() {
+        return jwksUri;
     }
 
-    void setTokenEndpoint(final URI tokenEndpoint) {
-        this.tokenEndpoint = tokenEndpoint;
+    public String getCheckSessionIframe() {
+        return checkSessionIframe;
     }
 
-    public URI getAuthorizationEndpoint() {
-        return this.authorizationEndpoint;
+    public List<String> getGrantTypesSupported() {
+        return grantTypesSupported;
     }
 
-    void setAuthorizationEndpoint(final URI authorizationEndpoint) {
-        this.authorizationEndpoint = authorizationEndpoint;
+    public List<String> getResponseTypesSupported() {
+        return responseTypesSupported;
     }
 
-    public URI getRequestingPartyClaimsEndpoint() {
-        return this.requestingPartyClaimsEndpoint;
+    public List<String> getSubjectTypesSupported() {
+        return subjectTypesSupported;
     }
 
-    void setRequestingPartyClaimsEndpoint(final URI requestingPartyClaimsEndpoint) {
-        this.requestingPartyClaimsEndpoint = requestingPartyClaimsEndpoint;
+    public List<String> getIdTokenSigningAlgValuesSupported() {
+        return idTokenSigningAlgValuesSupported;
     }
 
-    public URI getResourceSetRegistrationEndpoint() {
-        return this.resourceSetRegistrationEndpoint;
+    public List<String> getUserInfoSigningAlgValuesSupported() {
+        return userInfoSigningAlgValuesSupported;
     }
 
-    void setResourceSetRegistrationEndpoint(final URI resourceSetRegistrationEndpoint) {
-        this.resourceSetRegistrationEndpoint = resourceSetRegistrationEndpoint;
+    public List<String> getRequestObjectSigningAlgValuesSupported() {
+        return requestObjectSigningAlgValuesSupported;
     }
 
-    public URI getIntrospectionEndpoint() {
-        return this.introspectionEndpoint;
+    public List<String> getResponseModesSupported() {
+        return responseModesSupported;
     }
 
-    void setIntrospectionEndpoint(final URI introspectionEndpoint) {
-        this.introspectionEndpoint = introspectionEndpoint;
+    public String getRegistrationEndpoint() {
+        return registrationEndpoint;
     }
 
-    public URI getPermissionRegistrationEndpoint() {
-        return this.permissionRegistrationEndpoint;
+    public List<String> getTokenEndpointAuthMethodsSupported() {
+        return tokenEndpointAuthMethodsSupported;
     }
 
-    void setPermissionRegistrationEndpoint(final URI permissionRegistrationEndpoint) {
-        this.permissionRegistrationEndpoint = permissionRegistrationEndpoint;
+    public List<String> getTokenEndpointAuthSigningAlgValuesSupported() {
+        return tokenEndpointAuthSigningAlgValuesSupported;
     }
 
-    public URI getRptEndpoint() {
-        return this.rptEndpoint;
+    public List<String> getClaimsSupported() {
+        return claimsSupported;
     }
 
-    void setRptEndpoint(final URI rptEndpoint) {
-        this.rptEndpoint = rptEndpoint;
+    public List<String> getClaimTypesSupported() {
+        return claimTypesSupported;
     }
 
-    public String getRealm() {
-        return this.realm;
+    public Boolean getClaimsParameterSupported() {
+        return claimsParameterSupported;
     }
 
-    public void setRealm(final String realm) {
-        this.realm = realm;
+    public List<String> getScopesSupported() {
+        return scopesSupported;
     }
 
-    public String getRealmPublicKey() {
-        return this.realmPublicKey;
+    public Boolean getRequestParameterSupported() {
+        return requestParameterSupported;
     }
 
-    public void setRealmPublicKey(String realmPublicKey) {
-        this.realmPublicKey = realmPublicKey;
+    public Boolean getRequestUriParameterSupported() {
+        return requestUriParameterSupported;
     }
 
-    public URI getServerUrl() {
-        return this.serverUrl;
+    public String getResourceRegistrationEndpoint() {
+        return resourceRegistrationEndpoint;
     }
 
-    public void setServerUrl(URI serverUrl) {
-        this.serverUrl = serverUrl;
+    public String getPermissionEndpoint() {
+        return permissionEndpoint;
     }
 }
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/TokenIntrospectionResponse.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/TokenIntrospectionResponse.java
index 8fcc6f3..8bd0424 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/representation/TokenIntrospectionResponse.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/TokenIntrospectionResponse.java
@@ -17,12 +17,12 @@
  */
 package org.keycloak.authorization.client.representation;
 
+import java.util.List;
+
 import com.fasterxml.jackson.annotation.JsonProperty;
 import org.keycloak.representations.JsonWebToken;
 import org.keycloak.representations.idm.authorization.Permission;
 
-import java.util.List;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java
index 0f22ebe..6b30b0d 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java
@@ -18,34 +18,82 @@
 package org.keycloak.authorization.client.resource;
 
 
-import static org.keycloak.authorization.client.util.Throwables.handleAndWrapException;
+import java.util.concurrent.Callable;
 
-import org.keycloak.authorization.client.representation.AuthorizationRequest;
-import org.keycloak.authorization.client.representation.AuthorizationResponse;
+import org.keycloak.authorization.client.AuthorizationDeniedException;
+import org.keycloak.authorization.client.Configuration;
+import org.keycloak.authorization.client.representation.ServerConfiguration;
 import org.keycloak.authorization.client.util.Http;
-import org.keycloak.util.JsonSerialization;
+import org.keycloak.authorization.client.util.HttpMethod;
+import org.keycloak.authorization.client.util.Throwables;
+import org.keycloak.authorization.client.util.TokenCallable;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 
 /**
+ * An entry point for obtaining permissions from the server.
+ *
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class AuthorizationResource {
 
-    private final Http http;
-    private final String accessToken;
+    private Configuration configuration;
+    private ServerConfiguration serverConfiguration;
+    private Http http;
+    private TokenCallable token;
 
-    public AuthorizationResource(Http http, String aat) {
+    public AuthorizationResource(Configuration configuration, ServerConfiguration serverConfiguration, Http http, TokenCallable token) {
+        this.configuration = configuration;
+        this.serverConfiguration = serverConfiguration;
         this.http = http;
-        this.accessToken = aat;
+        this.token = token;
+    }
+
+    /**
+     * Query the server for all permissions.
+     *
+     * @return an {@link AuthorizationResponse} with a RPT holding all granted permissions
+     * @throws AuthorizationDeniedException in case the request was denied by the server
+     */
+    public AuthorizationResponse authorize() throws AuthorizationDeniedException {
+        return authorize(new AuthorizationRequest());
     }
 
-    public AuthorizationResponse authorize(AuthorizationRequest request) {
+    /**
+     * Query the server for permissions given an {@link AuthorizationRequest}.
+     *
+     * @param request an {@link AuthorizationRequest} (not {@code null})
+     * @return an {@link AuthorizationResponse} with a RPT holding all granted permissions
+     * @throws AuthorizationDeniedException in case the request was denied by the server
+     */
+    public AuthorizationResponse authorize(final AuthorizationRequest request) throws AuthorizationDeniedException {
+        if (request == null) {
+            throw new IllegalArgumentException("Authorization request must not be null");
+        }
+
+        Callable<AuthorizationResponse> callable = new Callable<AuthorizationResponse>() {
+            @Override
+            public AuthorizationResponse call() throws Exception {
+                request.setAudience(configuration.getResource());
+
+                HttpMethod<AuthorizationResponse> method = http.<AuthorizationResponse>post(serverConfiguration.getTokenEndpoint());
+
+                if (token != null) {
+                    method = method.authorizationBearer(token.call());
+                }
+
+                return method
+                        .authentication()
+                        .uma(request)
+                        .response()
+                        .json(AuthorizationResponse.class)
+                        .execute();
+            }
+        };
         try {
-            return this.http.<AuthorizationResponse>post("/authz/authorize")
-                    .authorizationBearer(this.accessToken)
-                    .json(JsonSerialization.writeValueAsBytes(request))
-                    .response().json(AuthorizationResponse.class).execute();
+            return callable.call();
         } catch (Exception cause) {
-            throw handleAndWrapException("Failed to obtain authorization data", cause);
+            return Throwables.retryAndWrapExceptionIfNecessary(callable, token, "Failed to obtain authorization data", cause);
         }
     }
 }
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java
index 785a3a6..b6628a9 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/PermissionResource.java
@@ -17,36 +17,205 @@
  */
 package org.keycloak.authorization.client.resource;
 
-import static org.keycloak.authorization.client.util.Throwables.handleAndWrapException;
-
+import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.Callable;
 
-import org.keycloak.authorization.client.representation.PermissionRequest;
-import org.keycloak.authorization.client.representation.PermissionResponse;
+import com.fasterxml.jackson.core.type.TypeReference;
+import org.keycloak.authorization.client.representation.ServerConfiguration;
 import org.keycloak.authorization.client.util.Http;
+import org.keycloak.authorization.client.util.Throwables;
+import org.keycloak.authorization.client.util.TokenCallable;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
+import org.keycloak.representations.idm.authorization.PermissionResponse;
+import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
 import org.keycloak.util.JsonSerialization;
 
 /**
+ * An entry point for managing permission tickets using the Protection API.
+ *
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class PermissionResource {
 
     private final Http http;
-    private final Callable<String> pat;
+    private final ServerConfiguration serverConfiguration;
+    private final TokenCallable pat;
 
-    public PermissionResource(Http http, Callable<String> pat) {
+    public PermissionResource(Http http, ServerConfiguration serverConfiguration, TokenCallable pat) {
         this.http = http;
+        this.serverConfiguration = serverConfiguration;
         this.pat = pat;
     }
 
+    /**
+     * @deprecated use {@link #create(PermissionRequest)}
+     * @param request
+     * @return
+     */
+    @Deprecated
     public PermissionResponse forResource(PermissionRequest request) {
+        return create(request);
+    }
+
+    /**
+     * Creates a new permission ticket for a single resource and scope(s).
+     *
+     * @param request the {@link PermissionRequest} representing the resource and scope(s) (not {@code null})
+     * @return a permission response holding a permission ticket with the requested permissions
+     */
+    public PermissionResponse create(PermissionRequest request) {
+        return create(Arrays.asList(request));
+    }
+
+    /**
+     * Creates a new permission ticket for a set of one or more resource and scope(s).
+     *
+     * @param request the {@link PermissionRequest} representing the resource and scope(s) (not {@code null})
+     * @return a permission response holding a permission ticket with the requested permissions
+     */
+    public PermissionResponse create(final List<PermissionRequest> requests) {
+        if (requests == null || requests.isEmpty()) {
+            throw new IllegalArgumentException("Permission request must not be null or empty");
+        }
+        Callable<PermissionResponse> callable = new Callable<PermissionResponse>() {
+            @Override
+            public PermissionResponse call() throws Exception {
+                return http.<PermissionResponse>post(serverConfiguration.getPermissionEndpoint())
+                        .authorizationBearer(pat.call())
+                        .json(JsonSerialization.writeValueAsBytes(requests))
+                        .response().json(PermissionResponse.class).execute();
+            }
+        };
+        try {
+            return callable.call();
+        } catch (Exception cause) {
+            return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error creating permission ticket", cause);
+        }
+    }
+
+    /**
+     * Query the server for any permission ticket associated with the given <code>scopeId</code>.
+     *
+     * @param scopeId the scope id (not {@code null})
+     * @return a list of permission tickets associated with the given <code>scopeId</code>
+     */
+    public List<PermissionTicketRepresentation> findByScope(final String scopeId) {
+        if (scopeId == null) {
+            throw new IllegalArgumentException("Scope id must not be null");
+        }
+        Callable<List<PermissionTicketRepresentation>> callable = new Callable<List<PermissionTicketRepresentation>>() {
+            @Override
+            public List<PermissionTicketRepresentation> call() throws Exception {
+                return http.<List<PermissionTicketRepresentation>>get(serverConfiguration.getPermissionEndpoint())
+                        .authorizationBearer(pat.call())
+                        .param("scopeId", scopeId)
+                        .response().json(new TypeReference<List<PermissionTicketRepresentation>>(){}).execute();
+            }
+        };
+        try {
+            return callable.call();
+        } catch (Exception cause) {
+            return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error querying permission ticket by scope", cause);
+        }
+    }
+
+    /**
+     * Query the server for any permission ticket associated with the given <code>resourceId</code>.
+     *
+     * @param resourceId the resource id (not {@code null})
+     * @return a list of permission tickets associated with the given <code>resourceId</code>
+     */
+    public List<PermissionTicketRepresentation> findByResource(final String resourceId) {
+        if (resourceId == null) {
+            throw new IllegalArgumentException("Resource id must not be null");
+        }
+        Callable<List<PermissionTicketRepresentation>> callable = new Callable<List<PermissionTicketRepresentation>>() {
+            @Override
+            public List<PermissionTicketRepresentation> call() throws Exception {
+                return http.<List<PermissionTicketRepresentation>>get(serverConfiguration.getPermissionEndpoint())
+                        .authorizationBearer(pat.call())
+                        .param("resourceId", resourceId)
+                        .response().json(new TypeReference<List<PermissionTicketRepresentation>>(){}).execute();
+            }
+        };
+        try {
+            return callable.call();
+        } catch (Exception cause) {
+            return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error querying permission ticket by resource", cause);
+        }
+    }
+
+    /**
+     * Query the server for any permission ticket with the matching arguments.
+     *
+     * @param resourceId the resource id or name
+     * @param scopeId the scope id or name
+     * @param owner the owner id or name
+     * @param requester the requester id or name
+     * @param granted if true, only permission tickets marked as granted are returned.
+     * @param returnNames if the response should include names for resource, scope and owner
+     * @param firstResult the position of the first resource to retrieve
+     * @param maxResult the maximum number of resources to retrieve
+     * @return a list of permission tickets with the matching arguments
+     */
+    public List<PermissionTicketRepresentation> find(final String resourceId,
+                                                     final String scopeId,
+                                                     final String owner,
+                                                     final String requester,
+                                                     final Boolean granted,
+                                                     final Boolean returnNames,
+                                                     final Integer firstResult,
+                                                     final Integer maxResult) {
+        Callable<List<PermissionTicketRepresentation>> callable = new Callable<List<PermissionTicketRepresentation>>() {
+            @Override
+            public List<PermissionTicketRepresentation> call() throws Exception {
+                return http.<List<PermissionTicketRepresentation>>get(serverConfiguration.getPermissionEndpoint())
+                        .authorizationBearer(pat.call())
+                        .param("resourceId", resourceId)
+                        .param("scopeId", scopeId)
+                        .param("owner", owner)
+                        .param("requester", requester)
+                        .param("granted", granted == null ? null : granted.toString())
+                        .param("returnNames", returnNames == null ? null : returnNames.toString())
+                        .param("firstResult", firstResult == null ? null : firstResult.toString())
+                        .param("maxResult", maxResult == null ? null : maxResult.toString())
+                        .response().json(new TypeReference<List<PermissionTicketRepresentation>>(){}).execute();
+            }
+        };
+        try {
+            return callable.call();
+        } catch (Exception cause) {
+            return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error querying permission ticket", cause);
+        }
+    }
+
+    /**
+     * Updates a permission ticket.
+     *
+     * @param ticket the permission ticket
+     */
+    public void update(final PermissionTicketRepresentation ticket) {
+        if (ticket == null) {
+            throw new IllegalArgumentException("Permission ticket must not be null or empty");
+        }
+        if (ticket.getId() == null) {
+            throw new IllegalArgumentException("Permission ticket must have an id");
+        }
+        Callable callable = new Callable() {
+            @Override
+            public Object call() throws Exception {
+                http.<List>put(serverConfiguration.getPermissionEndpoint())
+                        .json(JsonSerialization.writeValueAsBytes(ticket))
+                        .authorizationBearer(pat.call())
+                        .response().json(List.class).execute();
+                return null;
+            }
+        };
         try {
-            return this.http.<PermissionResponse>post("/authz/protection/permission")
-                    .authorizationBearer(this.pat.call())
-                    .json(JsonSerialization.writeValueAsBytes(request))
-                    .response().json(PermissionResponse.class).execute();
+            callable.call();
         } catch (Exception cause) {
-            throw handleAndWrapException("Error obtaining permission ticket", cause);
+            Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error updating permission ticket", cause);
         }
     }
 }
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
index fcf1e43..cc92712 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectedResource.java
@@ -17,88 +17,214 @@
  */
 package org.keycloak.authorization.client.resource;
 
-import static org.keycloak.authorization.client.util.Throwables.handleAndWrapException;
-
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.Callable;
 
-import org.keycloak.authorization.client.representation.RegistrationResponse;
 import org.keycloak.authorization.client.representation.ResourceRepresentation;
+import org.keycloak.authorization.client.representation.ServerConfiguration;
 import org.keycloak.authorization.client.util.Http;
+import org.keycloak.authorization.client.util.Throwables;
+import org.keycloak.authorization.client.util.TokenCallable;
 import org.keycloak.util.JsonSerialization;
 
 /**
+ * An entry point for managing resources using the Protection API.
+ *
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class ProtectedResource {
 
     private final Http http;
-    private final Callable<String> pat;
+    private ServerConfiguration serverConfiguration;
+    private final TokenCallable pat;
 
-    public ProtectedResource(Http http, Callable<String> pat) {
+    ProtectedResource(Http http, ServerConfiguration serverConfiguration, TokenCallable pat) {
         this.http = http;
+        this.serverConfiguration = serverConfiguration;
         this.pat = pat;
     }
 
-    public RegistrationResponse create(ResourceRepresentation resource) {
+    /**
+     * Creates a new resource.
+     *
+     * @param resource the resource data
+     * @return a {@link RegistrationResponse}
+     */
+    public ResourceRepresentation create(final ResourceRepresentation resource) {
+        Callable<ResourceRepresentation> callable = new Callable<ResourceRepresentation>() {
+            @Override
+            public ResourceRepresentation call() throws Exception {
+                return http.<ResourceRepresentation>post(serverConfiguration.getResourceRegistrationEndpoint())
+                        .authorizationBearer(pat.call())
+                        .json(JsonSerialization.writeValueAsBytes(resource))
+                        .response().json(ResourceRepresentation.class).execute();
+            }
+        };
         try {
-            return this.http.<RegistrationResponse>post("/authz/protection/resource_set")
-                    .authorizationBearer(this.pat.call())
-                    .json(JsonSerialization.writeValueAsBytes(resource))
-                    .response().json(RegistrationResponse.class).execute();
+            return callable.call();
         } catch (Exception cause) {
-            throw handleAndWrapException("Could not create resource", cause);
+            return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Could not create resource", cause);
         }
     }
 
-    public void update(ResourceRepresentation resource) {
+    /**
+     * Updates a resource.
+     *
+     * @param resource the resource data
+     * @return a {@link RegistrationResponse}
+     */
+    public void update(final ResourceRepresentation resource) {
+        if (resource.getId() == null) {
+            throw new IllegalArgumentException("You must provide the resource id");
+        }
+
+        Callable callable = new Callable() {
+            @Override
+            public Object call() throws Exception {
+                http.<ResourceRepresentation>put(serverConfiguration.getResourceRegistrationEndpoint() + "/" + resource.getId())
+                        .authorizationBearer(pat.call())
+                        .json(JsonSerialization.writeValueAsBytes(resource)).execute();
+                return null;
+            }
+        };
         try {
-            this.http.<RegistrationResponse>put("/authz/protection/resource_set/" + resource.getId())
-                    .authorizationBearer(this.pat.call())
-                    .json(JsonSerialization.writeValueAsBytes(resource)).execute();
+            callable.call();
         } catch (Exception cause) {
-            throw handleAndWrapException("Could not update resource", cause);
+            Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Could not update resource", cause);
         }
     }
 
-    public RegistrationResponse findById(String id) {
+    /**
+     * Query the server for a resource given its <code>id</code>.
+     *
+     * @param id the resource id
+     * @return a {@link ResourceRepresentation}
+     */
+    public ResourceRepresentation findById(final String id) {
+        Callable<ResourceRepresentation> callable = new Callable<ResourceRepresentation>() {
+            @Override
+            public ResourceRepresentation call() throws Exception {
+                return http.<ResourceRepresentation>get(serverConfiguration.getResourceRegistrationEndpoint() + "/" + id)
+                        .authorizationBearer(pat.call())
+                        .response().json(ResourceRepresentation.class).execute();
+            }
+        };
         try {
-            return this.http.<RegistrationResponse>get("/authz/protection/resource_set/" + id)
-                    .authorizationBearer(this.pat.call())
-                    .response().json(RegistrationResponse.class).execute();
+            return callable.call();
         } catch (Exception cause) {
-            throw handleAndWrapException("Could not find resource", cause);
+            return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Could not find resource", cause);
         }
     }
 
-    public Set<String> findByFilter(String filter) {
+    /**
+     * Query the server for a resource given its <code>name</code>.
+     *
+     * @param id the resource name
+     * @return a {@link ResourceRepresentation}
+     */
+    public ResourceRepresentation findByName(String name) {
+        String[] representations = find(null, name, null, null, null, null, null, null);
+
+        if (representations.length == 0) {
+            return null;
+        }
+
+        return findById(representations[0]);
+    }
+
+    /**
+     * Query the server for any resource with the matching arguments.
+     *
+     * @param id the resource id
+     * @param name the resource name
+     * @param uri the resource uri
+     * @param owner the resource owner
+     * @param type the resource type
+     * @param scope the resource scope
+     * @param firstResult the position of the first resource to retrieve
+     * @param maxResult the maximum number of resources to retrieve
+     * @return an array of strings with the resource ids
+     */
+    public String[] find(final String id, final String name, final String uri, final String owner, final String type, final String scope, final Integer firstResult, final Integer maxResult) {
+        Callable<String[]> callable = new Callable<String[]>() {
+            @Override
+            public String[] call() throws Exception {
+                return http.<String[]>get(serverConfiguration.getResourceRegistrationEndpoint())
+                        .authorizationBearer(pat.call())
+                        .param("_id", id)
+                        .param("name", name)
+                        .param("uri", uri)
+                        .param("owner", owner)
+                        .param("type", type)
+                        .param("scope", scope)
+                        .param("deep", Boolean.FALSE.toString())
+                        .param("first", firstResult != null ? firstResult.toString() : null)
+                        .param("max", maxResult != null ? maxResult.toString() : null)
+                        .response().json(String[].class).execute();
+            }
+        };
         try {
-            return this.http.<Set>get("/authz/protection/resource_set")
-                    .authorizationBearer(this.pat.call())
-                    .param("filter", filter)
-                    .response().json(Set.class).execute();
+            return callable.call();
         } catch (Exception cause) {
-            throw handleAndWrapException("Could not find resource", cause);
+            return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Could not find resource", cause);
         }
     }
 
-    public Set<String> findAll() {
+    /**
+     * Query the server for all resources.
+     *
+     * @return @return an array of strings with the resource ids
+     */
+    public String[] findAll() {
         try {
-            return this.http.<Set>get("/authz/protection/resource_set")
-                    .authorizationBearer(this.pat.call())
-                    .response().json(Set.class).execute();
+            return find(null,null , null, null, null, null, null, null);
         } catch (Exception cause) {
-            throw handleAndWrapException("Could not find resource", cause);
+            throw Throwables.handleWrapException("Could not find resource", cause);
         }
     }
 
-    public void delete(String id) {
+    /**
+     * Deletes a resource with the given <code>id</code>.
+     *
+     * @param id the resource id
+     */
+    public void delete(final String id) {
+        Callable callable = new Callable() {
+            @Override
+            public Object call() throws Exception {
+                http.delete(serverConfiguration.getResourceRegistrationEndpoint() + "/" + id)
+                        .authorizationBearer(pat.call())
+                        .execute();
+                return null;
+            }
+        };
         try {
-            this.http.delete("/authz/protection/resource_set/" + id)
-                    .authorizationBearer(this.pat.call())
-                    .execute();
+            callable.call();
         } catch (Exception cause) {
-            throw handleAndWrapException("Could not delete resource", cause);
+            Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "", cause);
         }
     }
+
+    /**
+     * Query the server for all resources with the given uri.
+     *
+     * @param uri the resource uri
+     */
+    public List<ResourceRepresentation> findByUri(String uri) {
+        String[] ids = find(null, null, uri, null, null, null, null, null);
+
+        if (ids.length == 0) {
+            return Collections.emptyList();
+        }
+
+        List<ResourceRepresentation> representations = new ArrayList<>();
+
+        for (String id : ids) {
+            representations.add(findById(id));
+        }
+
+        return representations;
+    }
 }
\ No newline at end of file
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
index 3d2eb2c..7268fe9 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
@@ -17,38 +17,58 @@
  */
 package org.keycloak.authorization.client.resource;
 
-import java.util.concurrent.Callable;
-
+import org.keycloak.authorization.client.representation.ServerConfiguration;
 import org.keycloak.authorization.client.representation.TokenIntrospectionResponse;
 import org.keycloak.authorization.client.util.Http;
+import org.keycloak.authorization.client.util.TokenCallable;
 
 /**
+ * An entry point to access the Protection API endpoints.
+ *
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class ProtectionResource {
 
-    private final Callable<String> pat;
+    private final TokenCallable pat;
     private final Http http;
+    private ServerConfiguration serverConfiguration;
 
-    public ProtectionResource(Http http, Callable<String> pat) {
+    public ProtectionResource(Http http, ServerConfiguration serverConfiguration, TokenCallable pat) {
         if (pat == null) {
             throw new RuntimeException("No access token was provided when creating client for Protection API.");
         }
 
         this.http = http;
+        this.serverConfiguration = serverConfiguration;
         this.pat = pat;
     }
 
+    /**
+     * Creates a {@link ProtectedResource} which can be used to manage resources.
+     *
+     * @return a {@link ProtectedResource}
+     */
     public ProtectedResource resource() {
-        return new ProtectedResource(http, pat);
+        return new ProtectedResource(http, serverConfiguration, pat);
     }
 
+    /**
+     * Creates a {@link PermissionResource} which can be used to manage permission tickets.
+     *
+     * @return a {@link PermissionResource}
+     */
     public PermissionResource permission() {
-        return new PermissionResource(http, pat);
+        return new PermissionResource(http, serverConfiguration, pat);
     }
 
+    /**
+     * Introspects the given <code>rpt</code> using the token introspection endpoint.
+     *
+     * @param rpt the rpt to introspect
+     * @return the {@link TokenIntrospectionResponse}
+     */
     public TokenIntrospectionResponse introspectRequestingPartyToken(String rpt) {
-        return this.http.<TokenIntrospectionResponse>post("/protocol/openid-connect/token/introspect")
+        return this.http.<TokenIntrospectionResponse>post(serverConfiguration.getTokenIntrospectionEndpoint())
                 .authentication()
                     .client()
                 .form()
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/Http.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/Http.java
index f72e6b7..eecb7e3 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/util/Http.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/Http.java
@@ -22,8 +22,6 @@ import org.keycloak.authorization.client.ClientAuthenticator;
 import org.keycloak.authorization.client.Configuration;
 import org.keycloak.authorization.client.representation.ServerConfiguration;
 
-import java.net.URI;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -39,27 +37,19 @@ public class Http {
     }
 
     public <R> HttpMethod<R> get(String path) {
-        return method(RequestBuilder.get().setUri(this.serverConfiguration.getIssuer() + path));
-    }
-
-    public <R> HttpMethod<R> get(URI path) {
         return method(RequestBuilder.get().setUri(path));
     }
 
-    public <R> HttpMethod<R> post(URI path) {
-        return method(RequestBuilder.post().setUri(path));
-    }
-
     public <R> HttpMethod<R> post(String path) {
-        return method(RequestBuilder.post().setUri(this.serverConfiguration.getIssuer() + path));
+        return method(RequestBuilder.post().setUri(path));
     }
 
     public <R> HttpMethod<R> put(String path) {
-        return method(RequestBuilder.put().setUri(this.serverConfiguration.getIssuer() + path));
+        return method(RequestBuilder.put().setUri(path));
     }
 
     public <R> HttpMethod<R> delete(String path) {
-        return method(RequestBuilder.delete().setUri(this.serverConfiguration.getIssuer() + path));
+        return method(RequestBuilder.delete().setUri(path));
     }
 
     private <R> HttpMethod<R> method(RequestBuilder builder) {
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethod.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethod.java
index 9a7e51a..230b7f8 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethod.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethod.java
@@ -43,17 +43,17 @@ public class HttpMethod<R> {
 
     private final HttpClient httpClient;
     private final ClientAuthenticator authenticator;
-    private final RequestBuilder builder;
+    protected final RequestBuilder builder;
     protected final Configuration configuration;
-    protected final HashMap<String, String> headers;
-    protected final HashMap<String, String> params;
+    protected final Map<String, String> headers;
+    protected final Map<String, List<String>> params;
     private HttpMethodResponse<R> response;
 
     public HttpMethod(Configuration configuration, ClientAuthenticator authenticator, RequestBuilder builder) {
-        this(configuration, authenticator, builder, new HashMap<String, String>(), new HashMap<String, String>());
+        this(configuration, authenticator, builder, new HashMap<String, List<String>>(), new HashMap<String, String>());
     }
 
-    public HttpMethod(Configuration configuration, ClientAuthenticator authenticator, RequestBuilder builder, HashMap<String, String> params, HashMap<String, String> headers) {
+    public HttpMethod(Configuration configuration, ClientAuthenticator authenticator, RequestBuilder builder, Map<String, List<String>> params, Map<String, String> headers) {
         this.configuration = configuration;
         this.httpClient = configuration.getHttpClient();
         this.authenticator = authenticator;
@@ -108,8 +108,10 @@ public class HttpMethod<R> {
     }
 
     protected void preExecute(RequestBuilder builder) {
-        for (Map.Entry<String, String> param : params.entrySet()) {
-            builder.addParameter(param.getKey(), param.getValue());
+        for (Map.Entry<String, List<String>> param : params.entrySet()) {
+            for (String value : param.getValue()) {
+                builder.addParameter(param.getKey(), value);
+            }
         }
     }
 
@@ -128,7 +130,30 @@ public class HttpMethod<R> {
     }
 
     public HttpMethod<R> param(String name, String value) {
-        this.params.put(name, value);
+        if (value != null) {
+            List<String> values = params.get(name);
+
+            if (values == null || !values.isEmpty()) {
+                values = new ArrayList<>();
+                params.put(name, values);
+            }
+
+            values.add(value);
+        }
+        return this;
+    }
+
+    public HttpMethod<R> params(String name, String value) {
+        if (value != null) {
+            List<String> values = params.get(name);
+
+            if (values == null) {
+                values = new ArrayList<>();
+                params.put(name, values);
+            }
+
+            values.add(value);
+        }
         return this;
     }
 
@@ -145,8 +170,10 @@ public class HttpMethod<R> {
                 if (params != null) {
                     List<NameValuePair> formparams = new ArrayList<>();
 
-                    for (Map.Entry<String, String> param : params.entrySet()) {
-                        formparams.add(new BasicNameValuePair(param.getKey(), param.getValue()));
+                    for (Map.Entry<String, List<String>> param : params.entrySet()) {
+                        for (String value : param.getValue()) {
+                            formparams.add(new BasicNameValuePair(param.getKey(), value));
+                        }
                     }
 
                     try {
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java
index 8807d39..33674fb 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java
@@ -17,8 +17,16 @@
  */
 package org.keycloak.authorization.client.util;
 
+import java.util.Arrays;
+import java.util.Set;
+
+import org.apache.http.Header;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.authorization.client.ClientAuthenticator;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest.Metadata;
+import org.keycloak.representations.idm.authorization.PermissionTicketToken;
+import org.keycloak.representations.idm.authorization.PermissionTicketToken.ResourcePermission;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -34,16 +42,84 @@ public class HttpMethodAuthenticator<R> {
     }
 
     public HttpMethod<R> client() {
-        this.method.params.put(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS);
+        this.method.params.put(OAuth2Constants.GRANT_TYPE, Arrays.asList(OAuth2Constants.CLIENT_CREDENTIALS));
         authenticator.configureClientCredentials(this.method.params, this.method.headers);
         return this.method;
     }
 
     public HttpMethod<R> oauth2ResourceOwnerPassword(String userName, String password) {
         client();
-        this.method.params.put(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD);
-        this.method.params.put("username", userName);
-        this.method.params.put("password", password);
+        this.method.params.put(OAuth2Constants.GRANT_TYPE, Arrays.asList(OAuth2Constants.PASSWORD));
+        this.method.params.put("username", Arrays.asList(userName));
+        this.method.params.put("password", Arrays.asList(password));
         return this.method;
     }
+
+    public HttpMethod<R> uma() {
+        // if there is an authorization bearer header authenticate using bearer token
+        Header authorizationHeader = method.builder.getFirstHeader("Authorization");
+
+        if (!(authorizationHeader != null && authorizationHeader.getValue().toLowerCase().startsWith("bearer"))) {
+            client();
+        }
+
+        method.params.put(OAuth2Constants.GRANT_TYPE, Arrays.asList(OAuth2Constants.UMA_GRANT_TYPE));
+        return method;
+    }
+
+    public HttpMethod<R> uma(AuthorizationRequest request) {
+        String ticket = request.getTicket();
+        PermissionTicketToken permissions = request.getPermissions();
+
+        if (ticket == null && permissions == null) {
+            throw new IllegalArgumentException("You must either provide a permission ticket or the permissions you want to request.");
+        }
+
+        uma();
+        method.param("ticket", ticket);
+        method.param("claim_token", request.getClaimToken());
+        method.param("claim_token_format", request.getClaimTokenFormat());
+        method.param("pct", request.getPct());
+        method.param("rpt", request.getRpt());
+        method.param("scope", request.getScope());
+        method.param("audience", request.getAudience());
+
+        if (permissions != null) {
+            for (ResourcePermission permission : permissions.getResources()) {
+                String resourceId = permission.getResourceId();
+                Set<String> scopes = permission.getScopes();
+                StringBuilder value = new StringBuilder();
+
+                if (resourceId != null) {
+                    value.append(resourceId);
+                }
+
+                if (scopes != null && !scopes.isEmpty()) {
+                    value.append("#");
+                    for (String scope : scopes) {
+                        if (!value.toString().endsWith("#")) {
+                            value.append(",");
+                        }
+                        value.append(scope);
+                    }
+                }
+
+                method.params("permission", value.toString());
+            }
+        }
+
+        Metadata metadata = request.getMetadata();
+
+        if (metadata != null) {
+            if (metadata.getIncludeResourceName() != null) {
+                method.param("response_include_resource_name", metadata.getIncludeResourceName().toString());
+            }
+
+            if (metadata.getLimit() != null) {
+                method.param("response_permissions_limit", metadata.getLimit().toString());
+            }
+        }
+
+        return method;
+    }
 }
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodResponse.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodResponse.java
index fceca19..7cfba8e 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodResponse.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodResponse.java
@@ -17,10 +17,12 @@
  */
 package org.keycloak.authorization.client.util;
 
-import org.keycloak.util.JsonSerialization;
-
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 
+import com.fasterxml.jackson.core.type.TypeReference;
+import org.keycloak.util.JsonSerialization;
+
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -58,4 +60,22 @@ public class HttpMethodResponse<R> {
             }
         };
     }
+
+    public HttpMethodResponse<R> json(final TypeReference responseType) {
+        return new HttpMethodResponse<R>(this.method) {
+            @Override
+            public R execute() {
+                return method.execute(new HttpResponseProcessor<R>() {
+                    @Override
+                    public R process(byte[] entity) {
+                        try {
+                            return (R) JsonSerialization.readValue(new ByteArrayInputStream(entity), responseType);
+                        } catch (IOException e) {
+                            throw new RuntimeException("Error parsing JSON response.", e);
+                        }
+                    }
+                });
+            }
+        };
+    }
 }
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/Throwables.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/Throwables.java
index d51b27e..ae2eaf1 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/util/Throwables.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/Throwables.java
@@ -16,7 +16,10 @@
  */
 package org.keycloak.authorization.client.util;
 
+import java.util.concurrent.Callable;
+
 import org.keycloak.authorization.client.AuthorizationDeniedException;
+import org.keycloak.authorization.client.representation.TokenIntrospectionResponse;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -24,19 +27,68 @@ import org.keycloak.authorization.client.AuthorizationDeniedException;
 public final class Throwables {
 
     /**
-     * Handles an {@code exception} and wraps it into a {@link RuntimeException}. The resulting exception contains
-     * more details in case the given {@code exception} is of a {@link HttpResponseException}.
+     * Handles an {@code cause} and wraps it into a {@link RuntimeException}. The resulting cause contains
+     * more details in case the given {@code cause} is of a {@link HttpResponseException}.
+     *
+     *
+     * @param callable
+     * @param pat
+     * @param message the message
+     * @param cause the root cause
+     * @return a {@link RuntimeException} wrapping the given {@code cause}
+     */
+    public static RuntimeException handleWrapException(String message, Throwable cause) {
+        if (cause instanceof HttpResponseException) {
+            throw handleAndWrapHttpResponseException(message, HttpResponseException.class.cast(cause));
+        }
+
+        return new RuntimeException(message, cause);
+    }
+
+    /**
+     * <p>Retries the given {@code callable} after obtaining a fresh {@code token} from the server. If the attempt to retry fails
+     * the exception is handled as defined by {@link #handleWrapException(String, Throwable)}.
      *
+     * <p>A retry is only attempted in case the {@code cause} is a {@link HttpResponseException} with a 403 status code. In some cases the
+     * session associated with the token is no longer valid and a new token must be issues.
+     *
+     * @param callable the callable to retry
+     * @param token the token
      * @param message the message
-     * @param exception the root exception
-     * @return a {@link RuntimeException} wrapping the given {@code exception}
+     * @param cause the cause
+     * @param <V> the result of the callable
+     * @return the result of the callable
+     * @throws RuntimeException in case the attempt to retry fails
      */
-    public static RuntimeException handleAndWrapException(String message, Exception exception) {
-        if (exception instanceof HttpResponseException) {
-            throw handleAndWrapHttpResponseException(message, HttpResponseException.class.cast(exception));
+    public static <V> V retryAndWrapExceptionIfNecessary(Callable<V> callable, TokenCallable token, String message, Throwable cause) throws RuntimeException {
+        if (token == null || !token.isRetry()) {
+            throw handleWrapException(message, cause);
+        }
+
+        if (cause instanceof HttpResponseException) {
+            HttpResponseException httpe = HttpResponseException.class.cast(cause);
+
+            if (httpe.getStatusCode() == 403) {
+                TokenIntrospectionResponse response = token.getHttp().<TokenIntrospectionResponse>post(token.getServerConfiguration().getTokenIntrospectionEndpoint())
+                        .authentication()
+                        .client()
+                        .param("token", token.call())
+                        .response().json(TokenIntrospectionResponse.class).execute();
+
+                if (!response.getActive()) {
+                    token.clearToken();
+                    try {
+                        return callable.call();
+                    } catch (Exception e) {
+                        throw handleWrapException(message, e);
+                    }
+                }
+
+                throw handleWrapException(message, cause);
+            }
         }
 
-        return new RuntimeException(message, exception);
+        throw new RuntimeException(message, cause);
     }
 
     private static RuntimeException handleAndWrapHttpResponseException(String message, HttpResponseException exception) {
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/TokenCallable.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/TokenCallable.java
new file mode 100644
index 0000000..b1c3280
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/TokenCallable.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018 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.authorization.client.util;
+
+import java.util.concurrent.Callable;
+
+import org.keycloak.authorization.client.Configuration;
+import org.keycloak.authorization.client.representation.ServerConfiguration;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.util.JsonSerialization;
+
+public class TokenCallable implements Callable<String> {
+
+    private final String userName;
+    private final String password;
+    private final Http http;
+    private final Configuration configuration;
+    private final ServerConfiguration serverConfiguration;
+    private AccessTokenResponse clientToken;
+
+    public TokenCallable(String userName, String password, Http http, Configuration configuration, ServerConfiguration serverConfiguration) {
+        this.userName = userName;
+        this.password = password;
+        this.http = http;
+        this.configuration = configuration;
+        this.serverConfiguration = serverConfiguration;
+    }
+
+    public TokenCallable(Http http, Configuration configuration, ServerConfiguration serverConfiguration) {
+        this(null, null, http, configuration, serverConfiguration);
+    }
+
+    @Override
+    public String call() {
+        if (clientToken == null) {
+            if (userName == null || password == null) {
+                clientToken = obtainAccessToken();
+            } else {
+                clientToken = obtainAccessToken(userName, password);
+            }
+        }
+
+        String token = clientToken.getToken();
+
+        try {
+            AccessToken accessToken = JsonSerialization.readValue(new JWSInput(token).getContent(), AccessToken.class);
+
+            if (accessToken.isActive()) {
+                return token;
+            }
+
+            clientToken = http.<AccessTokenResponse>post(serverConfiguration.getTokenEndpoint())
+                    .authentication().client()
+                    .form()
+                    .param("grant_type", "refresh_token")
+                    .param("refresh_token", clientToken.getRefreshToken())
+                    .response()
+                    .json(AccessTokenResponse.class)
+                    .execute();
+        } catch (Exception e) {
+            clientToken = null;
+            throw new RuntimeException(e);
+        }
+
+        return clientToken.getToken();
+    }
+
+    /**
+     * Obtains an access token using the client credentials.
+     *
+     * @return an {@link AccessTokenResponse}
+     */
+    AccessTokenResponse obtainAccessToken() {
+        return this.http.<AccessTokenResponse>post(this.serverConfiguration.getTokenEndpoint())
+                .authentication()
+                .client()
+                .response()
+                .json(AccessTokenResponse.class)
+                .execute();
+    }
+
+    /**
+     * Obtains an access token using the resource owner credentials.
+     *
+     * @return an {@link AccessTokenResponse}
+     */
+    AccessTokenResponse obtainAccessToken(String userName, String password) {
+        return this.http.<AccessTokenResponse>post(this.serverConfiguration.getTokenEndpoint())
+                .authentication()
+                .oauth2ResourceOwnerPassword(userName, password)
+                .response()
+                .json(AccessTokenResponse.class)
+                .execute();
+    }
+
+    Http getHttp() {
+        return http;
+    }
+
+    protected boolean isRetry() {
+        return true;
+    }
+
+    Configuration getConfiguration() {
+        return configuration;
+    }
+
+    ServerConfiguration getServerConfiguration() {
+        return serverConfiguration;
+    }
+
+    void clearToken() {
+        clientToken = null;
+    }
+}
diff --git a/common/src/main/java/org/keycloak/common/util/Time.java b/common/src/main/java/org/keycloak/common/util/Time.java
index 54809d8..e48f217 100644
--- a/common/src/main/java/org/keycloak/common/util/Time.java
+++ b/common/src/main/java/org/keycloak/common/util/Time.java
@@ -52,6 +52,15 @@ public class Time {
     }
 
     /**
+     * Returns {@link Date} object, its value set to time
+     * @param time Time in milliseconds since the epoch
+     * @return see description
+     */
+    public static Date toDate(long time) {
+        return new Date(time);
+    }
+
+    /**
      * Returns time in milliseconds for a time in seconds. No adjustment is made to the parameter.
      * @param time Time in seconds since the epoch
      * @return Time in milliseconds
diff --git a/core/src/main/java/org/keycloak/AuthorizationContext.java b/core/src/main/java/org/keycloak/AuthorizationContext.java
index e096e7e..0a9b332 100644
--- a/core/src/main/java/org/keycloak/AuthorizationContext.java
+++ b/core/src/main/java/org/keycloak/AuthorizationContext.java
@@ -68,7 +68,7 @@ public class AuthorizationContext {
         if (hasResourcePermission(resourceName)) {
             for (Permission permission : authorization.getPermissions()) {
                 for (PathConfig pathHolder : paths.values()) {
-                    if (pathHolder.getId().equals(permission.getResourceSetId())) {
+                    if (pathHolder.getId().equals(permission.getResourceId())) {
                         if (permission.getScopes().contains(scopeName)) {
                             return true;
                         }
@@ -98,7 +98,7 @@ public class AuthorizationContext {
         }
 
         for (Permission permission : authorization.getPermissions()) {
-            if (permission.getResourceSetName().equals(resourceName) || permission.getResourceSetId().equals(resourceName)) {
+            if (permission.getResourceName().equals(resourceName) || permission.getResourceId().equals(resourceName)) {
                 return true;
             }
         }
diff --git a/core/src/main/java/org/keycloak/OAuth2Constants.java b/core/src/main/java/org/keycloak/OAuth2Constants.java
index 59e0eee..df54112 100644
--- a/core/src/main/java/org/keycloak/OAuth2Constants.java
+++ b/core/src/main/java/org/keycloak/OAuth2Constants.java
@@ -111,6 +111,8 @@ public interface OAuth2Constants {
     String JWT_TOKEN_TYPE="urn:ietf:params:oauth:token-type:jwt";
     String ID_TOKEN_TYPE="urn:ietf:params:oauth:token-type:id_token";
 
+    String UMA_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:uma-ticket";
+
 
 }
 
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
index 26dc220..89dadbf 100644
--- a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
@@ -17,14 +17,13 @@
  */
 package org.keycloak.representations.adapters.config;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -37,26 +36,18 @@ public class PolicyEnforcerConfig {
     @JsonProperty("enforcement-mode")
     private EnforcementMode enforcementMode = EnforcementMode.ENFORCING;
 
-    @JsonProperty("user-managed-access")
-    @JsonInclude(JsonInclude.Include.NON_NULL)
-    private UmaProtocolConfig userManagedAccess;
-
-    @JsonProperty("entitlement")
-    @JsonInclude(JsonInclude.Include.NON_NULL)
-    private EntitlementProtocolConfig entitlement;
-
     @JsonProperty("paths")
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
     private List<PathConfig> paths = new ArrayList<>();
 
-    @JsonProperty("online-introspection")
-    @JsonInclude(JsonInclude.Include.NON_NULL)
-    private Boolean onlineIntrospection = Boolean.FALSE;
-
     @JsonProperty("on-deny-redirect-to")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private String onDenyRedirectTo;
 
+    @JsonProperty("user-managed-access")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private UserManagedAccessConfig userManagedAccess;
+
     public Boolean isCreateResources() {
         return this.createResources;
     }
@@ -73,26 +64,14 @@ public class PolicyEnforcerConfig {
         this.enforcementMode = enforcementMode;
     }
 
-    public UmaProtocolConfig getUserManagedAccess() {
+    public UserManagedAccessConfig getUserManagedAccess() {
         return this.userManagedAccess;
     }
 
-    public EntitlementProtocolConfig getEntitlement() {
-        return this.entitlement;
-    }
-
-    public Boolean isOnlineIntrospection() {
-        return onlineIntrospection;
-    }
-
     public void setCreateResources(Boolean createResources) {
         this.createResources = createResources;
     }
 
-    public void setOnlineIntrospection(Boolean onlineIntrospection) {
-        this.onlineIntrospection = onlineIntrospection;
-    }
-
     public void setPaths(List<PathConfig> paths) {
         this.paths = paths;
     }
@@ -101,14 +80,10 @@ public class PolicyEnforcerConfig {
         return onDenyRedirectTo;
     }
 
-    public void setUserManagedAccess(UmaProtocolConfig userManagedAccess) {
+    public void setUserManagedAccess(UserManagedAccessConfig userManagedAccess) {
         this.userManagedAccess = userManagedAccess;
     }
 
-    public void setEntitlement(EntitlementProtocolConfig entitlement) {
-        this.entitlement = entitlement;
-    }
-
     public void setOnDenyRedirectTo(String onDenyRedirectTo) {
         this.onDenyRedirectTo = onDenyRedirectTo;
     }
@@ -259,11 +234,7 @@ public class PolicyEnforcerConfig {
         ANY
     }
 
-    public static class UmaProtocolConfig {
-
-    }
-
-    public static class EntitlementProtocolConfig {
+    public static class UserManagedAccessConfig {
 
     }
 }
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationRequest.java b/core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationRequest.java
new file mode 100644
index 0000000..764ae02
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationRequest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2018 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.representations.idm.authorization;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import org.keycloak.representations.idm.authorization.PermissionTicketToken.ResourcePermission;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class AuthorizationRequest {
+
+    private String ticket;
+    private String rpt;
+    private String claimToken;
+    private String claimTokenFormat;
+    private String pct;
+    private String scope;
+    private PermissionTicketToken permissions = new PermissionTicketToken();
+    private Metadata metadata;
+    private String audience;
+    private String accessToken;
+    private boolean submitRequest;
+
+    public AuthorizationRequest(String ticket) {
+        this.ticket = ticket;
+    }
+
+    public AuthorizationRequest() {
+        this(null);
+    }
+
+    public String getTicket() {
+        return this.ticket;
+    }
+
+    public void setTicket(String ticket) {
+        this.ticket = ticket;
+    }
+
+    public String getRpt() {
+        return this.rpt;
+    }
+
+    public void setRpt(String rpt) {
+        this.rpt = rpt;
+    }
+
+    public void setClaimToken(String claimToken) {
+        this.claimToken = claimToken;
+    }
+
+    public String getClaimToken() {
+        return claimToken;
+    }
+
+    public void setClaimTokenFormat(String claimTokenFormat) {
+        this.claimTokenFormat = claimTokenFormat;
+    }
+
+    public String getClaimTokenFormat() {
+        return claimTokenFormat;
+    }
+
+    public void setPct(String pct) {
+        this.pct = pct;
+    }
+
+    public String getPct() {
+        return pct;
+    }
+
+    public void setScope(String scope) {
+        this.scope = scope;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public void setPermissions(PermissionTicketToken permissions) {
+        this.permissions = permissions;
+    }
+
+    public PermissionTicketToken getPermissions() {
+        return permissions;
+    }
+
+    public Metadata getMetadata() {
+        return metadata;
+    }
+
+    public void setMetadata(Metadata metadata) {
+        this.metadata = metadata;
+    }
+
+    public void setAudience(String audience) {
+        this.audience = audience;
+    }
+
+    public String getAudience() {
+        return audience;
+    }
+
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public void addPermission(String resourceId, List<String> scopes) {
+        addPermission(resourceId, scopes.toArray(new String[scopes.size()]));
+    }
+
+    public void addPermission(String resourceId, String... scopes) {
+        if (permissions == null) {
+            permissions = new PermissionTicketToken(new ArrayList<ResourcePermission>());
+        }
+
+        ResourcePermission permission = null;
+
+        for (ResourcePermission resourcePermission : permissions.getResources()) {
+            if (resourcePermission.getResourceId().equals(resourceId)) {
+                permission = resourcePermission;
+                break;
+            }
+        }
+
+        if (permission == null) {
+            permission = new ResourcePermission(resourceId, new HashSet<String>());
+            permissions.getResources().add(permission);
+        }
+
+        permission.getScopes().addAll(Arrays.asList(scopes));
+    }
+
+    public void setSubmitRequest(boolean submitRequest) {
+        this.submitRequest = submitRequest;
+    }
+
+    public boolean isSubmitRequest() {
+        return submitRequest && ticket != null;
+    }
+
+    public static class Metadata {
+
+        private Boolean includeResourceName;
+        private Integer limit;
+
+        public Boolean getIncludeResourceName() {
+            if (includeResourceName == null) {
+                includeResourceName = Boolean.TRUE;
+            }
+            return includeResourceName;
+        }
+
+        public void setIncludeResourceName(Boolean includeResourceName) {
+            this.includeResourceName = includeResourceName;
+        }
+
+        public Integer getLimit() {
+            return limit;
+        }
+
+        public void setLimit(Integer limit) {
+            this.limit = limit;
+        }
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/Permission.java b/core/src/main/java/org/keycloak/representations/idm/authorization/Permission.java
index 74df64f..ed392f0 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/Permission.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/Permission.java
@@ -28,11 +28,11 @@ import java.util.Set;
  */
 public class Permission {
 
-    @JsonProperty("resource_set_id")
-    private String resourceSetId;
+    @JsonProperty("rsid")
+    private String resourceId;
 
-    @JsonProperty("resource_set_name")
-    private final String resourceSetName;
+    @JsonProperty("rsname")
+    private final String resourceName;
 
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
     private Set<String> scopes;
@@ -44,19 +44,19 @@ public class Permission {
         this(null, null, null, null);
     }
 
-    public Permission(final String resourceSetId, String resourceSetName, final Set<String> scopes, Map<String, Set<String>> claims) {
-        this.resourceSetId = resourceSetId;
-        this.resourceSetName = resourceSetName;
+    public Permission(final String resourceId, String resourceName, final Set<String> scopes, Map<String, Set<String>> claims) {
+        this.resourceId = resourceId;
+        this.resourceName = resourceName;
         this.scopes = scopes;
         this.claims = claims;
     }
 
-    public String getResourceSetId() {
-        return this.resourceSetId;
+    public String getResourceId() {
+        return this.resourceId;
     }
 
-    public String getResourceSetName() {
-        return this.resourceSetName;
+    public String getResourceName() {
+        return this.resourceName;
     }
 
     public Set<String> getScopes() {
@@ -75,7 +75,7 @@ public class Permission {
     public String toString() {
         StringBuilder builder = new StringBuilder();
 
-        builder.append("Permission {").append("id=").append(resourceSetId).append(", name=").append(resourceSetName)
+        builder.append("Permission {").append("id=").append(resourceId).append(", name=").append(resourceName)
                 .append(", scopes=").append(scopes).append("}");
 
         return builder.toString();
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/PermissionTicketRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/PermissionTicketRepresentation.java
new file mode 100644
index 0000000..2a3e020
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/PermissionTicketRepresentation.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 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.representations.idm.authorization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionTicketRepresentation {
+
+    private String id;
+    private String owner;
+    private String resource;
+    private String scope;
+    private boolean granted;
+    private String scopeName;
+    private String resourceName;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getOwner() {
+        return owner;
+    }
+
+    public void setOwner(String owner) {
+        this.owner = owner;
+    }
+
+    public String getResource() {
+        return resource;
+    }
+
+    public void setResource(String resource) {
+        this.resource = resource;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public void setScope(String scope) {
+        this.scope = scope;
+    }
+
+    public boolean isGranted() {
+        return granted;
+    }
+
+    public void setGranted(boolean granted) {
+        this.granted = granted;
+    }
+
+    public void setScopeName(String scopeName) {
+        this.scopeName = scopeName;
+    }
+
+    public String getScopeName() {
+        return scopeName;
+    }
+
+    public void setResourceName(String resourceName) {
+        this.resourceName = resourceName;
+    }
+
+    public String getResourceName() {
+        return resourceName;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceOwnerRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceOwnerRepresentation.java
index c058b9d..4188ab3 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceOwnerRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceOwnerRepresentation.java
@@ -24,6 +24,14 @@ public class ResourceOwnerRepresentation {
     private String id;
     private String name;
 
+    public ResourceOwnerRepresentation() {
+
+    }
+
+    public ResourceOwnerRepresentation(String id) {
+        this.id = id;
+    }
+
     public String getId() {
         return this.id;
     }
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
index acbd2f2..ae876f0 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
@@ -47,10 +47,12 @@ public class ResourceRepresentation {
     @JsonProperty("icon_uri")
     private String iconUri;
     private ResourceOwnerRepresentation owner;
+    private Boolean ownerManagedAccess;
 
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
     private List<PolicyRepresentation> policies;
     private List<ScopeRepresentation> typedScopes;
+    private String displayName;
 
     /**
      * Creates a new instance.
@@ -121,6 +123,10 @@ public class ResourceRepresentation {
         return this.name;
     }
 
+    public String getDisplayName() {
+        return displayName;
+    }
+
     public String getUri() {
         return this.uri;
     }
@@ -145,6 +151,10 @@ public class ResourceRepresentation {
         this.name = name;
     }
 
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
     public void setUri(String uri) {
         this.uri = uri;
     }
@@ -169,6 +179,14 @@ public class ResourceRepresentation {
         this.owner = owner;
     }
 
+    public Boolean getOwnerManagedAccess() {
+        return ownerManagedAccess;
+    }
+
+    public void setOwnerManagedAccess(Boolean ownerManagedAccess) {
+        this.ownerManagedAccess = ownerManagedAccess;
+    }
+
     public void setTypedScopes(List<ScopeRepresentation> typedScopes) {
         this.typedScopes = typedScopes;
     }
@@ -177,6 +195,15 @@ public class ResourceRepresentation {
         return typedScopes;
     }
 
+    public void addScope(String... scopeNames) {
+        if (scopes == null) {
+            scopes = new HashSet<>();
+        }
+        for (String scopeName : scopeNames) {
+            scopes.add(new ScopeRepresentation(scopeName));
+        }
+    }
+
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ScopeRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ScopeRepresentation.java
index 3a1f252..e6445e4 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/ScopeRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ScopeRepresentation.java
@@ -35,6 +35,7 @@ public class ScopeRepresentation {
     private String iconUri;
     private List<PolicyRepresentation> policies;
     private List<ResourceRepresentation> resources;
+    private String displayName;
 
     /**
      * Creates an instance.
@@ -67,6 +68,10 @@ public class ScopeRepresentation {
         return this.name;
     }
 
+    public String getDisplayName() {
+        return displayName;
+    }
+
     public String getIconUri() {
         return this.iconUri;
     }
@@ -83,6 +88,10 @@ public class ScopeRepresentation {
         this.name = name;
     }
 
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
     public void setIconUri(String iconUri) {
         this.iconUri = iconUri;
     }
diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
index cd52d7c..1c039d3 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -145,6 +145,8 @@ public class RealmRepresentation {
 
     protected String keycloakVersion;
 
+    protected Boolean userManagedAccessAllowed;
+
     @Deprecated
     protected Boolean social;
     @Deprecated
@@ -964,4 +966,12 @@ public class RealmRepresentation {
     public void setFederatedUsers(List<UserRepresentation> federatedUsers) {
         this.federatedUsers = federatedUsers;
     }
+
+    public void setUserManagedAccessAllowed(Boolean userManagedAccessAllowed) {
+        this.userManagedAccessAllowed = userManagedAccessAllowed;
+    }
+
+    public Boolean isUserManagedAccessAllowed() {
+        return userManagedAccessAllowed;
+    }
 }
diff --git a/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java b/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java
index ea37d60..4c4573b 100644
--- a/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java
+++ b/examples/authz/hello-world/src/main/java/org/keycloak/authz/helloworld/AuthorizationClientExample.java
@@ -18,18 +18,14 @@
 package org.keycloak.authz.helloworld;
 
 import org.keycloak.authorization.client.AuthzClient;
-import org.keycloak.authorization.client.representation.EntitlementRequest;
-import org.keycloak.authorization.client.representation.EntitlementResponse;
-import org.keycloak.authorization.client.representation.PermissionRequest;
-import org.keycloak.authorization.client.representation.RegistrationResponse;
 import org.keycloak.authorization.client.representation.ResourceRepresentation;
 import org.keycloak.authorization.client.representation.ScopeRepresentation;
 import org.keycloak.authorization.client.representation.TokenIntrospectionResponse;
 import org.keycloak.authorization.client.resource.ProtectedResource;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.Permission;
 
-import java.util.Set;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -47,28 +43,10 @@ public class AuthorizationClientExample {
         // create a new instance based on the configuration defined in keycloak-authz.json
         AuthzClient authzClient = AuthzClient.create();
 
-        // query the server for a resource with a given name
-        Set<String> resourceId = authzClient.protection()
-                .resource()
-                .findByFilter("name=Default Resource");
-
-        // obtain an Entitlement API Token in order to get access to the Entitlement API.
-        // this token is just an access token issued to a client on behalf of an user
-        // with a scope = kc_entitlement
-        String eat = getEntitlementAPIToken(authzClient);
-
-        // create an entitlement request
-        EntitlementRequest request = new EntitlementRequest();
-        PermissionRequest permission = new PermissionRequest();
-
-        permission.setResourceSetId(resourceId.iterator().next());
-
-        request.addPermission(permission);
-
-        // send the entitlement request to the server in order to
+        // send the authorization request to the server in order to
         // obtain an RPT with all permissions granted to the user
-        EntitlementResponse response = authzClient.entitlement(eat).get("hello-world-authz-service", request);
-        String rpt = response.getRpt();
+        AuthorizationResponse response = authzClient.authorization("alice", "alice").authorize();
+        String rpt = response.getToken();
 
         TokenIntrospectionResponse requestingPartyToken = authzClient.protection().introspectRequestingPartyToken(rpt);
 
@@ -78,7 +56,6 @@ public class AuthorizationClientExample {
         for (Permission granted : requestingPartyToken.getPermissions()) {
             System.out.println(granted);
         }
-
     }
 
     private static void createResource() {
@@ -94,18 +71,18 @@ public class AuthorizationClientExample {
         newResource.addScope(new ScopeRepresentation("urn:hello-world-authz:scopes:view"));
 
         ProtectedResource resourceClient = authzClient.protection().resource();
-        Set<String> existingResource = resourceClient.findByFilter("name=" + newResource.getName());
+        ResourceRepresentation existingResource = resourceClient.findByName(newResource.getName());
 
-        if (!existingResource.isEmpty()) {
-            resourceClient.delete(existingResource.iterator().next());
+        if (existingResource != null) {
+            resourceClient.delete(existingResource.getId());
         }
 
         // create the resource on the server
-        RegistrationResponse response = resourceClient.create(newResource);
+        ResourceRepresentation response = resourceClient.create(newResource);
         String resourceId = response.getId();
 
         // query the resource using its newly generated id
-        ResourceRepresentation resource = resourceClient.findById(resourceId).getResourceDescription();
+        ResourceRepresentation resource = resourceClient.findById(resourceId);
 
         System.out.println(resource);
     }
@@ -120,20 +97,20 @@ public class AuthorizationClientExample {
         resource.setName("New Resource");
 
         ProtectedResource resourceClient = authzClient.protection().resource();
-        Set<String> existingResource = resourceClient.findByFilter("name=" + resource.getName());
+        ResourceRepresentation existingResource = resourceClient.findByName(resource.getName());
 
-        if (existingResource.isEmpty()) {
+        if (existingResource == null) {
             createResource();
         }
 
-        resource.setId(existingResource.iterator().next());
+        resource.setId(existingResource.getId());
         resource.setUri("Changed URI");
 
         // update the resource on the server
         resourceClient.update(resource);
 
         // query the resource using its newly generated id
-        ResourceRepresentation existing = resourceClient.findById(resource.getId()).getResourceDescription();
+        ResourceRepresentation existing = resourceClient.findById(resource.getId());
 
         System.out.println(existing);
     }
@@ -142,23 +119,16 @@ public class AuthorizationClientExample {
         // create a new instance based on the configuration define at keycloak-authz.json
         AuthzClient authzClient = AuthzClient.create();
 
-        // obtain an Entitlement API Token in order to get access to the Entitlement API.
-        // this token is just an access token issued to a client on behalf of an user
-        // with a scope = kc_entitlement
-        String eat = getEntitlementAPIToken(authzClient);
+        // create an authorization request
+        AuthorizationRequest request = new AuthorizationRequest();
 
-        // create an entitlement request
-        EntitlementRequest request = new EntitlementRequest();
-        PermissionRequest permission = new PermissionRequest();
+        // add permissions to the request based on the resources and scopes you want to check access
+        request.addPermission("Default Resource");
 
-        permission.setResourceSetName("Default Resource");
-
-        request.addPermission(permission);
-
-        // send the entitlement request to the server in order to obtain a RPT
-        // with all permissions granted to the user
-        EntitlementResponse response = authzClient.entitlement(eat).get("hello-world-authz-service", request);
-        String rpt = response.getRpt();
+        // send the entitlement request to the server in order to
+        // obtain an RPT with permissions for a single resource
+        AuthorizationResponse response = authzClient.authorization("alice", "alice").authorize(request);
+        String rpt = response.getToken();
 
         System.out.println("You got a RPT: " + rpt);
 
@@ -169,27 +139,16 @@ public class AuthorizationClientExample {
         // create a new instance based on the configuration defined in keycloak-authz.json
         AuthzClient authzClient = AuthzClient.create();
 
-        // obtian a Entitlement API Token in order to get access to the Entitlement API.
-        // this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement
-        String eat = getEntitlementAPIToken(authzClient);
+        // create an authorization request
+        AuthorizationRequest request = new AuthorizationRequest();
 
-        // send the entitlement request to the server in order to obtain a RPT with all permissions granted to the user
-        EntitlementResponse response = authzClient.entitlement(eat).getAll("hello-world-authz-service");
-        String rpt = response.getRpt();
+        // send the entitlement request to the server in order to
+        // obtain an RPT with all permissions granted to the user
+        AuthorizationResponse response = authzClient.authorization("alice", "alice").authorize(request);
+        String rpt = response.getToken();
 
         System.out.println("You got a RPT: " + rpt);
 
         // now you can use the RPT to access protected resources on the resource server
     }
-
-    /**
-     * Obtain an Entitlement API Token or EAT from the server. Usually, EATs are going to be obtained by clients using a
-     * authorization_code grant type. Here we are using resource owner credentials for demonstration purposes.
-     *
-     * @param authzClient the authorization client instance
-     * @return a string representing a EAT
-     */
-    private static String getEntitlementAPIToken(AuthzClient authzClient) {
-        return authzClient.obtainAccessToken("alice", "alice").getToken();
-    }
 }
diff --git a/examples/authz/hello-world/src/main/resources/keycloak.json b/examples/authz/hello-world/src/main/resources/keycloak.json
index b337389..4f9b0e5 100644
--- a/examples/authz/hello-world/src/main/resources/keycloak.json
+++ b/examples/authz/hello-world/src/main/resources/keycloak.json
@@ -1,6 +1,6 @@
 {
   "realm": "hello-world-authz",
-  "auth-server-url" : "http://localhost:8080/auth",
+  "auth-server-url" : "http://localhost:8180/auth",
   "resource" : "hello-world-authz-service",
   "credentials": {
     "secret": "secret"
diff --git a/examples/authz/hello-world-authz-service/src/main/webapp/index.jsp b/examples/authz/hello-world-authz-service/src/main/webapp/index.jsp
index 0aea6b0..c511b2d 100644
--- a/examples/authz/hello-world-authz-service/src/main/webapp/index.jsp
+++ b/examples/authz/hello-world-authz-service/src/main/webapp/index.jsp
@@ -38,8 +38,8 @@
         for (Permission permission : authzContext.getPermissions()) {
     %>
     <li>
-        <p>Resource: <%= permission.getResourceSetName() %></p>
-        <p>ID: <%= permission.getResourceSetId() %></p>
+        <p>Resource: <%= permission.getResourceName() %></p>
+        <p>ID: <%= permission.getResourceId() %></p>
     </li>
     <%
         }
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/index.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/index.html
index 203b6e2..158d89f 100755
--- a/examples/authz/photoz/photoz-html5-client/src/main/webapp/index.html
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/index.html
@@ -11,15 +11,15 @@
     <script src="lib/angular/angular-route.min.js"></script>
     <script src="lib/jwt-decode.min.js"></script>
 
-    <script src="http://localhost:8080/auth/js/keycloak.js"></script>
-    <script src="http://localhost:8080/auth/js/keycloak-authz.js"></script>
+    <script src="http://localhost:8180/auth/js/keycloak.js"></script>
+    <script src="http://localhost:8180/auth/js/keycloak-authz.js"></script>
     <script src="js/identity.js" type="text/javascript"></script>
     <script src="js/app.js" type="text/javascript"></script>
 </head>
 
 <body data-ng-controller="TokenCtrl">
 
-<a href data-ng-click="showRpt()">Show Requesting Party Token </a> | <a href data-ng-click="showAccessToken()">Show Access Token </a> | <a href data-ng-click="requestEntitlements()">Request Entitlements</a> | <a href="" ng-click="Identity.logout()">Sign Out</a>
+<a href data-ng-click="showRpt()">Show Requesting Party Token </a> | <a href data-ng-click="showAccessToken()">Show Access Token </a> | <a href data-ng-click="requestEntitlements()">Request Entitlements</a> | <a href="" ng-click="Identity.account()">My Account</a> | <a href="" ng-click="Identity.logout()">Sign Out</a>
 
 <div id="content-area" class="col-md-9" role="main">
     <div id="content" ng-view/>
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/app.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/app.js
index e58c5f5..b552391 100755
--- a/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/app.js
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/app.js
@@ -42,6 +42,9 @@ module.controller('GlobalCtrl', function ($scope, $http, $route, $location, Albu
     Album.query(function (albums) {
         $scope.albums = albums;
     });
+    Album.shares(function (albums) {
+        $scope.shares = albums;
+    });
 
     $scope.Identity = Identity;
 
@@ -50,6 +53,23 @@ module.controller('GlobalCtrl', function ($scope, $http, $route, $location, Albu
             $route.reload();
         });
     }
+
+    $scope.requestDeleteAccess = function (album) {
+        new Album(album).$delete({id: album.id}, function () {
+            // no-op
+        }, function () {
+            document.getElementById("output").innerHTML = 'Sent authorization request to resource owner, please, wait for approval.';
+        });
+    }
+
+    $scope.hasAccess = function (share, scope) {
+        for (i = 0; i < share.scopes.length; i++) {
+            if (share.scopes[i] == scope) {
+                return true;
+            }
+        }
+        return false;
+    }
 });
 
 module.controller('TokenCtrl', function ($scope, Identity) {
@@ -98,7 +118,9 @@ module.controller('AdminAlbumCtrl', function ($scope, $http, $route, $location, 
 });
 
 module.factory('Album', ['$resource', function ($resource) {
-    return $resource(apiUrl + '/album/:id');
+    return $resource(apiUrl + '/album/:id', {id: '@id'}, {
+            shares: {url: apiUrl + '/album/shares', method: 'GET', isArray: true}
+        });
 }]);
 
 module.factory('Profile', ['$resource', function ($resource) {
@@ -133,11 +155,46 @@ module.factory('authInterceptor', function ($q, $injector, $timeout, Identity) {
                 }
 
                 if (rejection.config.url.indexOf('/authorize') == -1 && retry) {
-                    var deferred = $q.defer();
-
                     // here is the authorization logic, which tries to obtain an authorization token from the server in case the resource server
                     // returns a 403 or 401.
-                    Identity.authorization.authorize(rejection.headers('WWW-Authenticate')).then(function (rpt) {
+                    var wwwAuthenticateHeader = rejection.headers('WWW-Authenticate');
+
+                    // when using UMA, a WWW-Authenticate header should be returned by the resource server
+                    if (!wwwAuthenticateHeader) {
+                        return $q.reject(rejection);
+                    }
+
+                    // when using UMA, a WWW-Authenticate header should contain UMA data
+                    if (wwwAuthenticateHeader.indexOf('UMA') == -1) {
+                        return $q.reject(rejection);
+                    }
+
+                    var deferred = $q.defer();
+
+                    var params = wwwAuthenticateHeader.split(',');
+                    var ticket;
+
+                    // try to extract the permission ticket from the WWW-Authenticate header
+                    for (i = 0; i < params.length; i++) {
+                        var param = params[i].split('=');
+
+                        if (param[0] == 'ticket') {
+                            ticket = param[1].substring(1, param[1].length - 1).trim();
+                            break;
+                        }
+                    }
+
+                    // a permission ticket must exist in order to send an authorization request
+                    if (!ticket) {
+                        return $q.reject(rejection);
+                    }
+
+                    // prepare a authorization request with the permission ticket
+                    var authorizationRequest = {};
+                    authorizationRequest.ticket = ticket;
+
+                    // send the authorization request, if successful retry the request
+                    Identity.authorization.authorize(authorizationRequest).then(function (rpt) {
                         deferred.resolve(rejection);
                     }, function () {
                         document.getElementById("output").innerHTML = 'You can not access or perform the requested operation on this resource.';
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/identity.js b/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/identity.js
index 9a018e4..4088f80 100644
--- a/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/identity.js
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/js/identity.js
@@ -34,6 +34,10 @@
             keycloak.logout();
         };
 
+        this.account = function () {
+            keycloak.accountManagement();
+        }
+
         this.hasRole = function (name) {
             if (keycloak && keycloak.hasRealmRole(name)) {
                 return true;
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json b/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json
index affafdd..d9354e3 100644
--- a/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/keycloak.json
@@ -1,6 +1,6 @@
 {
   "realm": "photoz",
-  "auth-server-url" : "http://localhost:8080/auth",
+  "auth-server-url" : "http://localhost:8180/auth",
   "ssl-required" : "external",
   "resource" : "photoz-html5-client",
   "public-client" : true
diff --git a/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/home.html b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/home.html
index 78c252a..fffcdea 100644
--- a/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/home.html
+++ b/examples/authz/photoz/photoz-html5-client/src/main/webapp/partials/home.html
@@ -5,18 +5,18 @@
 <div data-ng-show="!Identity.isAdmin()">
 <a href="#/album/create" id="create-album">Create Album</a> | <a href="#/profile">My Profile</a>
 <br/>
-<br/>
+<h3>Your Albums</h3>
 <span data-ng-show="albums.length == 0" id="resource-list-empty">You don't have any albums, yet.</span>
 <table class="table" data-ng-show="albums.length > 0">
-    <thead>
-        <tr>
-            <th>Your Albums</th>
-        </tr>
-    </thead>
-    <tbody>
-        <tr data-ng-repeat="p in albums">
-            <td><a id="view-{{p.name}}" href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" id="delete-{{p.name}}" ng-click="deleteAlbum(p)">X</a>]</td>
-        </tr>
-    </tbody>
+    <tr data-ng-repeat="p in albums">
+        <td><a id="view-{{p.name}}" href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" id="delete-{{p.name}}" ng-click="deleteAlbum(p)">X</a>]</td>
+    </tr>
+</table>
+<h3>Shared With Me</h3>
+<span data-ng-show="shares.length == 0" id="share-list-empty">You don't have any shares, yet.</span>
+<table class="table" data-ng-show="shares.length > 0">
+    <tr data-ng-repeat="p in shares">
+        <td><a id="view-share-{{p.album.name}}" href="#/album/{{p.album.id}}">{{p.album.name}}</a> - <a href="#" id="delete-share-{{p.album.name}}" data-ng-show="hasAccess(p, 'album:delete')" ng-click="deleteAlbum(p.album)">[X]</a><a href="#" id="request-delete-share-{{p.album.name}}" data-ng-hide="hasAccess(p, 'album:delete')" ng-click="requestDeleteAccess(p.album)">Request Delete Access</a></td>
+    </tr>
 </table>
 </div>
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-realm.json b/examples/authz/photoz/photoz-realm.json
index 118b982..4a15c38 100644
--- a/examples/authz/photoz/photoz-realm.json
+++ b/examples/authz/photoz/photoz-realm.json
@@ -1,6 +1,7 @@
 {
   "realm": "photoz",
   "enabled": true,
+  "userManagedAccessAllowed": "true",
   "sslRequired": "external",
   "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
   "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
@@ -26,6 +27,9 @@
       "clientRoles": {
         "photoz-restful-api": [
           "manage-albums"
+        ],
+        "account": [
+          "manage-account"
         ]
       }
     },
@@ -47,6 +51,9 @@
       "clientRoles": {
         "photoz-restful-api": [
           "manage-albums"
+        ],
+        "account": [
+          "manage-account"
         ]
       }
     },
@@ -100,13 +107,13 @@
     {
       "clientId": "photoz-html5-client",
       "enabled": true,
-      "adminUrl": "/photoz-html5-client",
-      "baseUrl": "/photoz-html5-client",
+      "adminUrl": "http://localhost:8080/photoz-html5-client",
+      "baseUrl": "http://localhost:8080/photoz-html5-client",
       "publicClient": true,
       "consentRequired" : true,
       "fullScopeAllowed" : true,
       "redirectUris": [
-        "/photoz-html5-client/*"
+        "http://localhost:8080/photoz-html5-client/*"
       ],
       "webOrigins": ["http://localhost:8080"]
     },
@@ -114,10 +121,10 @@
       "clientId": "photoz-restful-api",
       "secret": "secret",
       "enabled": true,
-      "baseUrl": "/photoz-restful-api",
+      "baseUrl": "http://localhost:8080/photoz-restful-api",
       "authorizationServicesEnabled" : true,
       "redirectUris": [
-        "/photoz-restful-api/*"
+        "http://localhost:8080/photoz-html5-client"
       ],
       "webOrigins" : ["http://localhost:8080"]
     }
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
index 056ff05..b49ba90 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
@@ -1,14 +1,11 @@
 package org.keycloak.example.photoz.album;
 
-import org.keycloak.KeycloakSecurityContext;
-import org.keycloak.authorization.client.AuthzClient;
-import org.keycloak.authorization.client.ClientAuthorizationContext;
-import org.keycloak.authorization.client.representation.ResourceRepresentation;
-import org.keycloak.authorization.client.representation.ScopeRepresentation;
-import org.keycloak.authorization.client.resource.ProtectionResource;
-import org.keycloak.example.photoz.ErrorResponse;
-import org.keycloak.example.photoz.entity.Album;
-import org.keycloak.example.photoz.util.Transaction;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 
 import javax.inject.Inject;
 import javax.persistence.EntityManager;
@@ -24,18 +21,24 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
-import java.security.Principal;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
+
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.ClientAuthorizationContext;
+import org.keycloak.authorization.client.representation.ResourceRepresentation;
+import org.keycloak.authorization.client.representation.ScopeRepresentation;
+import org.keycloak.authorization.client.resource.ProtectionResource;
+import org.keycloak.example.photoz.ErrorResponse;
+import org.keycloak.example.photoz.entity.Album;
+import org.keycloak.example.photoz.util.Transaction;
+import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
 
 @Path("/album")
 @Transaction
 public class AlbumService {
 
-    public static final String SCOPE_ALBUM_VIEW = "urn:photoz.com:scopes:album:view";
-    public static final String SCOPE_ALBUM_DELETE = "urn:photoz.com:scopes:album:delete";
+    public static final String SCOPE_ALBUM_VIEW = "album:view";
+    public static final String SCOPE_ALBUM_DELETE = "album:delete";
 
     @Inject
     private EntityManager entityManager;
@@ -60,9 +63,12 @@ public class AlbumService {
             throw new ErrorResponse("Name [" + newAlbum.getName() + "] already taken. Choose another one.", Status.CONFLICT);
         }
 
-        this.entityManager.persist(newAlbum);
-
-        createProtectedResource(newAlbum);
+        try {
+            this.entityManager.persist(newAlbum);
+            createProtectedResource(newAlbum);
+        } catch (Exception e) {
+            getAuthzClient().protection().resource().delete(newAlbum.getExternalId());
+        }
 
         return Response.ok(newAlbum).build();
     }
@@ -89,6 +95,29 @@ public class AlbumService {
     }
 
     @GET
+    @Path("/shares")
+    @Produces("application/json")
+    public Response findShares() {
+        List<PermissionTicketRepresentation> permissions = getAuthzClient().protection().permission().find(null, null, null, getKeycloakSecurityContext().getToken().getSubject(), true, true, null, null);
+        Map<String, SharedAlbum> shares = new HashMap<>();
+
+        for (PermissionTicketRepresentation permission : permissions) {
+            SharedAlbum share = shares.get(permission.getResource());
+
+            if (share == null) {
+                share = new SharedAlbum(Album.class.cast(entityManager.createQuery("from Album where externalId = :externalId").setParameter("externalId", permission.getResource()).getSingleResult()));
+                shares.put(permission.getResource(), share);
+            }
+
+            if (permission.getScope() != null) {
+                share.addScope(permission.getScopeName());
+            }
+        }
+
+        return Response.ok(shares.values()).build();
+    }
+
+    @GET
     @Path("{id}")
     @Produces("application/json")
     public Response findById(@PathParam("id") String id) {
@@ -111,8 +140,11 @@ public class AlbumService {
             ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), scopes, "/album/" + album.getId(), "http://photoz.com/album");
 
             albumResource.setOwner(album.getUserId());
+            albumResource.setOwnerManagedAccess(true);
+
+            ResourceRepresentation response = getAuthzClient().protection().resource().create(albumResource);
 
-            getAuthzClient().protection().resource().create(albumResource);
+            album.setExternalId(response.getId());
         } catch (Exception e) {
             throw new RuntimeException("Could not register protected resource.", e);
         }
@@ -123,13 +155,13 @@ public class AlbumService {
 
         try {
             ProtectionResource protection = getAuthzClient().protection();
-            Set<String> search = protection.resource().findByFilter("uri=" + uri);
+            List<ResourceRepresentation> search = protection.resource().findByUri(uri);
 
             if (search.isEmpty()) {
                 throw new RuntimeException("Could not find protected resource with URI [" + uri + "]");
             }
 
-            protection.resource().delete(search.iterator().next());
+            protection.resource().delete(search.get(0).getId());
         } catch (Exception e) {
             throw new RuntimeException("Could not search protected resource.", e);
         }
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/SharedAlbum.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/SharedAlbum.java
new file mode 100644
index 0000000..dfc5fb1
--- /dev/null
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/SharedAlbum.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 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.example.photoz.album;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.keycloak.example.photoz.entity.Album;
+
+public class SharedAlbum {
+
+    private Album album;
+    private List<String> scopes;
+
+    public SharedAlbum(Album album) {
+        this.album = album;
+    }
+
+    public Album getAlbum() {
+        return album;
+    }
+
+    public List<String> getScopes() {
+        return scopes;
+    }
+
+    public void addScope(String scope) {
+        if (scopes == null) {
+            scopes = new ArrayList<>();
+        }
+        scopes.add(scope);
+    }
+}
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java
index 990595e..d8dda5f 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java
@@ -17,6 +17,9 @@
  */
 package org.keycloak.example.photoz.entity;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
@@ -43,6 +46,9 @@ public class Album {
     @Column(nullable = false)
     private String userId;
 
+    @Column
+    private String externalId;
+
     public String getId() {
         return this.id;
     }
@@ -74,4 +80,12 @@ public class Album {
     public String getUserId() {
         return this.userId;
     }
+
+    public void setExternalId(String externalId) {
+        this.externalId = externalId;
+    }
+
+    public String getExternalId() {
+        return externalId;
+    }
 }
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/resources/photoz-restful-api-authz-service.json b/examples/authz/photoz/photoz-restful-api/src/main/resources/photoz-restful-api-authz-service.json
index 28b87bc..d94ce40 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/resources/photoz-restful-api-authz-service.json
+++ b/examples/authz/photoz/photoz-restful-api/src/main/resources/photoz-restful-api-authz-service.json
@@ -3,12 +3,22 @@
   "policyEnforcementMode": "ENFORCING",
   "resources": [
     {
+      "name": "Admin Resources",
+      "uri": "/admin/*",
+      "type": "http://photoz.com/admin",
+      "scopes": [
+        {
+          "name": "admin:manage"
+        }
+      ]
+    },
+    {
       "name": "User Profile Resource",
       "uri": "/profile",
       "type": "http://photoz.com/profile",
       "scopes": [
         {
-          "name": "urn:photoz.com:scopes:profile:view"
+          "name": "profile:view"
         }
       ]
     },
@@ -18,29 +28,46 @@
       "type": "http://photoz.com/album",
       "scopes": [
         {
-          "name": "urn:photoz.com:scopes:album:view"
-        },
-        {
-          "name": "urn:photoz.com:scopes:album:delete"
+          "name": "album:delete"
         },
         {
-          "name": "urn:photoz.com:scopes:album:create"
-        }
-      ]
-    },
-    {
-      "name": "Admin Resources",
-      "uri": "/admin/*",
-      "type": "http://photoz.com/admin",
-      "scopes": [
-        {
-          "name": "urn:photoz.com:scopes:album:admin:manage"
+          "name": "album:view"
         }
       ]
     }
   ],
   "policies": [
     {
+      "name": "Only Owner and Administrators Policy",
+      "description": "Defines that only the resource owner and administrators can do something",
+      "type": "aggregate",
+      "logic": "POSITIVE",
+      "decisionStrategy": "AFFIRMATIVE",
+      "config": {
+        "applyPolicies": "[\"Administration Policy\",\"Only Owner Policy\"]"
+      }
+    },
+    {
+      "name": "Administration Policy",
+      "description": "Defines that only administrators from a specific network address can do something.",
+      "type": "aggregate",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "applyPolicies": "[\"Any Admin Policy\",\"Only From a Specific Client Address\"]"
+      }
+    },
+    {
+      "name": "Only From @keycloak.org or Admin",
+      "description": "Defines that only users from @keycloak.org",
+      "type": "js",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "code": "var context = $evaluation.getContext();\nvar identity = context.getIdentity();\nvar attributes = identity.getAttributes();\nvar email = attributes.getValue('email').asString(0);\n\nif (identity.hasRealmRole('admin') || email.endsWith('@keycloak.org')) {\n    $evaluation.grant();\n}"
+      }
+    },
+    {
       "name": "Only Owner Policy",
       "description": "Defines that only the resource owner is allowed to do something",
       "type": "rules",
@@ -67,16 +94,6 @@
       }
     },
     {
-      "name": "Any User Policy",
-      "description": "Defines that only users from well known clients are allowed to access",
-      "type": "role",
-      "logic": "POSITIVE",
-      "decisionStrategy": "UNANIMOUS",
-      "config": {
-        "roles": "[{\"id\":\"user\"},{\"id\":\"manage-albums\",\"required\":true}]"
-      }
-    },
-    {
       "name": "Only From a Specific Client Address",
       "description": "Defines that only clients from a specific address can do something",
       "type": "js",
@@ -87,45 +104,13 @@
       }
     },
     {
-      "name": "Administration Policy",
-      "description": "Defines that only administrators from a specific network address can do something.",
-      "type": "aggregate",
-      "logic": "POSITIVE",
-      "decisionStrategy": "UNANIMOUS",
-      "config": {
-        "applyPolicies": "[\"Only From a Specific Client Address\",\"Any Admin Policy\"]"
-      }
-    },
-    {
-      "name": "Only Owner and Administrators Policy",
-      "description": "Defines that only the resource owner and administrators can do something",
-      "type": "aggregate",
-      "logic": "POSITIVE",
-      "decisionStrategy": "AFFIRMATIVE",
-      "config": {
-        "applyPolicies": "[\"Administration Policy\",\"Only Owner Policy\"]"
-      }
-    },
-    {
-      "name": "Only From @keycloak.org or Admin",
-      "description": "Defines that only users from @keycloak.org",
-      "type": "js",
+      "name": "Any User Policy",
+      "description": "Defines that only users from well known clients are allowed to access",
+      "type": "role",
       "logic": "POSITIVE",
       "decisionStrategy": "UNANIMOUS",
       "config": {
-        "code": "var context = $evaluation.getContext();\nvar identity = context.getIdentity();\nvar attributes = identity.getAttributes();\nvar email = attributes.getValue('email').asString(0);\n\nif (identity.hasRealmRole('admin') || email.endsWith('@keycloak.org')) {\n    $evaluation.grant();\n}"
-      }
-    },
-    {
-      "name": "Album Resource Permission",
-      "description": "General policies that apply to all album resources.",
-      "type": "resource",
-      "logic": "POSITIVE",
-      "decisionStrategy": "AFFIRMATIVE",
-      "config": {
-        "defaultResourceType": "http://photoz.com/album",
-        "default": "true",
-        "applyPolicies": "[\"Any User Policy\",\"Administration Policy\"]"
+        "roles": "[{\"id\":\"user\",\"required\":false},{\"id\":\"photoz-restful-api/manage-albums\",\"required\":true}]"
       }
     },
     {
@@ -136,48 +121,32 @@
       "decisionStrategy": "UNANIMOUS",
       "config": {
         "defaultResourceType": "http://photoz.com/admin",
-        "default": "true",
-        "applyPolicies": "[\"Administration Policy\"]"
+        "applyPolicies": "[\"Administration Policy\"]",
+        "default": "true"
       }
     },
     {
-      "name": "View User Permission",
-      "description": "Defines who is allowed to view an user profile",
+      "name": "Album Resource Permission",
+      "description": "A default permission that defines access for any album resource",
       "type": "scope",
       "logic": "POSITIVE",
       "decisionStrategy": "UNANIMOUS",
       "config": {
-        "applyPolicies": "[\"Only From @keycloak.org or Admin\"]",
-        "scopes": "[\"urn:photoz.com:scopes:profile:view\"]"
+        "resources": "[\"Album Resource\"]",
+        "scopes": "[\"album:view\",\"album:delete\"]",
+        "applyPolicies": "[\"Only Owner and Administrators Policy\"]"
       }
     },
     {
-      "name": "Delete Album Permission",
-      "description": "A policy that only allows the owner to delete his albums.",
+      "name": "View User Permission",
+      "description": "Defines who is allowed to view an user profile",
       "type": "scope",
       "logic": "POSITIVE",
       "decisionStrategy": "UNANIMOUS",
       "config": {
-        "applyPolicies": "[\"Only Owner and Administrators Policy\"]",
-        "scopes": "[\"urn:photoz.com:scopes:album:delete\"]"
+        "scopes": "[\"profile:view\"]",
+        "applyPolicies": "[\"Only From @keycloak.org or Admin\"]"
       }
     }
-  ],
-  "scopes": [
-    {
-      "name": "urn:photoz.com:scopes:profile:view"
-    },
-    {
-      "name": "urn:photoz.com:scopes:album:view"
-    },
-    {
-      "name": "urn:photoz.com:scopes:album:create"
-    },
-    {
-      "name": "urn:photoz.com:scopes:album:delete"
-    },
-    {
-      "name": "urn:photoz.com:scopes:album:admin:manage"
-    }
   ]
 }
\ No newline at end of file
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
index 9e06730..7748450 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/authz/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
@@ -1,6 +1,6 @@
 {
   "realm": "photoz",
-  "auth-server-url": "http://localhost:8080/auth",
+  "auth-server-url": "http://localhost:8180/auth",
   "ssl-required": "external",
   "resource": "photoz-restful-api",
   "bearer-only" : true,
@@ -8,36 +8,29 @@
     "secret": "secret"
   },
   "policy-enforcer": {
-    "user-managed-access" : {},
+    "enforcement-mode": "PERMISSIVE",
+    "user-managed-access": {},
     "paths": [
       {
-        "path" : "/album/*",
-        "methods" : [
-          {
-            "method": "POST",
-            "scopes" : ["urn:photoz.com:scopes:album:create"]
-          },
-          {
-            "method": "GET",
-            "scopes" : ["urn:photoz.com:scopes:album:view"]
-          }
-        ]
-      },
-      {
         "name" : "Album Resource",
         "path" : "/album/{id}",
         "methods" : [
           {
             "method": "DELETE",
-            "scopes" : ["urn:photoz.com:scopes:album:delete"]
+            "scopes" : ["album:delete"]
           },
           {
             "method": "GET",
-            "scopes" : ["urn:photoz.com:scopes:album:view"]
+            "scopes" : ["album:view"]
           }
         ]
       },
       {
+        "name" : "Album Resource",
+        "path" : "/album/shares",
+        "enforcement-mode": "DISABLED"
+      },
+      {
         "path" : "/profile"
       },
       {
diff --git a/examples/authz/servlet-authz/src/main/webapp/index.jsp b/examples/authz/servlet-authz/src/main/webapp/index.jsp
index 3fbfca2..345a69d 100755
--- a/examples/authz/servlet-authz/src/main/webapp/index.jsp
+++ b/examples/authz/servlet-authz/src/main/webapp/index.jsp
@@ -23,8 +23,8 @@
             for (Permission permission : authzContext.getPermissions()) {
         %>
         <li>
-            <p>Resource: <%= permission.getResourceSetName() %></p>
-            <p>ID: <%= permission.getResourceSetId() %></p>
+            <p>Resource: <%= permission.getResourceName() %></p>
+            <p>ID: <%= permission.getResourceId() %></p>
             <p>Scopes: <%= permission.getScopes() %></p>
         </li>
         <%
diff --git a/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp b/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp
index 364d887..21ef2ed 100644
--- a/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp
+++ b/examples/authz/servlet-authz/src/main/webapp/logout-include.jsp
@@ -7,5 +7,5 @@
     String contextPath = request.getContextPath();
     String redirectUri = scheme + "://" + host + ":" + port + contextPath;
 %>
-<h2>Click here <a href="<%= KeycloakUriBuilder.fromUri("http://localhost:8080/auth").path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
+<h2>Click here <a href="<%= KeycloakUriBuilder.fromUri("http://localhost:8180/auth").path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH)
             .queryParam("redirect_uri", redirectUri).build("servlet-authz").toString()%>">Sign Out</a></h2>
\ No newline at end of file
diff --git a/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json b/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json
index 7983fa3..d2834c3 100644
--- a/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json
+++ b/examples/authz/servlet-authz/src/main/webapp/WEB-INF/keycloak.json
@@ -1,6 +1,6 @@
 {
   "realm": "servlet-authz",
-  "auth-server-url": "http://localhost:8080/auth",
+  "auth-server-url": "http://localhost:8180/auth",
   "ssl-required": "external",
   "resource": "servlet-authz-app",
   "credentials": {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedPermissionTicket.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedPermissionTicket.java
new file mode 100644
index 0000000..a906a7d
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedPermissionTicket.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2017 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.models.cache.infinispan.authorization.entities;
+
+import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class CachedPermissionTicket extends AbstractRevisioned implements InResourceServer {
+
+    private final String requester;
+    private String owner;
+    private String resourceServerId;
+    private String resourceId;
+    private String scopeId;
+    private boolean granted;
+    private Long createdTimestamp;
+    private Long grantedTimestamp;
+
+    public CachedPermissionTicket(Long revision, PermissionTicket permissionTicket) {
+        super(revision, permissionTicket.getId());
+        this.owner = permissionTicket.getOwner();
+        requester = permissionTicket.getRequester();
+        this.resourceServerId = permissionTicket.getResourceServer().getId();
+        this.resourceId = permissionTicket.getResource().getId();
+        if (permissionTicket.getScope() != null) {
+            this.scopeId = permissionTicket.getScope().getId();
+        }
+        this.granted = permissionTicket.isGranted();
+        createdTimestamp = permissionTicket.getCreatedTimestamp();
+        grantedTimestamp = permissionTicket.getGrantedTimestamp();
+    }
+
+    public String getOwner() {
+        return owner;
+    }
+
+    public String getRequester() {
+        return requester;
+    }
+
+    public String getResourceId() {
+        return resourceId;
+    }
+
+    public String getScopeId() {
+        return scopeId;
+    }
+
+    public boolean isGranted() {
+        return granted;
+    }
+
+    public long getCreatedTimestamp() {
+        return createdTimestamp;
+    }
+
+    public Long getGrantedTimestamp() {
+        return grantedTimestamp;
+    }
+
+    public String getResourceServerId() {
+        return this.resourceServerId;
+    }
+
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedResource.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedResource.java
index f153883..383ab1c 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedResource.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedResource.java
@@ -35,18 +35,22 @@ public class CachedResource extends AbstractRevisioned implements InResourceServ
     private String owner;
     private String type;
     private String name;
+    private String displayName;
     private String uri;
     private Set<String> scopesIds;
+    private boolean ownerManagedAccess;
 
     public CachedResource(Long revision, Resource resource) {
         super(revision, resource.getId());
         this.name = resource.getName();
+        this.displayName = resource.getDisplayName();
         this.uri = resource.getUri();
         this.type = resource.getType();
         this.owner = resource.getOwner();
         this.iconUri = resource.getIconUri();
         this.resourceServerId = resource.getResourceServer().getId();
         this.scopesIds = resource.getScopes().stream().map(Scope::getId).collect(Collectors.toSet());
+        ownerManagedAccess = resource.isOwnerManagedAccess();
     }
 
 
@@ -54,6 +58,10 @@ public class CachedResource extends AbstractRevisioned implements InResourceServ
         return this.name;
     }
 
+    public String getDisplayName() {
+        return this.displayName;
+    }
+
     public String getUri() {
         return this.uri;
     }
@@ -70,6 +78,10 @@ public class CachedResource extends AbstractRevisioned implements InResourceServ
         return this.owner;
     }
 
+    public boolean isOwnerManagedAccess() {
+        return ownerManagedAccess;
+    }
+
     public String getResourceServerId() {
         return this.resourceServerId;
     }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedScope.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedScope.java
index 7bc31ed..e879134 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedScope.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/CachedScope.java
@@ -28,11 +28,13 @@ public class CachedScope extends AbstractRevisioned implements InResourceServer 
 
     private String resourceServerId;
     private String name;
+    private String displayName;
     private String iconUri;
 
     public CachedScope(Long revision, Scope scope) {
         super(revision, scope.getId());
         this.name = scope.getName();
+        this.displayName = scope.getDisplayName();
         this.iconUri = scope.getIconUri();
         this.resourceServerId = scope.getResourceServer().getId();
     }
@@ -41,6 +43,10 @@ public class CachedScope extends AbstractRevisioned implements InResourceServer 
         return this.name;
     }
 
+    public String getDisplayName() {
+        return displayName;
+    }
+
     public String getIconUri() {
         return this.iconUri;
     }
@@ -49,5 +55,4 @@ public class CachedScope extends AbstractRevisioned implements InResourceServer 
     public String getResourceServerId() {
         return this.resourceServerId;
     }
-
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PermissionTicketListQuery.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PermissionTicketListQuery.java
new file mode 100755
index 0000000..d8c397c
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/entities/PermissionTicketListQuery.java
@@ -0,0 +1,42 @@
+package org.keycloak.models.cache.infinispan.authorization.entities;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class PermissionTicketListQuery extends AbstractRevisioned implements PermissionTicketQuery {
+
+    private final Set<String> permissions;
+    private final String serverId;
+
+    public PermissionTicketListQuery(Long revision, String id, String permissionId, String serverId) {
+        super(revision, id);
+        this.serverId = serverId;
+        permissions = new HashSet<>();
+        permissions.add(permissionId);
+    }
+    public PermissionTicketListQuery(Long revision, String id, Set<String> permissions, String serverId) {
+        super(revision, id);
+        this.serverId = serverId;
+        this.permissions = permissions;
+    }
+
+    @Override
+    public String getResourceServerId() {
+        return serverId;
+    }
+
+    public Set<String> getPermissions() {
+        return permissions;
+    }
+
+    @Override
+    public boolean isInvalid(Set<String> invalidations) {
+        return invalidations.contains(getId()) || invalidations.contains(getResourceServerId());
+    }
+}
\ No newline at end of file
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PermissionTicketRemovedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PermissionTicketRemovedEvent.java
new file mode 100644
index 0000000..bbef979
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PermissionTicketRemovedEvent.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017 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.models.cache.infinispan.authorization.events;
+
+import java.util.Set;
+
+import org.keycloak.models.cache.infinispan.authorization.StoreFactoryCacheManager;
+import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class PermissionTicketRemovedEvent extends InvalidationEvent implements AuthorizationCacheInvalidationEvent {
+
+    private String id;
+    private String owner;
+    private String resource;
+    private String scope;
+    private String serverId;
+
+    public static PermissionTicketRemovedEvent create(String id, String owner, String resource, String scope, String serverId) {
+        PermissionTicketRemovedEvent event = new PermissionTicketRemovedEvent();
+        event.id = id;
+        event.owner = owner;
+        event.resource = resource;
+        event.scope = scope;
+        event.serverId = serverId;
+        return event;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("PermissionTicketRemovedEvent [ id=%s, name=%s]", id, resource);
+    }
+
+    @Override
+    public void addInvalidations(StoreFactoryCacheManager cache, Set<String> invalidations) {
+        cache.permissionTicketRemoval(id, owner, resource, scope, serverId, invalidations);
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PermissionTicketUpdatedEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PermissionTicketUpdatedEvent.java
new file mode 100644
index 0000000..1d830ed
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/events/PermissionTicketUpdatedEvent.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017 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.models.cache.infinispan.authorization.events;
+
+import java.util.Set;
+
+import org.keycloak.models.cache.infinispan.authorization.StoreFactoryCacheManager;
+import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class PermissionTicketUpdatedEvent extends InvalidationEvent implements AuthorizationCacheInvalidationEvent {
+
+    private String id;
+    private String owner;
+    private String resource;
+    private String scope;
+    private String serverId;
+
+    public static PermissionTicketUpdatedEvent create(String id, String owner, String resource, String scope, String serverId) {
+        PermissionTicketUpdatedEvent event = new PermissionTicketUpdatedEvent();
+        event.id = id;
+        event.owner = owner;
+        event.resource = resource;
+        event.scope = scope;
+        event.serverId = serverId;
+        return event;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("PermissionTicketUpdatedEvent [ id=%s, name=%s]", id, resource);
+    }
+
+    @Override
+    public void addInvalidations(StoreFactoryCacheManager cache, Set<String> invalidations) {
+        cache.permissionTicketUpdated(id, owner, resource, scope, serverId, invalidations);
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PermissionTicketAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PermissionTicketAdapter.java
new file mode 100644
index 0000000..d6a7e07
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PermissionTicketAdapter.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2017 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.models.cache.infinispan.authorization;
+
+import org.keycloak.authorization.model.CachedModel;
+import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.models.cache.infinispan.authorization.entities.CachedPermissionTicket;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class PermissionTicketAdapter implements PermissionTicket, CachedModel<PermissionTicket> {
+
+    protected CachedPermissionTicket cached;
+    protected StoreFactoryCacheSession cacheSession;
+    protected PermissionTicket updated;
+
+    public PermissionTicketAdapter(CachedPermissionTicket cached, StoreFactoryCacheSession cacheSession) {
+        this.cached = cached;
+        this.cacheSession = cacheSession;
+    }
+
+    @Override
+    public PermissionTicket getDelegateForUpdate() {
+        if (updated == null) {
+            cacheSession.registerPermissionTicketInvalidation(cached.getId(), cached.getOwner(), cached.getResourceId(), cached.getScopeId(), cached.getResourceServerId());
+            updated = cacheSession.getPermissionTicketStoreDelegate().findById(cached.getId(), cached.getResourceServerId());
+            if (updated == null) throw new IllegalStateException("Not found in database");
+        }
+        return updated;
+    }
+
+    protected boolean invalidated;
+
+    protected void invalidateFlag() {
+        invalidated = true;
+    }
+
+    @Override
+    public void invalidate() {
+        invalidated = true;
+        getDelegateForUpdate();
+    }
+
+    @Override
+    public long getCacheTimestamp() {
+        return cached.getCacheTimestamp();
+    }
+
+    protected boolean isUpdated() {
+        if (updated != null) return true;
+        if (!invalidated) return false;
+        updated = cacheSession.getPermissionTicketStoreDelegate().findById(cached.getId(), cached.getResourceServerId());
+        if (updated == null) throw new IllegalStateException("Not found in database");
+        return true;
+    }
+
+
+    @Override
+    public String getId() {
+        if (isUpdated()) return updated.getId();
+        return cached.getId();
+    }
+
+    @Override
+    public String getOwner() {
+        if (isUpdated()) return updated.getOwner();
+        return cached.getOwner();
+    }
+
+    @Override
+    public String getRequester() {
+        if (isUpdated()) return updated.getRequester();
+        return cached.getRequester();
+    }
+
+    @Override
+    public boolean isGranted() {
+        if (isUpdated()) return updated.isGranted();
+        return cached.isGranted();
+    }
+
+    @Override
+    public Long getCreatedTimestamp() {
+        if (isUpdated()) return updated.getCreatedTimestamp();
+        return cached.getCreatedTimestamp();
+    }
+
+    @Override
+    public Long getGrantedTimestamp() {
+        if (isUpdated()) return updated.getGrantedTimestamp();
+        return cached.getGrantedTimestamp();
+    }
+
+    @Override
+    public void setGrantedTimestamp(Long millis) {
+        getDelegateForUpdate();
+        cacheSession.registerPermissionTicketInvalidation(cached.getId(), cached.getOwner(), cached.getResourceId(), cached.getScopeId(), cached.getResourceServerId());
+        updated.setGrantedTimestamp(millis);
+    }
+
+    @Override
+    public ResourceServer getResourceServer() {
+        return cacheSession.getResourceServerStore().findById(cached.getResourceServerId());
+    }
+
+    @Override
+    public Resource getResource() {
+        return cacheSession.getResourceStore().findById(cached.getResourceId(), getResourceServer().getId());
+    }
+
+    @Override
+    public Scope getScope() {
+        return cacheSession.getScopeStore().findById(cached.getScopeId(), getResourceServer().getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return getId().hashCode();
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PolicyAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PolicyAdapter.java
index 7660c96..ae96113 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PolicyAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/PolicyAdapter.java
@@ -25,6 +25,7 @@ import org.keycloak.models.cache.infinispan.authorization.entities.CachedPolicy;
 import org.keycloak.representations.idm.authorization.DecisionStrategy;
 import org.keycloak.representations.idm.authorization.Logic;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Map;
@@ -206,14 +207,15 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
     @Override
     public void addScope(Scope scope) {
         getDelegateForUpdate();
+        cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), new HashSet<>(Arrays.asList(scope.getId())), cached.getResourceServerId());
         updated.addScope(scope);
     }
 
     @Override
     public void removeScope(Scope scope) {
         getDelegateForUpdate();
+        cacheSession.registerPolicyInvalidation(cached.getId(), cached.getName(), cached.getResourcesIds(), new HashSet<>(Arrays.asList(scope.getId())), cached.getResourceServerId());
         updated.removeScope(scope);
-
     }
 
     @Override
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java
index 38b6860..d310fca 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ResourceAdapter.java
@@ -17,9 +17,11 @@
 package org.keycloak.models.cache.infinispan.authorization;
 
 import org.keycloak.authorization.model.CachedModel;
+import org.keycloak.authorization.model.PermissionTicket;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.PermissionTicketStore;
 import org.keycloak.models.cache.infinispan.authorization.entities.CachedResource;
 
 import java.util.Collections;
@@ -96,7 +98,19 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
         getDelegateForUpdate();
         cacheSession.registerResourceInvalidation(cached.getId(), name, cached.getType(), cached.getUri(), cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
         updated.setName(name);
+    }
 
+    @Override
+    public String getDisplayName() {
+        if (isUpdated()) return updated.getDisplayName();
+        return cached.getDisplayName();
+    }
+
+    @Override
+    public void setDisplayName(String name) {
+        getDelegateForUpdate();
+        cacheSession.registerResourceInvalidation(cached.getId(), name, cached.getType(), cached.getUri(), cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
+        updated.setDisplayName(name);
     }
 
     @Override
@@ -165,8 +179,33 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
     }
 
     @Override
-    public void updateScopes(Set<Scope> scopes) {
+    public boolean isOwnerManagedAccess() {
+        if (isUpdated()) return updated.isOwnerManagedAccess();
+        return cached.isOwnerManagedAccess();
+    }
+
+    @Override
+    public void setOwnerManagedAccess(boolean ownerManagedAccess) {
         getDelegateForUpdate();
+        cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), cached.getUri(), cached.getScopesIds(), cached.getResourceServerId(), cached.getOwner());
+        updated.setOwnerManagedAccess(ownerManagedAccess);
+    }
+
+    @Override
+    public void updateScopes(Set<Scope> scopes) {
+        Resource updated = getDelegateForUpdate();
+
+        for (Scope scope : updated.getScopes()) {
+            if (!scopes.contains(scope)) {
+                PermissionTicketStore permissionStore = cacheSession.getPermissionTicketStoreDelegate();
+                List<PermissionTicket> permissions = permissionStore.findByScope(scope.getId(), getResourceServer().getId());
+
+                for (PermissionTicket permission : permissions) {
+                    permissionStore.delete(permission.getId());
+                }
+            }
+        }
+
         cacheSession.registerResourceInvalidation(cached.getId(), cached.getName(), cached.getType(), cached.getUri(), scopes.stream().map(scope1 -> scope1.getId()).collect(Collectors.toSet()), cached.getResourceServerId(), cached.getOwner());
         updated.updateScopes(scopes);
     }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ScopeAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ScopeAdapter.java
index d90b27a..a4492a7 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ScopeAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/ScopeAdapter.java
@@ -92,6 +92,18 @@ public class ScopeAdapter implements Scope, CachedModel<Scope> {
     }
 
     @Override
+    public String getDisplayName() {
+        if (isUpdated()) return updated.getDisplayName();
+        return cached.getDisplayName();
+    }
+
+    @Override
+    public void setDisplayName(String name) {
+        getDelegateForUpdate();
+        updated.setDisplayName(name);
+    }
+
+    @Override
     public String getIconUri() {
         if (isUpdated()) return updated.getIconUri();
         return cached.getIconUri();
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java
index 3f189a5..7b463be 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheManager.java
@@ -68,6 +68,7 @@ public class StoreFactoryCacheManager extends CacheManager {
         invalidations.add(id);
         invalidations.add(StoreFactoryCacheSession.getScopeByNameCacheKey(name, serverId));
         invalidations.add(StoreFactoryCacheSession.getResourceByScopeCacheKey(id, serverId));
+        invalidations.add(StoreFactoryCacheSession.getPermissionTicketByScope(id, serverId));
     }
 
     public void scopeRemoval(String id, String name, String serverId, Set<String> invalidations) {
@@ -79,6 +80,8 @@ public class StoreFactoryCacheManager extends CacheManager {
         invalidations.add(id);
         invalidations.add(StoreFactoryCacheSession.getResourceByNameCacheKey(name, serverId));
         invalidations.add(StoreFactoryCacheSession.getResourceByOwnerCacheKey(owner, serverId));
+        invalidations.add(StoreFactoryCacheSession.getResourceByOwnerCacheKey(owner, null));
+        invalidations.add(StoreFactoryCacheSession.getPermissionTicketByResource(id, serverId));
 
         if (type != null) {
             invalidations.add(StoreFactoryCacheSession.getResourceByTypeCacheKey(type, serverId));
@@ -125,9 +128,21 @@ public class StoreFactoryCacheManager extends CacheManager {
         }
     }
 
+    public void permissionTicketUpdated(String id, String owner, String resource, String scope, String serverId, Set<String> invalidations) {
+        invalidations.add(id);
+        invalidations.add(StoreFactoryCacheSession.getPermissionTicketByOwner(owner, serverId));
+        invalidations.add(StoreFactoryCacheSession.getPermissionTicketByResource(resource, serverId));
+        if (scope != null) {
+            invalidations.add(StoreFactoryCacheSession.getPermissionTicketByScope(scope, serverId));
+        }
+    }
+
     public void policyRemoval(String id, String name, Set<String> resources, Set<String> resourceTypes, Set<String> scopes, String serverId, Set<String> invalidations) {
         policyUpdated(id, name, resources, resourceTypes, scopes, serverId, invalidations);
     }
 
+    public void permissionTicketRemoval(String id, String owner, String resource, String scope, String serverId, Set<String> invalidations) {
+        permissionTicketUpdated(id, owner, resource, scope, serverId, invalidations);
+    }
 
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
index cb5d060..33c1ddc 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
@@ -30,10 +30,12 @@ import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 import org.jboss.logging.Logger;
+import org.keycloak.authorization.model.PermissionTicket;
 import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.PermissionTicketStore;
 import org.keycloak.authorization.store.PolicyStore;
 import org.keycloak.authorization.store.ResourceServerStore;
 import org.keycloak.authorization.store.ResourceStore;
@@ -43,10 +45,15 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakTransaction;
 import org.keycloak.models.ModelException;
 import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
+import org.keycloak.models.cache.infinispan.authorization.entities.CachedPermissionTicket;
 import org.keycloak.models.cache.infinispan.authorization.entities.CachedPolicy;
 import org.keycloak.models.cache.infinispan.authorization.entities.CachedResource;
 import org.keycloak.models.cache.infinispan.authorization.entities.CachedResourceServer;
 import org.keycloak.models.cache.infinispan.authorization.entities.CachedScope;
+import org.keycloak.models.cache.infinispan.authorization.entities.PermissionTicketListQuery;
+import org.keycloak.models.cache.infinispan.authorization.entities.PermissionTicketQuery;
+import org.keycloak.models.cache.infinispan.authorization.entities.PermissionTicketResourceListQuery;
+import org.keycloak.models.cache.infinispan.authorization.entities.PermissionTicketScopeListQuery;
 import org.keycloak.models.cache.infinispan.authorization.entities.PolicyListQuery;
 import org.keycloak.models.cache.infinispan.authorization.entities.PolicyQuery;
 import org.keycloak.models.cache.infinispan.authorization.entities.PolicyResourceListQuery;
@@ -55,6 +62,8 @@ import org.keycloak.models.cache.infinispan.authorization.entities.ResourceListQ
 import org.keycloak.models.cache.infinispan.authorization.entities.ResourceQuery;
 import org.keycloak.models.cache.infinispan.authorization.entities.ResourceScopeListQuery;
 import org.keycloak.models.cache.infinispan.authorization.entities.ScopeListQuery;
+import org.keycloak.models.cache.infinispan.authorization.events.PermissionTicketRemovedEvent;
+import org.keycloak.models.cache.infinispan.authorization.events.PermissionTicketUpdatedEvent;
 import org.keycloak.models.cache.infinispan.authorization.events.PolicyRemovedEvent;
 import org.keycloak.models.cache.infinispan.authorization.events.PolicyUpdatedEvent;
 import org.keycloak.models.cache.infinispan.authorization.events.ResourceRemovedEvent;
@@ -82,6 +91,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
     protected Map<String, ScopeAdapter> managedScopes = new HashMap<>();
     protected Map<String, ResourceAdapter> managedResources = new HashMap<>();
     protected Map<String, PolicyAdapter> managedPolicies = new HashMap<>();
+    protected Map<String, PermissionTicketAdapter> managedPermissionTickets = new HashMap<>();
     protected Set<String> invalidations = new HashSet<>();
     protected Set<InvalidationEvent> invalidationEvents = new HashSet<>(); // Events to be sent across cluster
 
@@ -93,6 +103,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
     protected ScopeCache scopeCache;
     protected ResourceCache resourceCache;
     protected PolicyCache policyCache;
+    protected PermissionTicketCache permissionTicketCache;
 
     public StoreFactoryCacheSession(StoreFactoryCacheManager cache, KeycloakSession session) {
         this.cache = cache;
@@ -102,6 +113,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
         this.scopeCache = new ScopeCache();
         this.resourceCache = new ResourceCache();
         this.policyCache = new PolicyCache();
+        this.permissionTicketCache = new PermissionTicketCache();
         session.getTransactionManager().enlistPrepare(getPrepareTransaction());
         session.getTransactionManager().enlistAfterCompletion(getAfterTransaction());
     }
@@ -126,6 +138,11 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
         return policyCache;
     }
 
+    @Override
+    public PermissionTicketStore getPermissionTicketStore() {
+        return permissionTicketCache;
+    }
+
     public void close() {
     }
 
@@ -263,6 +280,14 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
         invalidationEvents.add(PolicyUpdatedEvent.create(id, name, resources, resourceTypes, scopes, serverId));
     }
 
+    public void registerPermissionTicketInvalidation(String id, String owner, String resource,  String scope,  String serverId) {
+        cache.permissionTicketUpdated(id, owner, resource, scope, serverId, invalidations);
+        PermissionTicketAdapter adapter = managedPermissionTickets.get(id);
+        if (adapter != null) adapter.invalidateFlag();
+
+        invalidationEvents.add(PermissionTicketUpdatedEvent.create(id, owner, resource, scope, serverId));
+    }
+
     private Set<String> getResourceTypes(Set<String> resources, String serverId) {
         if (resources == null) {
             return Collections.emptySet();
@@ -296,6 +321,10 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
         return getDelegate().getPolicyStore();
     }
 
+    public PermissionTicketStore getPermissionTicketStoreDelegate() {
+        return getDelegate().getPermissionTicketStore();
+    }
+
     public static String getResourceServerByClientCacheKey(String clientId) {
         return "resource.server.client.id." + clientId;
     }
@@ -340,6 +369,18 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
         return "policy.scope." + scope + "." + serverId;
     }
 
+    public static String getPermissionTicketByResource(String resourceId, String serverId) {
+        return "permission.ticket.resource." + resourceId + "." + serverId;
+    }
+
+    public static String getPermissionTicketByScope(String scopeId, String serverId) {
+        return "permission.ticket.scope." + scopeId + "." + serverId;
+    }
+
+    public static String getPermissionTicketByOwner(String owner, String serverId) {
+        return "permission.ticket.owner." + owner + "." + serverId;
+    }
+
     public StoreFactory getDelegate() {
         if (delegate != null) return delegate;
         delegate = session.getProvider(StoreFactory.class);
@@ -592,7 +633,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
 
          @Override
         public List<Resource> findByType(String type, String resourceServerId) {
-             if (type == null) return null;
+             if (type == null) return Collections.emptyList();
              String cacheKey = getResourceByTypeCacheKey(type, resourceServerId);
              return cacheQuery(cacheKey, ResourceListQuery.class, () -> getResourceStoreDelegate().findByType(type, resourceServerId),
                      (revision, resources) -> new ResourceListQuery(revision, cacheKey, resources.stream().map(resource -> resource.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
@@ -761,5 +802,108 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
         }
     }
 
+    protected class PermissionTicketCache implements PermissionTicketStore {
+        @Override
+        public PermissionTicket create(String resourceId, String scopeId, String requester, ResourceServer resourceServer) {
+            PermissionTicket created = getPermissionTicketStoreDelegate().create(resourceId, scopeId, requester, resourceServer);
+            registerPermissionTicketInvalidation(created.getId(), created.getOwner(), created.getResource().getId(), scopeId, created.getResourceServer().getId());
+            return created;
+        }
+
+        @Override
+        public void delete(String id) {
+            if (id == null) return;
+            PermissionTicket permission = findById(id, null);
+            if (permission == null) return;
+
+            cache.invalidateObject(id);
+            String scopeId = null;
+            if (permission.getScope() != null) {
+                scopeId = permission.getScope().getId();
+            }
+            invalidationEvents.add(PermissionTicketRemovedEvent.create(id, permission.getOwner(), permission.getResource().getId(), scopeId, permission.getResourceServer().getId()));
+            cache.permissionTicketRemoval(id, permission.getOwner(), permission.getResource().getId(), scopeId, permission.getResourceServer().getId(), invalidations);
+            getPermissionTicketStoreDelegate().delete(id);
+
+        }
+
+        @Override
+        public PermissionTicket findById(String id, String resourceServerId) {
+            if (id == null) return null;
+
+            CachedPermissionTicket cached = cache.get(id, CachedPermissionTicket.class);
+            if (cached != null) {
+                logger.tracev("by id cache hit: {0}", cached.getId());
+            }
+            boolean wasCached = false;
+            if (cached == null) {
+                Long loaded = cache.getCurrentRevision(id);
+                PermissionTicket model = getPermissionTicketStoreDelegate().findById(id, resourceServerId);
+                if (model == null) return null;
+                if (invalidations.contains(id)) return model;
+                cached = new CachedPermissionTicket(loaded, model);
+                cache.addRevisioned(cached, startupRevision);
+                wasCached =true;
+            } else if (invalidations.contains(id)) {
+                return getPermissionTicketStoreDelegate().findById(id, resourceServerId);
+            } else if (managedPermissionTickets.containsKey(id)) {
+                return managedPermissionTickets.get(id);
+            }
+            PermissionTicketAdapter adapter = new PermissionTicketAdapter(cached, StoreFactoryCacheSession.this);
+            managedPermissionTickets.put(id, adapter);
+            return adapter;
+        }
+
+        @Override
+        public List<PermissionTicket> findByResourceServer(String resourceServerId) {
+            return getPermissionTicketStoreDelegate().findByResourceServer(resourceServerId);
+        }
+
+        @Override
+        public List<PermissionTicket> findByResource(String resourceId, String resourceServerId) {
+            String cacheKey = getPermissionTicketByResource(resourceId, resourceServerId);
+            return cacheQuery(cacheKey, PermissionTicketResourceListQuery.class, () -> getPermissionTicketStoreDelegate().findByResource(resourceId, resourceServerId),
+                    (revision, permissions) -> new PermissionTicketResourceListQuery(revision, cacheKey, resourceId, permissions.stream().map(permission -> permission.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
+        }
+
+        @Override
+        public List<PermissionTicket> findByScope(String scopeId, String resourceServerId) {
+            String cacheKey = getPermissionTicketByScope(scopeId, resourceServerId);
+            return cacheQuery(cacheKey, PermissionTicketScopeListQuery.class, () -> getPermissionTicketStoreDelegate().findByScope(scopeId, resourceServerId),
+                    (revision, permissions) -> new PermissionTicketScopeListQuery(revision, cacheKey, scopeId, permissions.stream().map(permission -> permission.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
+        }
+
+        @Override
+        public List<PermissionTicket> find(Map<String, String> attributes, String resourceServerId, int firstResult, int maxResult) {
+            return getPermissionTicketStoreDelegate().find(attributes, resourceServerId, firstResult, maxResult);
+        }
+
+        @Override
+        public List<PermissionTicket> findByOwner(String owner, String resourceServerId) {
+            String cacheKey = getPermissionTicketByOwner(owner, resourceServerId);
+            return cacheQuery(cacheKey, PermissionTicketListQuery.class, () -> getPermissionTicketStoreDelegate().findByOwner(owner, resourceServerId),
+                    (revision, permissions) -> new PermissionTicketListQuery(revision, cacheKey, permissions.stream().map(permission -> permission.getId()).collect(Collectors.toSet()), resourceServerId), resourceServerId);
+        }
+
+        private <R, Q extends PermissionTicketQuery> List<R> cacheQuery(String cacheKey, Class<Q> queryType, Supplier<List<R>> resultSupplier, BiFunction<Long, List<R>, Q> querySupplier, String resourceServerId) {
+            Q query = cache.get(cacheKey, queryType);
+            if (query != null) {
+                logger.tracev("cache hit for key: {0}", cacheKey);
+            }
+            if (query == null) {
+                Long loaded = cache.getCurrentRevision(cacheKey);
+                List<R> model = resultSupplier.get();
+                if (model == null) return null;
+                if (invalidations.contains(cacheKey)) return model;
+                query = querySupplier.apply(loaded, model);
+                cache.addRevisioned(query, startupRevision);
+                return model;
+            } else if (query.isInvalid(invalidations)) {
+                return resultSupplier.get();
+            } else {
+                return query.getPermissions().stream().map(resourceId -> (R) findById(resourceId, resourceServerId)).collect(Collectors.toList());
+            }
+        }
+    }
 
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java
index e854002..f187e9c 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java
@@ -128,6 +128,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
     protected Set<String> adminEnabledEventOperations = new HashSet<String>();
     protected boolean adminEventsDetailsEnabled;
     protected List<String> defaultRoles;
+    private boolean allowUserManagedAccess;
 
     public Set<IdentityProviderMapperModel> getIdentityProviderMapperSet() {
         return identityProviderMapperSet;
@@ -151,6 +152,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
         displayName = model.getDisplayName();
         displayNameHtml = model.getDisplayNameHtml();
         enabled = model.isEnabled();
+        allowUserManagedAccess = model.isUserManagedAccessAllowed();
         sslRequired = model.getSslRequired();
         registrationAllowed = model.isRegistrationAllowed();
         registrationEmailAsUsername = model.isRegistrationEmailAsUsername();
@@ -629,4 +631,8 @@ public class CachedRealm extends AbstractExtendableRevisioned {
     public Map<String, String> getAttributes() {
         return attributes;
     }
+
+    public boolean isAllowUserManagedAccess() {
+        return allowUserManagedAccess;
+    }
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
index dd62377..7e6c3c7 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
@@ -142,6 +142,18 @@ public class RealmAdapter implements CachedRealmModel {
     }
 
     @Override
+    public boolean isUserManagedAccessAllowed() {
+        if (isUpdated()) return updated.isEnabled();
+        return cached.isAllowUserManagedAccess();
+    }
+
+    @Override
+    public void setUserManagedAccessAllowed(boolean userManagedAccessAllowed) {
+        getDelegateForUpdate();
+        updated.setUserManagedAccessAllowed(userManagedAccessAllowed);
+    }
+
+    @Override
     public SslRequired getSslRequired() {
         if (isUpdated()) return updated.getSslRequired();
         return cached.getSslRequired();
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PermissionTicketEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PermissionTicketEntity.java
new file mode 100644
index 0000000..c607bec
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/PermissionTicketEntity.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2017 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.authorization.jpa.entities;
+
+import javax.persistence.Access;
+import javax.persistence.AccessType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+@Entity
+@Table(name = "RESOURCE_SERVER_PERMISSION_TICKET", uniqueConstraints = {
+        @UniqueConstraint(columnNames = {"OWNER", "RESOURCE_SERVER_ID", "RESOURCE_ID", "SCOPE_ID"})
+})
+@NamedQueries(
+    {
+        @NamedQuery(name="findPermissionIdByResource", query="select p.id from PermissionTicketEntity p inner join p.resource r where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)"),
+        @NamedQuery(name="findPermissionIdByScope", query="select p.id from PermissionTicketEntity p inner join p.scope s where p.resourceServer.id = :serverId and (s.resourceServer.id = :serverId and s.id = :scopeId)"),
+        @NamedQuery(name="findPermissionTicketIdByServerId", query="select p.id from PermissionTicketEntity p where  p.resourceServer.id = :serverId ")
+    }
+)
+public class PermissionTicketEntity {
+
+    @Id
+    @Column(name = "ID", length = 36)
+    @Access(AccessType.PROPERTY)
+    // we do this because relationships often fetch id, but not entity.  This avoids an extra SQL
+    private String id;
+
+    @Column(name = "OWNER")
+    private String owner;
+
+    @Column(name = "REQUESTER")
+    private String requester;
+
+    @Column(name = "CREATED_TIMESTAMP")
+    private Long createdTimestamp;
+
+    @Column(name = "GRANTED_TIMESTAMP")
+    private Long grantedTimestamp;
+
+    @ManyToOne(optional = false, fetch = FetchType.LAZY)
+    @JoinColumn(name = "RESOURCE_ID")
+    private ResourceEntity resource;
+
+    @ManyToOne(optional = true, fetch = FetchType.LAZY)
+    @JoinColumn(name = "SCOPE_ID")
+    private ScopeEntity scope;
+
+    @ManyToOne(optional = false, fetch = FetchType.LAZY)
+    @JoinColumn(name = "RESOURCE_SERVER_ID")
+    private ResourceServerEntity resourceServer;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getOwner() {
+        return owner;
+    }
+
+    public void setOwner(String owner) {
+        this.owner = owner;
+    }
+
+    public ResourceEntity getResource() {
+        return resource;
+    }
+
+    public void setResource(ResourceEntity resource) {
+        this.resource = resource;
+    }
+
+    public ScopeEntity getScope() {
+        return scope;
+    }
+
+    public void setScope(ScopeEntity scope) {
+        this.scope = scope;
+    }
+
+    public ResourceServerEntity getResourceServer() {
+        return resourceServer;
+    }
+
+    public void setResourceServer(ResourceServerEntity resourceServer) {
+        this.resourceServer = resourceServer;
+    }
+
+    public void setRequester(String requester) {
+        this.requester = requester;
+    }
+
+    public String getRequester() {
+        return requester;
+    }
+
+    public Long getCreatedTimestamp() {
+        return createdTimestamp;
+    }
+
+    public void setCreatedTimestamp(Long createdTimestamp) {
+        this.createdTimestamp = createdTimestamp;
+    }
+
+    public Long getGrantedTimestamp() {
+        return grantedTimestamp;
+    }
+
+    public void setGrantedTimestamp(long grantedTimestamp) {
+        this.grantedTimestamp = grantedTimestamp;
+    }
+
+    public boolean isGranted() {
+        return grantedTimestamp != null;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        PermissionTicketEntity that = (PermissionTicketEntity) o;
+
+        return getId().equals(that.getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return getId().hashCode();
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java
index d91be73..6031a6e 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceEntity.java
@@ -45,6 +45,7 @@ import java.util.List;
 @NamedQueries(
         {
                 @NamedQuery(name="findResourceIdByOwner", query="select r.id from ResourceEntity r where r.resourceServer.id = :serverId and r.owner = :owner"),
+                @NamedQuery(name="findAnyResourceIdByOwner", query="select r.id from ResourceEntity r where r.owner = :owner"),
                 @NamedQuery(name="findResourceIdByUri", query="select r.id from ResourceEntity r where  r.resourceServer.id = :serverId  and r.uri = :uri"),
                 @NamedQuery(name="findResourceIdByName", query="select r.id from ResourceEntity r where  r.resourceServer.id = :serverId  and r.name = :name"),
                 @NamedQuery(name="findResourceIdByType", query="select r.id from ResourceEntity r where  r.resourceServer.id = :serverId  and r.type = :type"),
@@ -63,6 +64,9 @@ public class ResourceEntity {
     @Column(name = "NAME")
     private String name;
 
+    @Column(name = "DISPLAY_NAME")
+    private String displayName;
+
     @Column(name = "URI")
     private String uri;
 
@@ -75,6 +79,9 @@ public class ResourceEntity {
     @Column(name = "OWNER")
     private String owner;
 
+    @Column(name = "OWNER_MANAGED_ACCESS")
+    private boolean ownerManagedAccess;
+
     @ManyToOne(optional = false, fetch = FetchType.LAZY)
     @JoinColumn(name = "RESOURCE_SERVER_ID")
     private ResourceServerEntity resourceServer;
@@ -103,6 +110,14 @@ public class ResourceEntity {
         this.name = name;
     }
 
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
     public String getUri() {
         return uri;
     }
@@ -147,6 +162,14 @@ public class ResourceEntity {
         this.owner = owner;
     }
 
+    public void setOwnerManagedAccess(boolean ownerManagedAccess) {
+        this.ownerManagedAccess = ownerManagedAccess;
+    }
+
+    public boolean isOwnerManagedAccess() {
+        return ownerManagedAccess;
+    }
+
     public List<PolicyEntity> getPolicies() {
         return this.policies;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java
index 5d86d7b..9d9d7d8 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ScopeEntity.java
@@ -59,6 +59,9 @@ public class ScopeEntity {
     @Column(name = "NAME")
     private String name;
 
+    @Column(name = "DISPLAY_NAME")
+    private String displayName;
+
     @Column(name = "ICON_URI")
     private String iconUri;
 
@@ -86,6 +89,18 @@ public class ScopeEntity {
         this.name = name;
     }
 
+    public void setDisplayName(String displayName) {
+        if (displayName != null && !"".equals(displayName.trim())) {
+            this.displayName = displayName;
+        } else {
+            this.displayName = null;
+        }
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
     public String getIconUri() {
         return iconUri;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPermissionTicketStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPermissionTicketStore.java
new file mode 100644
index 0000000..ee51d5e
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPermissionTicketStore.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2017 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.authorization.jpa.store;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.EntityManager;
+import javax.persistence.FlushModeType;
+import javax.persistence.Query;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.jpa.entities.PermissionTicketEntity;
+import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.store.PermissionTicketStore;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class JPAPermissionTicketStore implements PermissionTicketStore {
+
+    private final EntityManager entityManager;
+    private final AuthorizationProvider provider;
+
+    public JPAPermissionTicketStore(EntityManager entityManager, AuthorizationProvider provider) {
+        this.entityManager = entityManager;
+        this.provider = provider;
+    }
+
+    @Override
+    public PermissionTicket create(String resourceId, String scopeId, String requester, ResourceServer resourceServer) {
+        PermissionTicketEntity entity = new PermissionTicketEntity();
+
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setResource(ResourceAdapter.toEntity(entityManager, provider.getStoreFactory().getResourceStore().findById(resourceId, resourceServer.getId())));
+        entity.setRequester(requester);
+        entity.setCreatedTimestamp(System.currentTimeMillis());
+
+        if (scopeId != null) {
+            entity.setScope(ScopeAdapter.toEntity(entityManager, provider.getStoreFactory().getScopeStore().findById(scopeId, resourceServer.getId())));
+        }
+
+        entity.setOwner(entity.getResource().getOwner());
+        entity.setResourceServer(ResourceServerAdapter.toEntity(entityManager, resourceServer));
+
+        this.entityManager.persist(entity);
+        this.entityManager.flush();
+        PermissionTicket model = new PermissionTicketAdapter(entity, entityManager, provider.getStoreFactory());
+        return model;
+    }
+
+    @Override
+    public void delete(String id) {
+        PermissionTicketEntity policy = entityManager.find(PermissionTicketEntity.class, id);
+        if (policy != null) {
+            this.entityManager.remove(policy);
+        }
+    }
+
+
+    @Override
+    public PermissionTicket findById(String id, String resourceServerId) {
+        if (id == null) {
+            return null;
+        }
+
+        PermissionTicketEntity entity = entityManager.find(PermissionTicketEntity.class, id);
+        if (entity == null) return null;
+
+        return new PermissionTicketAdapter(entity, entityManager, provider.getStoreFactory());
+    }
+
+    @Override
+    public List<PermissionTicket> findByResourceServer(final String resourceServerId) {
+        TypedQuery<String> query = entityManager.createNamedQuery("findPolicyIdByServerId", String.class);
+
+        query.setParameter("serverId", resourceServerId);
+
+        List<String> result = query.getResultList();
+        List<PermissionTicket> list = new LinkedList<>();
+        for (String id : result) {
+            list.add(provider.getStoreFactory().getPermissionTicketStore().findById(id, resourceServerId));
+        }
+        return list;
+    }
+
+    @Override
+    public List<PermissionTicket> findByResource(final String resourceId, String resourceServerId) {
+        TypedQuery<String> query = entityManager.createNamedQuery("findPermissionIdByResource", String.class);
+
+        query.setFlushMode(FlushModeType.COMMIT);
+        query.setParameter("resourceId", resourceId);
+        query.setParameter("serverId", resourceServerId);
+
+        List<String> result = query.getResultList();
+        List<PermissionTicket> list = new LinkedList<>();
+        for (String id : result) {
+            list.add(provider.getStoreFactory().getPermissionTicketStore().findById(id, resourceServerId));
+        }
+        return list;
+    }
+
+    @Override
+    public List<PermissionTicket> findByScope(String scopeId, String resourceServerId) {
+        if (scopeId==null) {
+            return Collections.emptyList();
+        }
+
+        // Use separate subquery to handle DB2 and MSSSQL
+        TypedQuery<String> query = entityManager.createNamedQuery("findPermissionIdByScope", String.class);
+
+        query.setFlushMode(FlushModeType.COMMIT);
+        query.setParameter("scopeId", scopeId);
+        query.setParameter("serverId", resourceServerId);
+
+        List<String> result = query.getResultList();
+        List<PermissionTicket> list = new LinkedList<>();
+        for (String id : result) {
+            list.add(provider.getStoreFactory().getPermissionTicketStore().findById(id, resourceServerId));
+        }
+        return list;
+    }
+
+    @Override
+    public List<PermissionTicket> find(Map<String, String> attributes, String resourceServerId, int firstResult, int maxResult) {
+        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<PermissionTicketEntity> querybuilder = builder.createQuery(PermissionTicketEntity.class);
+        Root<PermissionTicketEntity> root = querybuilder.from(PermissionTicketEntity.class);
+
+        querybuilder.select(root.get("id"));
+
+        List<Predicate> predicates = new ArrayList();
+
+        if (resourceServerId != null) {
+            predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
+        }
+
+        attributes.forEach((name, value) -> {
+            if (PermissionTicket.ID.equals(name)) {
+                predicates.add(root.get(name).in(value));
+            } else if (PermissionTicket.SCOPE.equals(name)) {
+                predicates.add(root.join("scope").get("id").in(value));
+            } else if (PermissionTicket.SCOPE_IS_NULL.equals(name)) {
+                if (Boolean.valueOf(value)) {
+                    predicates.add(builder.isNull(root.get("scope")));
+                } else {
+                    predicates.add(builder.isNotNull(root.get("scope")));
+                }
+            } else if (PermissionTicket.RESOURCE.equals(name)) {
+                predicates.add(root.join("resource").get("id").in(value));
+            } else if (PermissionTicket.OWNER.equals(name)) {
+                predicates.add(builder.equal(root.get("owner"), value));
+            } else if (PermissionTicket.REQUESTER.equals(name)) {
+                predicates.add(builder.equal(root.get("requester"), value));
+            } else if (PermissionTicket.GRANTED.equals(name)) {
+                if (Boolean.valueOf(value)) {
+                    predicates.add(builder.isNotNull(root.get("grantedTimestamp")));
+                } else {
+                    predicates.add(builder.isNull(root.get("grantedTimestamp")));
+                }
+            } else if (PermissionTicket.REQUESTER_IS_NULL.equals(name)) {
+                predicates.add(builder.isNull(root.get("requester")));
+            } else {
+                throw new RuntimeException("Unsupported filter [" + name + "]");
+            }
+        });
+
+        querybuilder.where(predicates.toArray(new Predicate[predicates.size()])).orderBy(builder.asc(root.get("resource").get("id")));
+
+        Query query = entityManager.createQuery(querybuilder);
+
+        if (firstResult != -1) {
+            query.setFirstResult(firstResult);
+        }
+        if (maxResult != -1) {
+            query.setMaxResults(maxResult);
+        }
+
+        List<String> result = query.getResultList();
+        List<PermissionTicket> list = new LinkedList<>();
+        PermissionTicketStore ticket = provider.getStoreFactory().getPermissionTicketStore();
+
+        for (String id : result) {
+            list.add(ticket.findById(id, resourceServerId));
+        }
+
+        return list;
+    }
+
+    @Override
+    public List<PermissionTicket> findByOwner(String owner, String resourceServerId) {
+        TypedQuery<String> query = entityManager.createNamedQuery("findPolicyIdByType", String.class);
+
+        query.setFlushMode(FlushModeType.COMMIT);
+        query.setParameter("serverId", resourceServerId);
+        query.setParameter("owner", owner);
+
+        List<String> result = query.getResultList();
+        List<PermissionTicket> list = new LinkedList<>();
+        for (String id : result) {
+            list.add(provider.getStoreFactory().getPermissionTicketStore().findById(id, resourceServerId));
+        }
+        return list;
+    }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java
index 5e79bad..308b073 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java
@@ -18,11 +18,11 @@
 package org.keycloak.authorization.jpa.store;
 
 import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.jpa.entities.PermissionTicketEntity;
 import org.keycloak.authorization.jpa.entities.PolicyEntity;
 import org.keycloak.authorization.jpa.entities.ResourceEntity;
 import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
 import org.keycloak.authorization.jpa.entities.ScopeEntity;
-import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.store.ResourceServerStore;
 import org.keycloak.models.ModelException;
@@ -30,7 +30,6 @@ import org.keycloak.storage.StorageId;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
-import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -77,6 +76,17 @@ public class JPAResourceServerStore implements ResourceServerStore {
             }
         }
 
+        {
+            TypedQuery<String> query = entityManager.createNamedQuery("findPermissionTicketIdByServerId", String.class);
+
+            query.setParameter("serverId", id);
+
+            List<String> result = query.getResultList();
+            for (String permissionId : result) {
+                entityManager.remove(entityManager.getReference(PermissionTicketEntity.class, permissionId));
+            }
+        }
+
         //entityManager.createNamedQuery("deleteResourceByResourceServer")
         //        .setParameter("serverId", id).executeUpdate();
         {
@@ -85,7 +95,6 @@ public class JPAResourceServerStore implements ResourceServerStore {
             query.setParameter("serverId", id);
 
             List<String> result = query.getResultList();
-            List<Resource> list = new LinkedList<>();
             for (String resourceId : result) {
                 entityManager.remove(entityManager.getReference(ResourceEntity.class, resourceId));
             }
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
index de83c42..c938afe 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
@@ -88,11 +88,20 @@ public class JPAResourceStore implements ResourceStore {
 
     @Override
     public List<Resource> findByOwner(String ownerId, String resourceServerId) {
-        TypedQuery<String> query = entityManager.createNamedQuery("findResourceIdByOwner", String.class);
+        String queryName = "findResourceIdByOwner";
+
+        if (resourceServerId == null) {
+            queryName = "findAnyResourceIdByOwner";
+        }
+
+        TypedQuery<String> query = entityManager.createNamedQuery(queryName, String.class);
 
         query.setFlushMode(FlushModeType.COMMIT);
         query.setParameter("owner", ownerId);
-        query.setParameter("serverId", resourceServerId);
+
+        if (resourceServerId != null) {
+            query.setParameter("serverId", resourceServerId);
+        }
 
         List<String> result = query.getResultList();
         List<Resource> list = new LinkedList<>();
@@ -161,13 +170,17 @@ public class JPAResourceStore implements ResourceStore {
         querybuilder.select(root.get("id"));
         List<Predicate> predicates = new ArrayList();
 
-        predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
+        if (resourceServerId != null) {
+            predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
+        }
 
         attributes.forEach((name, value) -> {
             if ("id".equals(name)) {
                 predicates.add(root.get(name).in(value));
             } else if ("scope".equals(name)) {
                 predicates.add(root.join("scopes").get("id").in(value));
+            } else if ("ownerManagedAccess".equals(name)) {
+                predicates.add(builder.equal(root.get(name), Boolean.valueOf(value[0])));
             } else {
                 predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
             }
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java
index 855f66a..cd08f0c 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAStoreFactory.java
@@ -21,6 +21,7 @@ package org.keycloak.authorization.jpa.store;
 import javax.persistence.EntityManager;
 
 import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.store.PermissionTicketStore;
 import org.keycloak.authorization.store.PolicyStore;
 import org.keycloak.authorization.store.ResourceServerStore;
 import org.keycloak.authorization.store.ResourceStore;
@@ -36,12 +37,14 @@ public class JPAStoreFactory implements StoreFactory {
     private final ResourceServerStore resourceServerStore;
     private final ResourceStore resourceStore;
     private final ScopeStore scopeStore;
+    private final JPAPermissionTicketStore permissionTicketStore;
 
     public JPAStoreFactory(EntityManager entityManager, AuthorizationProvider provider) {
         policyStore = new JPAPolicyStore(entityManager, provider);
         resourceServerStore = new JPAResourceServerStore(entityManager, provider);
         resourceStore = new JPAResourceStore(entityManager, provider);
         scopeStore = new JPAScopeStore(entityManager, provider);
+        permissionTicketStore = new JPAPermissionTicketStore(entityManager, provider);
     }
 
     @Override
@@ -65,6 +68,11 @@ public class JPAStoreFactory implements StoreFactory {
     }
 
     @Override
+    public PermissionTicketStore getPermissionTicketStore() {
+        return permissionTicketStore;
+    }
+
+    @Override
     public void close() {
 
     }
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/PermissionTicketAdapter.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/PermissionTicketAdapter.java
new file mode 100644
index 0000000..e1c56a2
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/PermissionTicketAdapter.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2017 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.authorization.jpa.store;
+
+import javax.persistence.EntityManager;
+
+import org.keycloak.authorization.jpa.entities.PermissionTicketEntity;
+import org.keycloak.authorization.jpa.entities.ScopeEntity;
+import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.jpa.JpaModel;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class PermissionTicketAdapter implements PermissionTicket, JpaModel<PermissionTicketEntity> {
+
+    private PermissionTicketEntity entity;
+    private EntityManager em;
+    private StoreFactory storeFactory;
+
+    public PermissionTicketAdapter(PermissionTicketEntity entity, EntityManager em, StoreFactory storeFactory) {
+        this.entity = entity;
+        this.em = em;
+        this.storeFactory = storeFactory;
+    }
+
+    @Override
+    public PermissionTicketEntity getEntity() {
+        return entity;
+    }
+
+    @Override
+    public String getId() {
+        return entity.getId();
+    }
+
+    @Override
+    public String getOwner() {
+        return entity.getOwner();
+    }
+
+    @Override
+    public String getRequester() {
+        return entity.getRequester();
+    }
+
+    @Override
+    public boolean isGranted() {
+        return entity.isGranted();
+    }
+
+    @Override
+    public Long getCreatedTimestamp() {
+        return entity.getCreatedTimestamp();
+    }
+
+    @Override
+    public Long getGrantedTimestamp() {
+        return entity.getGrantedTimestamp();
+    }
+
+    @Override
+    public void setGrantedTimestamp(Long millis) {
+        entity.setGrantedTimestamp(millis);
+    }
+
+    @Override
+    public ResourceServer getResourceServer() {
+        return storeFactory.getResourceServerStore().findById(entity.getResourceServer().getId());
+    }
+
+    @Override
+    public Resource getResource() {
+        return storeFactory.getResourceStore().findById(entity.getResource().getId(), getResourceServer().getId());
+    }
+
+    @Override
+    public Scope getScope() {
+        ScopeEntity scope = entity.getScope();
+
+        if (scope == null) {
+            return null;
+        }
+
+        return storeFactory.getScopeStore().findById(scope.getId(), getResourceServer().getId());
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || !(o instanceof Policy)) return false;
+
+        PermissionTicket that = (PermissionTicket) o;
+        return that.getId().equals(getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return getId().hashCode();
+    }
+
+    public static PermissionTicketEntity toEntity(EntityManager em, PermissionTicket permission) {
+        if (permission instanceof PermissionTicketAdapter) {
+            return ((PermissionTicketAdapter)permission).getEntity();
+        } else {
+            return em.getReference(PermissionTicketEntity.class, permission.getId());
+        }
+    }
+
+
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ResourceAdapter.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ResourceAdapter.java
index 9ce0de2..782f084 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ResourceAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ResourceAdapter.java
@@ -63,6 +63,16 @@ public class ResourceAdapter implements Resource, JpaModel<ResourceEntity> {
     }
 
     @Override
+    public String getDisplayName() {
+        return entity.getDisplayName();
+    }
+
+    @Override
+    public void setDisplayName(String name) {
+        entity.setDisplayName(name);
+    }
+
+    @Override
     public void setName(String name) {
         entity.setName(name);
 
@@ -122,6 +132,16 @@ public class ResourceAdapter implements Resource, JpaModel<ResourceEntity> {
     }
 
     @Override
+    public boolean isOwnerManagedAccess() {
+        return entity.isOwnerManagedAccess();
+    }
+
+    @Override
+    public void setOwnerManagedAccess(boolean ownerManagedAccess) {
+        entity.setOwnerManagedAccess(ownerManagedAccess);
+    }
+
+    @Override
     public void updateScopes(Set<Scope> toUpdate) {
         Set<String> ids = new HashSet<>();
         for (Scope scope : toUpdate) {
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ScopeAdapter.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ScopeAdapter.java
index f77310e..1a83d27 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ScopeAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/ScopeAdapter.java
@@ -61,6 +61,16 @@ public class ScopeAdapter implements Scope, JpaModel<ScopeEntity> {
     }
 
     @Override
+    public String getDisplayName() {
+        return entity.getDisplayName();
+    }
+
+    @Override
+    public void setDisplayName(String name) {
+        entity.setDisplayName(name);
+    }
+
+    @Override
     public String getIconUri() {
         return entity.getIconUri();
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
index fe6ee14..4f6244f 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -237,6 +237,9 @@ public class RealmEntity {
     @Column(name="DEFAULT_LOCALE")
     protected String defaultLocale;
 
+    @Column(name="ALLOW_USER_MANAGED_ACCESS")
+    private boolean allowUserManagedAccess;
+
 
     public String getId() {
         return id;
@@ -762,6 +765,14 @@ public class RealmEntity {
         this.clientTemplates = clientTemplates;
     }
 
+    public void setAllowUserManagedAccess(boolean allowUserManagedAccess) {
+        this.allowUserManagedAccess = allowUserManagedAccess;
+    }
+
+    public boolean isAllowUserManagedAccess() {
+        return allowUserManagedAccess;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -779,6 +790,5 @@ public class RealmEntity {
     public int hashCode() {
         return id.hashCode();
     }
-
 }
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 26bfc24..e737a25 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -115,6 +115,17 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
     }
 
     @Override
+    public boolean isUserManagedAccessAllowed() {
+        return realm.isAllowUserManagedAccess();
+    }
+
+    @Override
+    public void setUserManagedAccessAllowed(boolean userManagedAccessAllowed) {
+        realm.setAllowUserManagedAccess(userManagedAccessAllowed);
+        em.flush();
+    }
+
+    @Override
     public boolean isRegistrationAllowed() {
         return realm.isRegistrationAllowed();
     }
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-authz-4.0.0.CR1.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-authz-4.0.0.CR1.xml
new file mode 100755
index 0000000..aa3d17e
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-authz-4.0.0.CR1.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ * Copyright 2017 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.
+  -->
+
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.2.xsd">
+    <changeSet author="psilva@redhat.com" id="authz-3.3.0.CR1">
+        <createTable tableName="RESOURCE_SERVER_PERMISSION_TICKET">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="OWNER" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="REQUESTER" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="CREATED_TIMESTAMP" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column name="GRANTED_TIMESTAMP" type="BIGINT">
+                <constraints nullable="true"/>
+            </column>
+            <column name="RESOURCE_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="SCOPE_ID" type="VARCHAR(36)">
+                <constraints nullable="true"/>
+            </column>
+            <column name="RESOURCE_SERVER_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FAPMT" tableName="RESOURCE_SERVER_PERMISSION_TICKET"/>
+        <addForeignKeyConstraint baseColumnNames="RESOURCE_SERVER_ID" baseTableName="RESOURCE_SERVER_PERMISSION_TICKET" constraintName="FK_FRSRHO213XCX4WNKOG82SSPMT" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER"/>
+        <addForeignKeyConstraint baseColumnNames="RESOURCE_ID" baseTableName="RESOURCE_SERVER_PERMISSION_TICKET" constraintName="FK_FRSRHO213XCX4WNKOG83SSPMT" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER_RESOURCE"/>
+        <addForeignKeyConstraint baseColumnNames="SCOPE_ID" baseTableName="RESOURCE_SERVER_PERMISSION_TICKET" constraintName="FK_FRSRHO213XCX4WNKOG84SSPMT" referencedColumnNames="ID" referencedTableName="RESOURCE_SERVER_SCOPE"/>
+        <addUniqueConstraint columnNames="OWNER, REQUESTER, RESOURCE_SERVER_ID, RESOURCE_ID, SCOPE_ID" constraintName="UK_FRSR6T700S9V50BU18WS5PMT" tableName="RESOURCE_SERVER_PERMISSION_TICKET"/>
+
+        <addColumn tableName="RESOURCE_SERVER_RESOURCE">
+            <column name="OWNER_MANAGED_ACCESS" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false" />
+            </column>
+        </addColumn>
+
+        <addColumn tableName="RESOURCE_SERVER_RESOURCE">
+            <column name="DISPLAY_NAME" type="VARCHAR(255)" >
+                <constraints nullable="true" />
+            </column>
+        </addColumn>
+
+        <addColumn tableName="RESOURCE_SERVER_SCOPE">
+            <column name="DISPLAY_NAME" type="VARCHAR(255)" >
+                <constraints nullable="true" />
+            </column>
+        </addColumn>
+
+        <addColumn tableName="REALM">
+            <column name="ALLOW_USER_MANAGED_ACCESS" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
index fa824e2..c9d0e16 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -54,4 +54,5 @@
     <include file="META-INF/jpa-changelog-3.4.1.xml"/>
     <include file="META-INF/jpa-changelog-3.4.2.xml"/>
     <include file="META-INF/jpa-changelog-4.0.0.xml"/>
+    <include file="META-INF/jpa-changelog-authz-4.0.0.CR1.xml"/>
 </databaseChangeLog>
diff --git a/model/jpa/src/main/resources/META-INF/persistence.xml b/model/jpa/src/main/resources/META-INF/persistence.xml
index 36e3fb4..86eda78 100755
--- a/model/jpa/src/main/resources/META-INF/persistence.xml
+++ b/model/jpa/src/main/resources/META-INF/persistence.xml
@@ -66,6 +66,7 @@
         <class>org.keycloak.authorization.jpa.entities.ResourceEntity</class>
         <class>org.keycloak.authorization.jpa.entities.ScopeEntity</class>
         <class>org.keycloak.authorization.jpa.entities.PolicyEntity</class>
+        <class>org.keycloak.authorization.jpa.entities.PermissionTicketEntity</class>
 
         <!-- User Federation Storage -->
         <class>org.keycloak.storage.jpa.entity.BrokerLinkEntity</class>
diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
index 5eb18db..c316c47 100755
--- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
@@ -111,6 +111,10 @@ public interface RealmModel extends RoleContainerModel {
 
     void setEditUsernameAllowed(boolean editUsernameAllowed);
 
+    boolean isUserManagedAccessAllowed();
+
+    void setUserManagedAccessAllowed(boolean userManagedAccessAllowed);
+
     void setAttribute(String name, String value);
     void setAttribute(String name, Boolean value);
     void setAttribute(String name, Integer value);
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
index 644b90a..81da078 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
@@ -24,6 +24,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import org.keycloak.authorization.model.PermissionTicket;
 import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
@@ -32,6 +33,7 @@ import org.keycloak.authorization.permission.evaluator.Evaluators;
 import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
 import org.keycloak.authorization.policy.provider.PolicyProvider;
 import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.authorization.store.PermissionTicketStore;
 import org.keycloak.authorization.store.PolicyStore;
 import org.keycloak.authorization.store.ResourceServerStore;
 import org.keycloak.authorization.store.ResourceStore;
@@ -122,11 +124,69 @@ public final class AuthorizationProvider implements Provider {
         return storeFactoryDelegate;
     }
 
+    /**
+     * Returns the registered {@link PolicyProviderFactory}.
+     *
+     * @return a {@link List} containing all registered {@link PolicyProviderFactory}
+     */
+    public Collection<PolicyProviderFactory> getProviderFactories() {
+        return this.policyProviderFactories.values();
+    }
+
+    /**
+     * Returns a {@link PolicyProviderFactory} given a <code>type</code>.
+     *
+     * @param type the type of the policy provider
+     * @param <F> the expected type of the provider
+     * @return a {@link PolicyProviderFactory} with the given <code>type</code>
+     */
+    public <F extends PolicyProviderFactory> F getProviderFactory(String type) {
+        return (F) policyProviderFactories.get(type);
+    }
+
+    /**
+     * Returns a {@link PolicyProviderFactory} given a <code>type</code>.
+     *
+     * @param type the type of the policy provider
+     * @param <P> the expected type of the provider
+     * @return a {@link PolicyProvider} with the given <code>type</code>
+     */
+    public <P extends PolicyProvider> P getProvider(String type) {
+        PolicyProviderFactory policyProviderFactory = policyProviderFactories.get(type);
+
+        if (policyProviderFactory == null) {
+            return null;
+        }
+
+        return (P) policyProviderFactory.create(this);
+    }
+
+    public KeycloakSession getKeycloakSession() {
+        return this.keycloakSession;
+    }
+
+    public RealmModel getRealm() {
+        return realm;
+    }
+
+    @Override
+    public void close() {
+
+    }
+
     private StoreFactory createStoreFactory(StoreFactory storeFactory) {
         return new StoreFactory() {
+
+            ResourceStore resourceStore;
+            ScopeStore scopeStore;
+            PolicyStore policyStore;
+
             @Override
             public ResourceStore getResourceStore() {
-                return storeFactory.getResourceStore();
+                if (resourceStore == null) {
+                    resourceStore = createResourceStoreWrapper(storeFactory);
+                }
+                return resourceStore;
             }
 
             @Override
@@ -136,192 +196,268 @@ public final class AuthorizationProvider implements Provider {
 
             @Override
             public ScopeStore getScopeStore() {
-                return storeFactory.getScopeStore();
+                if (scopeStore == null) {
+                    scopeStore = createScopeWrapper(storeFactory);
+                }
+                return scopeStore;
             }
 
             @Override
             public PolicyStore getPolicyStore() {
-                PolicyStore policyStore = storeFactory.getPolicyStore();
-                return new PolicyStore() {
-                    @Override
-                    public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
-                        Set<String> resources = representation.getResources();
-
-                        if (resources != null) {
-                            representation.setResources(resources.stream().map(id -> {
-                                Resource resource = getResourceStore().findById(id, resourceServer.getId());
-
-                                if (resource == null) {
-                                    resource = getResourceStore().findByName(id, resourceServer.getId());
-                                }
-
-                                if (resource == null) {
-                                    throw new RuntimeException("Resource [" + id + "] does not exist");
-                                }
-
-                                return resource.getId();
-                            }).collect(Collectors.toSet()));
+                if (policyStore == null) {
+                    policyStore = createPolicyWrapper(storeFactory);
+                }
+                return policyStore;
+            }
+
+            @Override
+            public PermissionTicketStore getPermissionTicketStore() {
+                return storeFactory.getPermissionTicketStore();
+            }
+
+            @Override
+            public void close() {
+                storeFactory.close();
+            }
+        };
+    }
+
+    private ScopeStore createScopeWrapper(StoreFactory storeFactory) {
+        return new ScopeStore() {
+
+            ScopeStore delegate = storeFactory.getScopeStore();
+
+            @Override
+            public Scope create(String name, ResourceServer resourceServer) {
+                return delegate.create(name, resourceServer);
+            }
+
+            @Override
+            public void delete(String id) {
+                Scope scope = findById(id, null);
+                PermissionTicketStore ticketStore = storeFactory.getPermissionTicketStore();
+                List<PermissionTicket> permissions = ticketStore.findByScope(id, scope.getResourceServer().getId());
+
+                for (PermissionTicket permission : permissions) {
+                    ticketStore.delete(permission.getId());
+                }
+
+                delegate.delete(id);
+            }
+
+            @Override
+            public Scope findById(String id, String resourceServerId) {
+                return delegate.findById(id, resourceServerId);
+            }
+
+            @Override
+            public Scope findByName(String name, String resourceServerId) {
+                return delegate.findByName(name, resourceServerId);
+            }
+
+            @Override
+            public List<Scope> findByResourceServer(String id) {
+                return delegate.findByResourceServer(id);
+            }
+
+            @Override
+            public List<Scope> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+                return delegate.findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
+            }
+        };
+    }
+
+    private PolicyStore createPolicyWrapper(StoreFactory storeFactory) {
+        return new PolicyStore() {
+
+            PolicyStore policyStore = storeFactory.getPolicyStore();
+
+            @Override
+            public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
+                Set<String> resources = representation.getResources();
+
+                if (resources != null) {
+                    representation.setResources(resources.stream().map(id -> {
+                        Resource resource = storeFactory.getResourceStore().findById(id, resourceServer.getId());
+
+                        if (resource == null) {
+                            resource = storeFactory.getResourceStore().findByName(id, resourceServer.getId());
+                        }
+
+                        if (resource == null) {
+                            throw new RuntimeException("Resource [" + id + "] does not exist");
                         }
 
-                        Set<String> scopes = representation.getScopes();
+                        return resource.getId();
+                    }).collect(Collectors.toSet()));
+                }
 
-                        if (scopes != null) {
-                            representation.setScopes(scopes.stream().map(id -> {
-                                Scope scope = getScopeStore().findById(id, resourceServer.getId());
+                Set<String> scopes = representation.getScopes();
 
-                                if (scope == null) {
-                                    scope = getScopeStore().findByName(id, resourceServer.getId());
-                                }
+                if (scopes != null) {
+                    representation.setScopes(scopes.stream().map(id -> {
+                        Scope scope = storeFactory.getScopeStore().findById(id, resourceServer.getId());
 
-                                if (scope == null) {
-                                    throw new RuntimeException("Scope [" + id + "] does not exist");
-                                }
+                        if (scope == null) {
+                            scope = storeFactory.getScopeStore().findByName(id, resourceServer.getId());
+                        }
 
-                                return scope.getId();
-                            }).collect(Collectors.toSet()));
+                        if (scope == null) {
+                            throw new RuntimeException("Scope [" + id + "] does not exist");
                         }
 
+                        return scope.getId();
+                    }).collect(Collectors.toSet()));
+                }
 
-                        Set<String> policies = representation.getPolicies();
 
-                        if (policies != null) {
-                            representation.setPolicies(policies.stream().map(id -> {
-                                Policy policy = getPolicyStore().findById(id, resourceServer.getId());
+                Set<String> policies = representation.getPolicies();
 
-                                if (policy == null) {
-                                    policy = getPolicyStore().findByName(id, resourceServer.getId());
-                                }
+                if (policies != null) {
+                    representation.setPolicies(policies.stream().map(id -> {
+                        Policy policy = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
 
-                                if (policy == null) {
-                                    throw new RuntimeException("Policy [" + id + "] does not exist");
-                                }
+                        if (policy == null) {
+                            policy = storeFactory.getPolicyStore().findByName(id, resourceServer.getId());
+                        }
 
-                                return policy.getId();
-                            }).collect(Collectors.toSet()));
+                        if (policy == null) {
+                            throw new RuntimeException("Policy [" + id + "] does not exist");
                         }
 
-                        return RepresentationToModel.toModel(representation, AuthorizationProvider.this, policyStore.create(representation, resourceServer));
-                    }
+                        return policy.getId();
+                    }).collect(Collectors.toSet()));
+                }
 
-                    @Override
-                    public void delete(String id) {
-                        Policy policy = findById(id, null);
+                return RepresentationToModel.toModel(representation, AuthorizationProvider.this, policyStore.create(representation, resourceServer));
+            }
 
-                        if (policy != null) {
-                            ResourceServer resourceServer = policy.getResourceServer();
+            @Override
+            public void delete(String id) {
+                Policy policy = findById(id, null);
 
-                            findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
-                                dependentPolicy.removeAssociatedPolicy(policy);
-                                if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
-                                    delete(dependentPolicy.getId());
-                                }
-                            });
+                if (policy != null) {
+                    ResourceServer resourceServer = policy.getResourceServer();
 
-                            policyStore.delete(id);
+                    findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
+                        dependentPolicy.removeAssociatedPolicy(policy);
+                        if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
+                            delete(dependentPolicy.getId());
                         }
-                    }
+                    });
 
-                    @Override
-                    public Policy findById(String id, String resourceServerId) {
-                        return policyStore.findById(id, resourceServerId);
-                    }
+                    policyStore.delete(id);
+                }
+            }
 
-                    @Override
-                    public Policy findByName(String name, String resourceServerId) {
-                        return policyStore.findByName(name, resourceServerId);
-                    }
+            @Override
+            public Policy findById(String id, String resourceServerId) {
+                return policyStore.findById(id, resourceServerId);
+            }
 
-                    @Override
-                    public List<Policy> findByResourceServer(String resourceServerId) {
-                        return policyStore.findByResourceServer(resourceServerId);
-                    }
+            @Override
+            public Policy findByName(String name, String resourceServerId) {
+                return policyStore.findByName(name, resourceServerId);
+            }
 
-                    @Override
-                    public List<Policy> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
-                        return policyStore.findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
-                    }
+            @Override
+            public List<Policy> findByResourceServer(String resourceServerId) {
+                return policyStore.findByResourceServer(resourceServerId);
+            }
 
-                    @Override
-                    public List<Policy> findByResource(String resourceId, String resourceServerId) {
-                        return policyStore.findByResource(resourceId, resourceServerId);
-                    }
+            @Override
+            public List<Policy> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+                return policyStore.findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
+            }
 
-                    @Override
-                    public List<Policy> findByResourceType(String resourceType, String resourceServerId) {
-                        return policyStore.findByResourceType(resourceType, resourceServerId);
-                    }
+            @Override
+            public List<Policy> findByResource(String resourceId, String resourceServerId) {
+                return policyStore.findByResource(resourceId, resourceServerId);
+            }
 
-                    @Override
-                    public List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId) {
-                        return policyStore.findByScopeIds(scopeIds, resourceServerId);
-                    }
+            @Override
+            public List<Policy> findByResourceType(String resourceType, String resourceServerId) {
+                return policyStore.findByResourceType(resourceType, resourceServerId);
+            }
 
-                    @Override
-                    public List<Policy> findByType(String type, String resourceServerId) {
-                        return policyStore.findByType(type, resourceServerId);
-                    }
+            @Override
+            public List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId) {
+                return policyStore.findByScopeIds(scopeIds, resourceServerId);
+            }
 
-                    @Override
-                    public List<Policy> findDependentPolicies(String id, String resourceServerId) {
-                        return policyStore.findDependentPolicies(id, resourceServerId);
-                    }
-                };
+            @Override
+            public List<Policy> findByType(String type, String resourceServerId) {
+                return policyStore.findByType(type, resourceServerId);
             }
 
             @Override
-            public void close() {
-                storeFactory.close();
+            public List<Policy> findDependentPolicies(String id, String resourceServerId) {
+                return policyStore.findDependentPolicies(id, resourceServerId);
             }
         };
     }
 
-    /**
-     * Returns the registered {@link PolicyProviderFactory}.
-     *
-     * @return a {@link List} containing all registered {@link PolicyProviderFactory}
-     */
-    public Collection<PolicyProviderFactory> getProviderFactories() {
-        return this.policyProviderFactories.values();
-    }
+    private ResourceStore createResourceStoreWrapper(StoreFactory storeFactory) {
+        return new ResourceStore() {
+            ResourceStore delegate = storeFactory.getResourceStore();
 
-    /**
-     * Returns a {@link PolicyProviderFactory} given a <code>type</code>.
-     *
-     * @param type the type of the policy provider
-     * @param <F> the expected type of the provider
-     * @return a {@link PolicyProviderFactory} with the given <code>type</code>
-     */
-    public <F extends PolicyProviderFactory> F getProviderFactory(String type) {
-        return (F) policyProviderFactories.get(type);
-    }
+            @Override
+            public Resource create(String name, ResourceServer resourceServer, String owner) {
+                return delegate.create(name, resourceServer, owner);
+            }
 
-    /**
-     * Returns a {@link PolicyProviderFactory} given a <code>type</code>.
-     *
-     * @param type the type of the policy provider
-     * @param <P> the expected type of the provider
-     * @return a {@link PolicyProvider} with the given <code>type</code>
-     */
-    public <P extends PolicyProvider> P getProvider(String type) {
-        PolicyProviderFactory policyProviderFactory = policyProviderFactories.get(type);
+            @Override
+            public void delete(String id) {
+                Resource resource = findById(id, null);
+                PermissionTicketStore ticketStore = storeFactory.getPermissionTicketStore();
+                List<PermissionTicket> permissions = ticketStore.findByResource(id, resource.getResourceServer().getId());
 
-        if (policyProviderFactory == null) {
-            return null;
-        }
+                for (PermissionTicket permission : permissions) {
+                    ticketStore.delete(permission.getId());
+                }
 
-        return (P) policyProviderFactory.create(this);
-    }
+                delegate.delete(id);
+            }
 
-    public KeycloakSession getKeycloakSession() {
-        return this.keycloakSession;
-    }
+            @Override
+            public Resource findById(String id, String resourceServerId) {
+                return delegate.findById(id, resourceServerId);
+            }
 
-    public RealmModel getRealm() {
-        return realm;
-    }
+            @Override
+            public List<Resource> findByOwner(String ownerId, String resourceServerId) {
+                return delegate.findByOwner(ownerId, resourceServerId);
+            }
 
-    @Override
-    public void close() {
+            @Override
+            public List<Resource> findByUri(String uri, String resourceServerId) {
+                return delegate.findByUri(uri, resourceServerId);
+            }
+
+            @Override
+            public List<Resource> findByResourceServer(String resourceServerId) {
+                return delegate.findByResourceServer(resourceServerId);
+            }
+
+            @Override
+            public List<Resource> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
+                return delegate.findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
+            }
+
+            @Override
+            public List<Resource> findByScope(List<String> id, String resourceServerId) {
+                return delegate.findByScope(id, resourceServerId);
+            }
 
+            @Override
+            public Resource findByName(String name, String resourceServerId) {
+                return delegate.findByName(name, resourceServerId);
+            }
+
+            @Override
+            public List<Resource> findByType(String type, String resourceServerId) {
+                return delegate.findByType(type, resourceServerId);
+            }
+        };
     }
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/model/PermissionTicket.java b/server-spi-private/src/main/java/org/keycloak/authorization/model/PermissionTicket.java
new file mode 100644
index 0000000..39366d6
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/model/PermissionTicket.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2017 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.authorization.model;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface PermissionTicket {
+
+    String ID = "id";
+    String RESOURCE = "resource.id";
+    String SCOPE = "scope.id";
+    String SCOPE_IS_NULL = "scope_is_null";
+    String OWNER = "owner";
+    String GRANTED = "granted";
+    String REQUESTER = "requester";
+    String REQUESTER_IS_NULL = "requester_is_null";
+
+    /**
+     * Returns the unique identifier for this instance.
+     *
+     * @return the unique identifier for this instance
+     */
+    String getId();
+
+    /**
+     * Returns the resource's owner, which is usually an identifier that uniquely identifies the resource's owner.
+     *
+     * @return the owner of this resource
+     */
+    String getOwner();
+
+    String getRequester();
+
+    /**
+     * Returns the {@link Resource} associated with this instance
+     *
+     * @return the {@link Resource} associated with this instance
+     */
+    Resource getResource();
+
+    /**
+     * Returns the {@link Scope} associated with this instance
+     *
+     * @return the {@link Scope} associated with this instance
+     */
+    Scope getScope();
+
+    boolean isGranted();
+
+    Long getCreatedTimestamp();
+
+    Long getGrantedTimestamp();
+    void setGrantedTimestamp(Long millis);
+
+    /**
+     * Returns the {@link ResourceServer} where this policy belongs to.
+     *
+     * @return a resource server
+     */
+    ResourceServer getResourceServer();
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/model/Resource.java b/server-spi-private/src/main/java/org/keycloak/authorization/model/Resource.java
index 4c2521c..cdfc0b6 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/model/Resource.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/model/Resource.java
@@ -50,6 +50,20 @@ public interface Resource {
     void setName(String name);
 
     /**
+     * Returns the end user friendly name for this resource. If not defined, value for {@link #getName()} is returned.
+     *
+     * @return the friendly name for this resource
+     */
+    String getDisplayName();
+
+    /**
+     * Sets an end user friendly name for this resource.
+     *
+     * @param name the name of this resource
+     */
+    void setDisplayName(String name);
+
+    /**
      * Returns a {@link java.net.URI} that uniquely identify this resource.
      *
      * @return an {@link java.net.URI} for this resource or null if not defined.
@@ -112,5 +126,8 @@ public interface Resource {
      */
     String getOwner();
 
+    boolean isOwnerManagedAccess();
+    void setOwnerManagedAccess(boolean ownerManagedAccess);
+
     void updateScopes(Set<Scope> scopes);
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/model/Scope.java b/server-spi-private/src/main/java/org/keycloak/authorization/model/Scope.java
index e13a789..fd90eca 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/model/Scope.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/model/Scope.java
@@ -48,6 +48,20 @@ public interface Scope {
     void setName(String name);
 
     /**
+     * Returns the end user friendly name for this scope. If not defined, value for {@link #getName()} is returned.
+     *
+     * @return the friendly name for this scope
+     */
+    String getDisplayName();
+
+    /**
+     * Sets an end user friendly name for this scope.
+     *
+     * @param name the name of this scope
+     */
+    void setDisplayName(String name);
+
+    /**
      * Returns an icon {@link java.net.URI} for this scope.
      *
      * @return a uri for an icon
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/IterablePermissionEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/IterablePermissionEvaluator.java
index f2da3a5..c43acac 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/IterablePermissionEvaluator.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/IterablePermissionEvaluator.java
@@ -44,7 +44,7 @@ class IterablePermissionEvaluator implements PermissionEvaluator {
     }
 
     @Override
-    public void evaluate(Decision decision) {
+    public Decision evaluate(Decision decision) {
         try {
             while (this.permissions.hasNext()) {
                 this.policyEvaluator.evaluate(this.permissions.next(), this.executionContext, decision);
@@ -53,6 +53,7 @@ class IterablePermissionEvaluator implements PermissionEvaluator {
         } catch (Throwable cause) {
             decision.onError(cause);
         }
+        return decision;
     }
 
     @Override
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/PermissionEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/PermissionEvaluator.java
index 587856f..ae0d7fd 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/PermissionEvaluator.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/PermissionEvaluator.java
@@ -30,6 +30,6 @@ import org.keycloak.authorization.policy.evaluation.Result;
  */
 public interface PermissionEvaluator {
 
-    void evaluate(Decision decision);
+    <D extends Decision> D evaluate(D decision);
     List<Result> evaluate();
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java
index 2ffc049..bfba2c6 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java
@@ -62,17 +62,25 @@ public abstract class DecisionResultCollector implements Decision<DefaultEvaluat
             }
 
             if (deniedCount == 0) {
-                result.setStatus(Effect.PERMIT);
+                onGrant(result);
             } else {
-                result.setStatus(Effect.DENY);
+                onDeny(result);
             }
         }
 
         onComplete(results.values().stream().collect(Collectors.toList()));
     }
 
+    protected void onGrant(Result result) {
+        result.setStatus(Effect.PERMIT);
+    }
+
     protected abstract void onComplete(List<Result> results);
 
+    protected void onDeny(Result result) {
+        result.setStatus(Effect.DENY);
+    }
+
     private boolean isGranted(Result.PolicyResult policyResult) {
         List<Result.PolicyResult> values = policyResult.getAssociatedPolicies();
 
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
index c720504..9b708c5 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
@@ -19,6 +19,7 @@
 package org.keycloak.authorization.policy.evaluation;
 
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -75,7 +76,15 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
             evaluatePolicies(() -> policyStore.findByResource(resource.getId(), resourceServer.getId()), consumer);
 
             if (resource.getType() != null) {
-                evaluatePolicies(() -> policyStore.findByResourceType(resource.getType(), resourceServer.getId()), consumer);
+                evaluatePolicies(() -> {
+                    List<Policy> policies = policyStore.findByResourceType(resource.getType(), resourceServer.getId());
+
+                    for (Resource typedResource : resourceStore.findByType(resource.getType(), resourceServer.getId())) {
+                        policies.addAll(policyStore.findByResource(typedResource.getId(), resourceServer.getId()));
+                    }
+
+                    return policies;
+                }, consumer);
             }
 
             if (scopes.isEmpty() && !resource.getScopes().isEmpty()) {
@@ -137,7 +146,11 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
         Set<Resource> policyResources = policy.getResources();
 
         if (resourcePermission != null && !policyResources.isEmpty()) {
-                if (!policyResources.stream().filter(resource -> resource.getId().equals(resourcePermission.getId())).findFirst().isPresent()) {
+            if (!policyResources.stream().filter(resource -> {
+                Iterator<Resource> policyResourceType = policy.getResources().iterator();
+                Resource policyResource = policyResourceType.hasNext() ? policyResourceType.next() : null;
+                return resource.getId().equals(resourcePermission.getId()) || (policyResourceType != null && policyResource.getType() != null && policyResource.getType().equals(resourcePermission.getType()));
+            }).findFirst().isPresent()) {
                 return false;
             }
         }
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PermissionTicketAwareDecisionResultCollector.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PermissionTicketAwareDecisionResultCollector.java
new file mode 100644
index 0000000..708f7e1
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PermissionTicketAwareDecisionResultCollector.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2017 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.authorization.policy.evaluation;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.identity.Identity;
+import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.permission.ResourcePermission;
+import org.keycloak.authorization.policy.evaluation.Result.PolicyResult;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.authorization.store.ScopeStore;
+import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.PermissionTicketToken;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionTicketAwareDecisionResultCollector extends DecisionResultCollector {
+
+    private final AuthorizationRequest request;
+    private PermissionTicketToken ticket;
+    private final Identity identity;
+    private ResourceServer resourceServer;
+    private final AuthorizationProvider authorization;
+    private List<Result> results;
+
+    public PermissionTicketAwareDecisionResultCollector(AuthorizationRequest request, PermissionTicketToken ticket, Identity identity, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        this.request = request;
+        this.ticket = ticket;
+        this.identity = identity;
+        this.resourceServer = resourceServer;
+        this.authorization = authorization;
+    }
+
+    @Override
+    protected void onDeny(Result result) {
+        ResourcePermission permission = result.getPermission();
+        Resource resource = permission.getResource();
+
+        if (resource != null && resource.isOwnerManagedAccess()) {
+            if (!resource.getOwner().equals(identity.getId())) {
+                Map<String, String> filters = new HashMap<>();
+
+                filters.put(PermissionTicket.RESOURCE, resource.getId());
+                filters.put(PermissionTicket.REQUESTER, identity.getId());
+                filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
+
+                List<PermissionTicket> permissions = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer().getId(), -1, -1);
+
+                if (!permissions.isEmpty()) {
+                    List<Scope> grantedScopes = new ArrayList<>();
+
+                    for (PolicyResult policyResult : result.getResults()) {
+                        for (PermissionTicket ticket : permissions) {
+                            Scope grantedScope = ticket.getScope();
+
+                            if ("resource".equals(policyResult.getPolicy().getType())) {
+                                policyResult.setStatus(Effect.PERMIT);
+                            }
+
+                            if (grantedScope != null) {
+                                grantedScopes.add(grantedScope);
+
+                                for (Scope policyScope : policyResult.getPolicy().getScopes()) {
+                                    if (policyScope.equals(grantedScope)) {
+                                        policyResult.setStatus(Effect.PERMIT);
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    permission.getScopes().clear();
+                    permission.getScopes().addAll(grantedScopes);
+                }
+            }
+        }
+
+        super.onDeny(result);
+    }
+
+    @Override
+    public void onComplete() {
+        super.onComplete();
+
+        if (request.isSubmitRequest()) {
+            StoreFactory storeFactory = authorization.getStoreFactory();
+            ResourceStore resourceStore = storeFactory.getResourceStore();
+
+            if (ticket.getResources() != null) {
+                for (PermissionTicketToken.ResourcePermission permission : ticket.getResources()) {
+                    Resource resource = resourceStore.findById(permission.getResourceId(), resourceServer.getId());
+
+                    if (resource == null) {
+                        resource = resourceStore.findByName(permission.getResourceId(), resourceServer.getId());
+                    }
+
+                    if (!resource.isOwnerManagedAccess() || resource.getOwner().equals(identity.getId()) || resource.getOwner().equals(resourceServer.getId())) {
+                        continue;
+                    }
+
+                    Set<String> scopes = permission.getScopes();
+
+                    if (scopes.isEmpty()) {
+                        scopes = resource.getScopes().stream().map(Scope::getName).collect(Collectors.toSet());
+                    }
+
+                    if (scopes.isEmpty()) {
+                        Map<String, String> filters = new HashMap<>();
+
+                        filters.put(PermissionTicket.RESOURCE, resource.getId());
+                        filters.put(PermissionTicket.REQUESTER, identity.getId());
+                        filters.put(PermissionTicket.SCOPE_IS_NULL, Boolean.TRUE.toString());
+
+                        List<PermissionTicket> permissions = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer().getId(), -1, -1);
+
+                        if (permissions.isEmpty()) {
+                            authorization.getStoreFactory().getPermissionTicketStore().create(resource.getId(), null, identity.getId(), resource.getResourceServer());
+                        }
+                    } else {
+                        ScopeStore scopeStore = authorization.getStoreFactory().getScopeStore();
+
+                        for (String scopeId : scopes) {
+                            Scope scope = scopeStore.findByName(scopeId, resourceServer.getId());
+
+                            if (scope == null) {
+                                scope = scopeStore.findById(scopeId, resourceServer.getId());
+                            }
+
+                            Map<String, String> filters = new HashMap<>();
+
+                            filters.put(PermissionTicket.RESOURCE, resource.getId());
+                            filters.put(PermissionTicket.REQUESTER, identity.getId());
+                            filters.put(PermissionTicket.SCOPE, scope.getId());
+
+                            List<PermissionTicket> permissions = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer().getId(), -1, -1);
+
+                            if (permissions.isEmpty()) {
+                                authorization.getStoreFactory().getPermissionTicketStore().create(resource.getId(), scope.getId(), identity.getId(), resource.getResourceServer());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void onComplete(List<Result> results) {
+        this.results = results;
+    }
+
+    public List<Result> results() {
+        return results;
+    }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/PermissionTicketStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/PermissionTicketStore.java
new file mode 100644
index 0000000..654d68b
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/PermissionTicketStore.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2017 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.authorization.store;
+
+
+import java.util.List;
+import java.util.Map;
+
+import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.authorization.model.ResourceServer;
+
+/**
+ * A {@link PermissionTicketStore} is responsible to manage the persistence of {@link org.keycloak.authorization.model.PermissionTicket} instances.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface PermissionTicketStore {
+
+    /**
+     * Creates a new {@link PermissionTicket} instance.
+     *
+     * @param permission the policy representation
+     * @param resourceServer the resource server to which this policy belongs
+     * @return a new instance of {@link PermissionTicket}
+     */
+    PermissionTicket create(String resourceId, String scopeId, String requester, ResourceServer resourceServer);
+
+    /**
+     * Deletes a permission from the underlying persistence mechanism.
+     *
+     * @param id the id of the policy to delete
+     */
+    void delete(String id);
+
+    /**
+     * Returns a {@link PermissionTicket} with the given <code>id</code>
+     *
+     * @param id the identifier of the permission
+     * @param resourceServerId the resource server id
+     * @return a permission with the given identifier.
+     */
+    PermissionTicket findById(String id, String resourceServerId);
+
+    /**
+     * Returns a list of {@link PermissionTicket} associated with a {@link ResourceServer} with the given <code>resourceServerId</code>.
+     *
+     * @param resourceServerId the identifier of a resource server
+     * @return a list of permissions belonging to the given resource server
+     */
+    List<PermissionTicket> findByResourceServer(String resourceServerId);
+
+    /**
+     * Returns a list of {@link PermissionTicket} associated with the given <code>owner</code>.
+     *
+     * @param owner the identifier of a resource server
+     * @return a list of permissions belonging to the given owner
+     */
+    List<PermissionTicket> findByOwner(String owner, String resourceServerId);
+
+    /**
+     * Returns a list of {@link PermissionTicket} associated with a {@link org.keycloak.authorization.core.model.Resource} with the given <code>resourceId</code>.
+     *
+     * @param resourceId the identifier of a resource
+     * @param resourceServerId the resource server id
+     * @return a list of permissions associated with the given resource
+     */
+    List<PermissionTicket> findByResource(String resourceId, String resourceServerId);
+
+    /**
+     * Returns a list of {@link PermissionTicket} associated with a {@link org.keycloak.authorization.core.model.Scope} with the given <code>scopeId</code>.
+     *
+     * @param scopeId the id of the scopes
+     * @param resourceServerId the resource server id
+     * @return a list of permissions associated with the given scopes
+     */
+    List<PermissionTicket> findByScope(String scopeId, String resourceServerId);
+
+    List<PermissionTicket> find(Map<String, String> attributes, String resourceServerId, int firstResult, int maxResult);
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/StoreFactory.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/StoreFactory.java
index 4f50c11..a1123a9 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/StoreFactory.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/StoreFactory.java
@@ -58,4 +58,10 @@ public interface StoreFactory extends Provider {
      */
     PolicyStore getPolicyStore();
 
+    /**
+     * Returns a {@link PermissionTicketStore}.
+     *
+     * @return the permission ticket store
+     */
+    PermissionTicketStore getPermissionTicketStore();
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/events/EventType.java b/server-spi-private/src/main/java/org/keycloak/events/EventType.java
index b48e243..29b38bc 100755
--- a/server-spi-private/src/main/java/org/keycloak/events/EventType.java
+++ b/server-spi-private/src/main/java/org/keycloak/events/EventType.java
@@ -125,7 +125,10 @@ public enum EventType {
     CLIENT_INITIATED_ACCOUNT_LINKING(true),
     CLIENT_INITIATED_ACCOUNT_LINKING_ERROR(true),
     TOKEN_EXCHANGE(true),
-    TOKEN_EXCHANGE_ERROR(true);
+    TOKEN_EXCHANGE_ERROR(true),
+
+    PERMISSION_TOKEN(true),
+    PERMISSION_TOKEN_ERROR(false);
 
     private boolean saveByDefault;
 
diff --git a/server-spi-private/src/main/java/org/keycloak/forms/account/AccountPages.java b/server-spi-private/src/main/java/org/keycloak/forms/account/AccountPages.java
index f9b4835..6c01f79 100755
--- a/server-spi-private/src/main/java/org/keycloak/forms/account/AccountPages.java
+++ b/server-spi-private/src/main/java/org/keycloak/forms/account/AccountPages.java
@@ -22,6 +22,6 @@ package org.keycloak.forms.account;
  */
 public enum AccountPages {
 
-    ACCOUNT, PASSWORD, TOTP, FEDERATED_IDENTITY, LOG, SESSIONS, APPLICATIONS;
+    ACCOUNT, PASSWORD, TOTP, FEDERATED_IDENTITY, LOG, SESSIONS, APPLICATIONS, RESOURCES, RESOURCE_DETAIL;
 
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/forms/account/AccountProvider.java b/server-spi-private/src/main/java/org/keycloak/forms/account/AccountProvider.java
index a61c0a9..1568f18 100755
--- a/server-spi-private/src/main/java/org/keycloak/forms/account/AccountProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/forms/account/AccountProvider.java
@@ -65,7 +65,7 @@ public interface AccountProvider extends Provider {
 
     AccountProvider setStateChecker(String stateChecker);
 
-    AccountProvider setFeatures(boolean social, boolean events, boolean passwordUpdateSupported);
+    AccountProvider setFeatures(boolean social, boolean events, boolean passwordUpdateSupported, boolean authorizationSupported);
 
     AccountProvider setAttribute(String key, String value);
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 2e7d3ef..56c95e5 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -18,6 +18,7 @@
 package org.keycloak.models.utils;
 
 import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.PermissionTicket;
 import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
@@ -234,6 +235,7 @@ public class ModelToRepresentation {
         rep.setQuickLoginCheckMilliSeconds(realm.getQuickLoginCheckMilliSeconds());
         rep.setMaxDeltaTimeSeconds(realm.getMaxDeltaTimeSeconds());
         rep.setFailureFactor(realm.getFailureFactor());
+        rep.setUserManagedAccessAllowed(realm.isUserManagedAccessAllowed());
 
         rep.setEventsEnabled(realm.isEventsEnabled());
         if (realm.getEventsExpiration() != 0) {
@@ -741,6 +743,7 @@ public class ModelToRepresentation {
 
         scope.setId(model.getId());
         scope.setName(model.getName());
+        scope.setDisplayName(model.getDisplayName());
         scope.setIconUri(model.getIconUri());
 
         return scope;
@@ -800,8 +803,10 @@ public class ModelToRepresentation {
         resource.setId(model.getId());
         resource.setType(model.getType());
         resource.setName(model.getName());
+        resource.setDisplayName(model.getDisplayName());
         resource.setUri(model.getUri());
         resource.setIconUri(model.getIconUri());
+        resource.setOwnerManagedAccess(model.isOwnerManagedAccess());
 
         ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation();
 
@@ -858,4 +863,35 @@ public class ModelToRepresentation {
 
         return resource;
     }
+
+    public static PermissionTicketRepresentation toRepresentation(PermissionTicket ticket) {
+        return toRepresentation(ticket, false);
+    }
+
+    public static PermissionTicketRepresentation toRepresentation(PermissionTicket ticket, boolean returnNames) {
+        PermissionTicketRepresentation representation = new PermissionTicketRepresentation();
+
+        representation.setId(ticket.getId());
+        representation.setGranted(ticket.isGranted());
+        representation.setOwner(ticket.getOwner());
+
+        Resource resource = ticket.getResource();
+
+        representation.setResource(resource.getId());
+
+        if (returnNames) {
+            representation.setResourceName(resource.getName());
+        }
+
+        Scope scope = ticket.getScope();
+
+        if (scope != null) {
+            representation.setScope(scope.getId());
+            if (returnNames) {
+                representation.setScopeName(scope.getName());
+            }
+        }
+
+        return representation;
+    }
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 98ccf38..c738b81 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -33,11 +33,13 @@ import java.util.stream.Collectors;
 import org.jboss.logging.Logger;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.AuthorizationProviderFactory;
+import org.keycloak.authorization.model.PermissionTicket;
 import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
 import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
+import org.keycloak.authorization.store.PermissionTicketStore;
 import org.keycloak.authorization.store.PolicyStore;
 import org.keycloak.authorization.store.ResourceServerStore;
 import org.keycloak.authorization.store.ResourceStore;
@@ -109,6 +111,7 @@ import org.keycloak.representations.idm.UserFederationMapperRepresentation;
 import org.keycloak.representations.idm.UserFederationProviderRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
 import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
@@ -145,6 +148,7 @@ public class RepresentationToModel {
         if (rep.getDisplayName() != null) newRealm.setDisplayName(rep.getDisplayName());
         if (rep.getDisplayNameHtml() != null) newRealm.setDisplayNameHtml(rep.getDisplayNameHtml());
         if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled());
+        if (rep.isUserManagedAccessAllowed() != null) newRealm.setUserManagedAccessAllowed(rep.isUserManagedAccessAllowed());
         if (rep.isBruteForceProtected() != null) newRealm.setBruteForceProtected(rep.isBruteForceProtected());
         if (rep.isPermanentLockout() != null) newRealm.setPermanentLockout(rep.isPermanentLockout());
         if (rep.getMaxFailureWaitSeconds() != null) newRealm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds());
@@ -817,6 +821,7 @@ public class RepresentationToModel {
         if (rep.getDisplayName() != null) realm.setDisplayName(rep.getDisplayName());
         if (rep.getDisplayNameHtml() != null) realm.setDisplayNameHtml(rep.getDisplayNameHtml());
         if (rep.isEnabled() != null) realm.setEnabled(rep.isEnabled());
+        if (rep.isUserManagedAccessAllowed() != null) realm.setUserManagedAccessAllowed(rep.isUserManagedAccessAllowed());
         if (rep.isBruteForceProtected() != null) realm.setBruteForceProtected(rep.isBruteForceProtected());
         if (rep.isPermanentLockout() != null) realm.setPermanentLockout(rep.isPermanentLockout());
         if (rep.getMaxFailureWaitSeconds() != null) realm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds());
@@ -2308,9 +2313,11 @@ public class RepresentationToModel {
 
         if (existing != null) {
             existing.setName(resource.getName());
+            existing.setDisplayName(resource.getDisplayName());
             existing.setType(resource.getType());
             existing.setUri(resource.getUri());
             existing.setIconUri(resource.getIconUri());
+            existing.setOwnerManagedAccess(Boolean.TRUE.equals(resource.getOwnerManagedAccess()));
             existing.updateScopes(resource.getScopes().stream()
                     .map((ScopeRepresentation scope) -> toModel(scope, resourceServer, authorization))
                     .collect(Collectors.toSet()));
@@ -2356,9 +2363,11 @@ public class RepresentationToModel {
 
         Resource model = resourceStore.create(resource.getName(), resourceServer, ownerId);
 
+        model.setDisplayName(resource.getDisplayName());
         model.setType(resource.getType());
         model.setUri(resource.getUri());
         model.setIconUri(resource.getIconUri());
+        model.setOwnerManagedAccess(Boolean.TRUE.equals(resource.getOwnerManagedAccess()));
 
         Set<ScopeRepresentation> scopes = resource.getScopes();
 
@@ -2384,17 +2393,35 @@ public class RepresentationToModel {
 
         if (existing != null) {
             existing.setName(scope.getName());
+            existing.setDisplayName(scope.getDisplayName());
             existing.setIconUri(scope.getIconUri());
             return existing;
         }
 
         Scope model = scopeStore.create(scope.getName(), resourceServer);
+
+        model.setDisplayName(scope.getDisplayName());
         model.setIconUri(scope.getIconUri());
+
         scope.setId(model.getId());
 
         return model;
     }
 
+    public static PermissionTicket toModel(PermissionTicketRepresentation representation, String resourceServerId, AuthorizationProvider authorization) {
+        PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
+        PermissionTicket ticket = ticketStore.findById(representation.getId(), resourceServerId);
+        boolean granted = representation.isGranted();
+
+        if (granted && !ticket.isGranted()) {
+            ticket.setGrantedTimestamp(System.currentTimeMillis());
+        } else if (!granted) {
+            ticket.setGrantedTimestamp(null);
+        }
+
+        return ticket;
+    }
+
     public static void importFederatedUser(KeycloakSession session, RealmModel newRealm, UserRepresentation userRep) {
         UserFederatedStorageProvider federatedStorage = session.userFederatedStorage();
         if (userRep.getAttributes() != null) {
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
index d1acce0..3ba29dd 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
@@ -20,6 +20,7 @@ package org.keycloak.authorization.admin;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -81,9 +82,6 @@ public class PolicyEvaluationService {
 
     private final AuthorizationProvider authorization;
     private final AdminPermissionEvaluator auth;
-    @Context
-    private HttpRequest httpRequest;
-
     private final ResourceServer resourceServer;
 
     PolicyEvaluationService(ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth) {
diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponseBuilder.java b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponseBuilder.java
index 17002ab..b5023b5 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponseBuilder.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponseBuilder.java
@@ -24,9 +24,11 @@ import org.keycloak.authorization.model.Scope;
 import org.keycloak.authorization.policy.evaluation.Result;
 import org.keycloak.authorization.util.Permissions;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.authorization.DecisionEffect;
 import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
+import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse.PolicyResultRepresentation;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
@@ -108,7 +110,13 @@ public class PolicyEvaluationResponseBuilder {
             List<PolicyEvaluationResponse.PolicyResultRepresentation> policies = new ArrayList<>();
 
             for (Result.PolicyResult policy : result.getResults()) {
-                policies.add(toRepresentation(policy, authorization));
+                PolicyResultRepresentation policyRep = toRepresentation(policy, authorization);
+
+                if ("resource".equals(policy.getPolicy().getType())) {
+                    policyRep.getPolicy().setScopes(result.getPermission().getResource().getScopes().stream().map(Scope::getName).collect(Collectors.toSet()));
+                }
+
+                policies.add(policyRep);
             }
 
             rep.setPolicies(policies);
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
index f4d685c..ff294c9 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
@@ -17,6 +17,34 @@
  */
 package org.keycloak.authorization.admin;
 
+import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
+import static org.keycloak.models.utils.RepresentationToModel.toModel;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
+
 import org.jboss.resteasy.annotations.cache.NoCache;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.model.Policy;
@@ -40,32 +68,6 @@ import org.keycloak.services.ErrorResponse;
 import org.keycloak.services.resources.admin.AdminEventBuilder;
 import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriInfo;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
-import static org.keycloak.models.utils.RepresentationToModel.toModel;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -88,12 +90,32 @@ public class ResourceSetService {
     @Consumes("application/json")
     @Produces("application/json")
     public Response create(@Context UriInfo uriInfo, ResourceRepresentation resource) {
-        Response response = create(resource);
+        return create(uriInfo, resource, (Function<Resource, ResourceRepresentation>) resource1 -> {
+            ResourceRepresentation representation = new ResourceRepresentation();
+
+            representation.setId(resource1.getId());
+
+            return representation;
+        });
+    }
+
+    public Response create(@Context UriInfo uriInfo, ResourceRepresentation resource, Function<Resource, ?> toRepresentation) {
+        Response response = create(resource, toRepresentation);
         audit(uriInfo, resource, resource.getId(), OperationType.CREATE);
         return response;
     }
 
     public Response create(ResourceRepresentation resource) {
+        return create(resource, (Function<Resource, ResourceRepresentation>) resource1 -> {
+            ResourceRepresentation representation = new ResourceRepresentation();
+
+            representation.setId(resource1.getId());
+
+            return representation;
+        });
+    }
+
+    public Response create(ResourceRepresentation resource, Function<Resource, ?> toRepresentation) {
         requireManage();
         StoreFactory storeFactory = this.authorization.getStoreFactory();
         Resource existingResource = storeFactory.getResourceStore().findByName(resource.getName(), this.resourceServer.getId());
@@ -114,11 +136,7 @@ public class ResourceSetService {
             return ErrorResponse.exists("Resource with name [" + resource.getName() + "] already exists.");
         }
 
-        ResourceRepresentation representation = new ResourceRepresentation();
-
-        representation.setId(toModel(resource, this.resourceServer, authorization).getId());
-
-        return Response.status(Status.CREATED).entity(representation).build();
+        return Response.status(Status.CREATED).entity(toRepresentation.apply(toModel(resource, this.resourceServer, authorization))).build();
     }
 
     @Path("{id}")
@@ -179,6 +197,10 @@ public class ResourceSetService {
     @NoCache
     @Produces("application/json")
     public Response findById(@PathParam("id") String id) {
+        return findById(id, (Function<Resource, ResourceRepresentation>) resource -> toRepresentation(resource, resourceServer, authorization, true));
+    }
+
+    public Response findById(@PathParam("id") String id, Function<Resource, ?> toRepresentation) {
         requireView();
         StoreFactory storeFactory = authorization.getStoreFactory();
         Resource model = storeFactory.getResourceStore().findById(id, resourceServer.getId());
@@ -187,7 +209,7 @@ public class ResourceSetService {
             return Response.status(Status.NOT_FOUND).build();
         }
 
-        return Response.ok(toRepresentation(model, this.resourceServer, authorization, true)).build();
+        return Response.ok(toRepresentation.apply(model)).build();
     }
 
     @Path("{id}/scopes")
@@ -295,14 +317,27 @@ public class ResourceSetService {
     @NoCache
     @Produces("application/json")
     public Response find(@QueryParam("_id") String id,
-                            @QueryParam("name") String name,
-                            @QueryParam("uri") String uri,
-                            @QueryParam("owner") String owner,
-                            @QueryParam("type") String type,
-                            @QueryParam("scope") String scope,
-                            @QueryParam("deep") Boolean deep,
-                            @QueryParam("first") Integer firstResult,
-                            @QueryParam("max") Integer maxResult) {
+                         @QueryParam("name") String name,
+                         @QueryParam("uri") String uri,
+                         @QueryParam("owner") String owner,
+                         @QueryParam("type") String type,
+                         @QueryParam("scope") String scope,
+                         @QueryParam("deep") Boolean deep,
+                         @QueryParam("first") Integer firstResult,
+                         @QueryParam("max") Integer maxResult) {
+        return find(id, name, uri, owner, type, scope, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, ResourceRepresentation>) (resource, deep1) -> toRepresentation(resource, resourceServer, authorization, deep1));
+    }
+
+    public Response find(@QueryParam("_id") String id,
+                         @QueryParam("name") String name,
+                         @QueryParam("uri") String uri,
+                         @QueryParam("owner") String owner,
+                         @QueryParam("type") String type,
+                         @QueryParam("scope") String scope,
+                         @QueryParam("deep") Boolean deep,
+                         @QueryParam("first") Integer firstResult,
+                         @QueryParam("max") Integer maxResult,
+                         BiFunction<Resource, Boolean, ?> toRepresentation) {
         requireView();
 
         StoreFactory storeFactory = authorization.getStoreFactory();
@@ -363,7 +398,7 @@ public class ResourceSetService {
         Boolean finalDeep = deep;
         return Response.ok(
                 storeFactory.getResourceStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
-                        .map(resource -> toRepresentation(resource, resourceServer, authorization, finalDeep))
+                        .map(resource -> toRepresentation.apply(resource, finalDeep))
                         .collect(Collectors.toList()))
                 .build();
     }
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
index 1ab3546..294951c 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
@@ -120,7 +120,7 @@ public class ScopeService {
         List<Resource> resources = storeFactory.getResourceStore().findByScope(Arrays.asList(id), resourceServer.getId());
 
         if (!resources.isEmpty()) {
-            return ErrorResponse.exists("Scopes can not be removed while associated with resources.");
+            return ErrorResponse.error("Scopes can not be removed while associated with resources.", Status.BAD_REQUEST);
         }
 
         Scope scope = storeFactory.getScopeStore().findById(id, resourceServer.getId());
diff --git a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
index c86dd45..a8075a0 100644
--- a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
+++ b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
@@ -16,185 +16,335 @@
  */
 package org.keycloak.authorization.authorization;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.spi.HttpRequest;
-import org.keycloak.OAuth2Constants;
 import org.keycloak.OAuthErrorException;
 import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.authorization.representation.AuthorizationRequest;
-import org.keycloak.authorization.authorization.representation.AuthorizationResponse;
 import org.keycloak.authorization.common.KeycloakEvaluationContext;
 import org.keycloak.authorization.common.KeycloakIdentity;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
 import org.keycloak.authorization.permission.ResourcePermission;
+import org.keycloak.authorization.policy.evaluation.PermissionTicketAwareDecisionResultCollector;
 import org.keycloak.authorization.policy.evaluation.Result;
-import org.keycloak.authorization.protection.permission.PermissionTicket;
+import org.keycloak.authorization.store.ResourceServerStore;
 import org.keycloak.authorization.store.ResourceStore;
 import org.keycloak.authorization.store.ScopeStore;
 import org.keycloak.authorization.store.StoreFactory;
 import org.keycloak.authorization.util.Permissions;
 import org.keycloak.authorization.util.Tokens;
+import org.keycloak.events.EventBuilder;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.JWSInputException;
+import org.keycloak.models.AuthenticatedClientSessionModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.protocol.oidc.TokenManager.AccessTokenResponseBuilder;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.AccessToken.Authorization;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.IDToken;
+import org.keycloak.representations.RefreshToken;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest.Metadata;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.Permission;
-import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.representations.idm.authorization.PermissionTicketToken;
+import org.keycloak.services.CorsErrorResponseException;
 import org.keycloak.services.ErrorResponseException;
 import org.keycloak.services.resources.Cors;
 
-import javax.ws.rs.Consumes;
-import javax.ws.rs.OPTIONS;
-import javax.ws.rs.POST;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class AuthorizationTokenService {
-    protected static final Logger logger = Logger.getLogger(AuthorizationTokenService.class);
-
-    private final AuthorizationProvider authorization;
 
-    @Context
-    private HttpRequest httpRequest;
+    private static final Logger logger = Logger.getLogger(AuthorizationTokenService.class);
+    private static Map<String, BiFunction<AuthorizationRequest, AuthorizationProvider, KeycloakEvaluationContext>> SUPPORTED_CLAIM_TOKEN_FORMATS;
 
-    @Context
-    private KeycloakSession session;
+    static {
+        SUPPORTED_CLAIM_TOKEN_FORMATS = new HashMap<>();
+        SUPPORTED_CLAIM_TOKEN_FORMATS.put("urn:ietf:params:oauth:token-type:jwt", (authorizationRequest, authorization) -> {
+            String claimToken = authorizationRequest.getClaimToken();
 
-    public AuthorizationTokenService(AuthorizationProvider authorization) {
-        this.authorization = authorization;
-    }
+            if (claimToken == null) {
+                claimToken = authorizationRequest.getAccessToken();
+            }
 
-    @OPTIONS
-    public Response authorizepPreFlight() {
-        return Cors.add(this.httpRequest, Response.ok()).auth().preflight().build();
+            return new KeycloakEvaluationContext(new KeycloakIdentity(authorization.getKeycloakSession(), Tokens.getAccessToken(claimToken, authorization.getKeycloakSession())), authorization.getKeycloakSession());
+        });
+        SUPPORTED_CLAIM_TOKEN_FORMATS.put("http://openid.net/specs/openid-connect-core-1_0.html#IDToken", (authorizationRequest, authorization) -> {
+            try {
+                KeycloakSession keycloakSession = authorization.getKeycloakSession();
+                IDToken idToken = new TokenManager().verifyIDTokenSignature(keycloakSession, authorization.getRealm(), authorizationRequest.getClaimToken());
+                return new KeycloakEvaluationContext(new KeycloakIdentity(keycloakSession, idToken), keycloakSession);
+            } catch (OAuthErrorException cause) {
+                throw new RuntimeException("Failed to verify ID token", cause);
+            }
+        });
     }
 
-    @POST
-    @Consumes("application/json")
-    @Produces("application/json")
-    public Response authorize(AuthorizationRequest authorizationRequest) {
-        KeycloakEvaluationContext evaluationContext = new KeycloakEvaluationContext(this.authorization.getKeycloakSession());
-        KeycloakIdentity identity = (KeycloakIdentity) evaluationContext.getIdentity();
+    private final TokenManager tokenManager;
+    private final EventBuilder event;
+    private final HttpRequest httpRequest;
+    private final AuthorizationProvider authorization;
+    private final Cors cors;
 
-        if (!identity.hasRealmRole("uma_authorization")) {
-            throw new ErrorResponseException(OAuthErrorException.INVALID_SCOPE, "Requires uma_authorization scope.", Status.FORBIDDEN);
-        }
+    public AuthorizationTokenService(AuthorizationProvider authorization, TokenManager tokenManager, EventBuilder event, HttpRequest httpRequest, Cors cors) {
+        this.tokenManager = tokenManager;
+        this.event = event;
+        this.httpRequest = httpRequest;
+        this.authorization = authorization;
+        this.cors = cors;
+    }
 
-        if (authorizationRequest == null) {
-            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Invalid authorization request.", Status.BAD_REQUEST);
+    public Response authorize(AuthorizationRequest request) {
+        if (request == null) {
+            throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Invalid authorization request.", Status.BAD_REQUEST);
         }
 
         try {
-            PermissionTicket ticket = verifyPermissionTicket(authorizationRequest);
-            ResourceServer resourceServer = authorization.getStoreFactory().getResourceServerStore().findById(ticket.getResourceServerId());
-
-            if (resourceServer == null) {
-                throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Client does not support permissions", Status.FORBIDDEN);
+            PermissionTicketToken ticket = getPermissionTicket(request);
+            ResourceServer resourceServer = getResourceServer(ticket);
+            KeycloakEvaluationContext evaluationContext = createEvaluationContext(request);
+            KeycloakIdentity identity = KeycloakIdentity.class.cast(evaluationContext.getIdentity());
+            List<Result> evaluation;
+
+            if (ticket.getResources().isEmpty() && request.getRpt() == null) {
+                evaluation = evaluateAllPermissions(resourceServer, evaluationContext, identity);
+            } else if(!request.getPermissions().getResources().isEmpty()) {
+                evaluation = evaluatePermissions(request, ticket, resourceServer, evaluationContext, identity);
+            } else {
+                evaluation = evaluateUserManagedPermissions(request, ticket, resourceServer, evaluationContext, identity);
             }
 
-            List<Result> result = authorization.evaluators().from(createPermissions(ticket, authorizationRequest, authorization), evaluationContext).evaluate();
+            List<Permission> permissions = Permissions.permits(evaluation, request.getMetadata(), authorization, resourceServer);
+
+            if (permissions.isEmpty()) {
+                if (request.isSubmitRequest()) {
+                    throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "request_submitted", Status.FORBIDDEN);
+                } else {
+                    throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "not_authorized", Status.FORBIDDEN);
+                }
+            }
 
-            List<Permission> entitlements = Permissions.permits(result, authorizationRequest.getMetadata(), authorization, resourceServer);
+            ClientModel targetClient = this.authorization.getRealm().getClientById(resourceServer.getId());
+            AuthorizationResponse response = new AuthorizationResponse(createRequestingPartyToken(identity, permissions, targetClient), request.getRpt() != null);
 
-            if (!entitlements.isEmpty()) {
-                AuthorizationResponse response = new AuthorizationResponse(createRequestingPartyToken(entitlements, identity.getAccessToken(), resourceServer));
-                return Cors.add(httpRequest, Response.status(Status.CREATED).entity(response)).allowedOrigins(identity.getAccessToken())
-                        .allowedMethods("POST")
-                        .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
+            return Cors.add(httpRequest, Response.status(Status.OK).type(MediaType.APPLICATION_JSON_TYPE).entity(response))
+                    .allowedOrigins(getKeycloakSession().getContext().getUri(), targetClient)
+                    .allowedMethods(HttpMethod.POST)
+                    .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
+        } catch (ErrorResponseException | CorsErrorResponseException cause) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Error while evaluating permissions", cause);
             }
+            throw cause;
         } catch (Exception cause) {
-            logger.error("Failed to evaluate permissions", cause);
-            throw new ErrorResponseException(OAuthErrorException.SERVER_ERROR, "Error while evaluating permissions.", Status.INTERNAL_SERVER_ERROR);
+            logger.error("Unexpected error while evaluating permissions", cause);
+            throw new CorsErrorResponseException(cors, OAuthErrorException.SERVER_ERROR, "Unexpected error while evaluating permissions", Status.INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    private List<Result> evaluatePermissions(AuthorizationRequest authorizationRequest, PermissionTicketToken ticket, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
+        return authorization.evaluators()
+                .from(createPermissions(ticket, authorizationRequest, resourceServer, authorization), evaluationContext)
+                .evaluate();
+    }
+
+    private List<Result> evaluateUserManagedPermissions(AuthorizationRequest request, PermissionTicketToken ticket, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
+        return authorization.evaluators()
+                .from(createPermissions(ticket, request, resourceServer, authorization), evaluationContext)
+                .evaluate(new PermissionTicketAwareDecisionResultCollector(request, ticket, identity, resourceServer, authorization)).results();
+    }
+
+    private List<Result> evaluateAllPermissions(ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
+        return authorization.evaluators()
+                .from(Permissions.all(resourceServer, identity, authorization), evaluationContext)
+                .evaluate();
+    }
+
+    private AccessTokenResponse createRequestingPartyToken(KeycloakIdentity identity, List<Permission> entitlements, ClientModel targetClient) {
+        KeycloakSession keycloakSession = getKeycloakSession();
+        AccessToken accessToken = identity.getAccessToken();
+        UserSessionModel userSessionModel = keycloakSession.sessions().getUserSession(getRealm(), accessToken.getSessionState());
+        ClientModel client = getRealm().getClientByClientId(accessToken.getIssuedFor());
+        AuthenticatedClientSessionModel clientSession = userSessionModel.getAuthenticatedClientSessionByClient(client.getId());
+        AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(getRealm(), clientSession.getClient(), event, keycloakSession, userSessionModel, clientSession)
+                .generateAccessToken()
+                .generateRefreshToken();
+
+        AccessToken rpt = responseBuilder.getAccessToken();
+
+        rpt.issuedFor(client.getClientId());
+
+        Authorization authorization = new Authorization();
+
+        authorization.setPermissions(entitlements);
+
+        rpt.setAuthorization(authorization);
+
+        RefreshToken refreshToken = responseBuilder.getRefreshToken();
+
+        refreshToken.issuedFor(client.getClientId());
+        refreshToken.setAuthorization(authorization);
+
+        if (!rpt.hasAudience(targetClient.getClientId())) {
+            rpt.audience(targetClient.getClientId());
         }
 
-        HashMap<Object, Object> error = new HashMap<>();
+        return responseBuilder.build();
+    }
 
-        error.put(OAuth2Constants.ERROR, "not_authorized");
+    private PermissionTicketToken getPermissionTicket(AuthorizationRequest request) {
+        // if there is a ticket is because it is a UMA flow and the ticket was sent by the client after obtaining it from the target resource server
+        if (request.getTicket() != null) {
+            return verifyPermissionTicket(request);
+        }
 
-        return Cors.add(httpRequest, Response.status(Status.FORBIDDEN)
-                .entity(error))
-                .allowedOrigins(identity.getAccessToken())
-                .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
+        // if there is no ticket, we use the permissions the client is asking for.
+        // This is a Keycloak extension to UMA flow where clients are capable of obtaining a RPT without a ticket
+        PermissionTicketToken permissions = request.getPermissions();
+
+        // an audience must be set by the client when doing this method of obtaining RPT, that is how we know the target resource server
+        permissions.audience(request.getAudience());
+
+        return permissions;
     }
 
-    private List<ResourcePermission> createPermissions(PermissionTicket ticket, AuthorizationRequest request, AuthorizationProvider authorization) {
+    private ResourceServer getResourceServer(PermissionTicketToken ticket) {
         StoreFactory storeFactory = authorization.getStoreFactory();
-        ResourceServer resourceServer = authorization.getStoreFactory().getResourceServerStore().findById(ticket.getResourceServerId());
+        ResourceServerStore resourceServerStore = storeFactory.getResourceServerStore();
+        String[] audience = ticket.getAudience();
+
+        if (audience == null || audience.length == 0) {
+            throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "You must provide the audience", Status.BAD_REQUEST);
+        }
+
+        ClientModel clientModel = getRealm().getClientByClientId(audience[0]);
+
+        if (clientModel == null) {
+            throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Unknown resource server id.", Status.BAD_REQUEST);
+        }
+
+        ResourceServer resourceServer = resourceServerStore.findById(clientModel.getId());
 
         if (resourceServer == null) {
-            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Client does not support permissions", Status.FORBIDDEN);
+            throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Client does not support permissions", Status.BAD_REQUEST);
         }
 
-        Map<String, Set<String>> permissionsToEvaluate = new HashMap<>();
+        return resourceServer;
+    }
 
-        ticket.getResources().forEach(requestedResource -> {
-            Resource resource;
+    private KeycloakEvaluationContext createEvaluationContext(AuthorizationRequest authorizationRequest) {
+        String claimTokenFormat = authorizationRequest.getClaimTokenFormat();
 
-            if (requestedResource.getId() != null) {
-                resource = storeFactory.getResourceStore().findById(requestedResource.getId(), ticket.getResourceServerId());
-            } else {
-                resource = storeFactory.getResourceStore().findByName(requestedResource.getName(), ticket.getResourceServerId());
+        if (claimTokenFormat == null) {
+            claimTokenFormat = "urn:ietf:params:oauth:token-type:jwt";
+        }
+
+        BiFunction<AuthorizationRequest, AuthorizationProvider, KeycloakEvaluationContext> evaluationContextProvider = SUPPORTED_CLAIM_TOKEN_FORMATS.get(claimTokenFormat);
+
+        if (evaluationContextProvider == null) {
+            throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Claim token format [" + claimTokenFormat + "] not supported", Status.BAD_REQUEST);
+        }
+
+        return evaluationContextProvider.apply(authorizationRequest, authorization);
+    }
+
+    private List<ResourcePermission> createPermissions(PermissionTicketToken ticket, AuthorizationRequest request, ResourceServer resourceServer, AuthorizationProvider authorization) {
+        StoreFactory storeFactory = authorization.getStoreFactory();
+        Map<String, Set<String>> permissionsToEvaluate = new LinkedHashMap<>();
+        ResourceStore resourceStore = storeFactory.getResourceStore();
+        Metadata metadata = request.getMetadata();
+        Integer limit = metadata != null ? metadata.getLimit() : null;
+
+        for (PermissionTicketToken.ResourcePermission requestedResource : ticket.getResources()) {
+            if (limit != null && limit <= 0) {
+                break;
             }
 
-            if (resource == null && (requestedResource.getScopes() == null || requestedResource.getScopes().isEmpty())) {
-                throw new ErrorResponseException("invalid_resource", "Resource with id [" + requestedResource.getId() + "] or name [" + requestedResource.getName() + "] does not exist.", Status.FORBIDDEN);
+            Set<String> requestedScopes = requestedResource.getScopes();
+
+            if (requestedResource.getScopes() == null) {
+                requestedScopes = new HashSet<>();
             }
 
-            Set<ScopeRepresentation> requestedScopes = requestedResource.getScopes();
-            Set<String> collect = requestedScopes.stream().map(ScopeRepresentation::getName).collect(Collectors.toSet());
+            Resource existingResource = null;
 
-            if (resource != null) {
-                permissionsToEvaluate.put(resource.getId(), collect);
-            } else {
-                ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
-                ScopeStore scopeStore = authorization.getStoreFactory().getScopeStore();
-                List<Resource> resources = new ArrayList<Resource>();
+            if (requestedResource.getResourceId() != null) {
+                existingResource = resourceStore.findById(requestedResource.getResourceId(), resourceServer.getId());
+
+                if (existingResource == null) {
+                    existingResource = resourceStore.findByName(requestedResource.getResourceId(), resourceServer.getId());
+                }
+            }
+
+            if (existingResource == null && (requestedScopes == null || requestedScopes.isEmpty())) {
+                throw new CorsErrorResponseException(cors, "invalid_resource", "Resource with id [" + requestedResource.getResourceId() + "] does not exist.", Status.FORBIDDEN);
+            }
+
+            String clientAdditionalScopes = request.getScope();
 
-                resources.addAll(resourceStore.findByScope(requestedScopes.stream().map(scopeRepresentation -> {
-                    Scope scope = scopeStore.findByName(scopeRepresentation.getName(), ticket.getResourceServerId());
+            if (clientAdditionalScopes != null) {
+                requestedScopes.addAll(Arrays.asList(clientAdditionalScopes.split(" ")));
+            }
+
+            if (existingResource != null) {
+                Set<String> scopes = permissionsToEvaluate.get(existingResource.getId());
 
-                    if (scope == null) {
-                        return null;
+                if (scopes == null) {
+                    scopes = new HashSet<>();
+                    permissionsToEvaluate.put(existingResource.getId(), scopes);
+                    if (limit != null) {
+                        limit--;
                     }
+                }
 
-                    return scope.getId();
-                }).filter(s -> s != null).collect(Collectors.toList()), ticket.getResourceServerId()));
+                scopes.addAll(requestedScopes);
+            } else {
+                List<Resource> resources = resourceStore.findByScope(new ArrayList<>(requestedScopes), ticket.getAudience()[0]);
 
-                for (Resource resource1 : resources) {
-                    permissionsToEvaluate.put(resource1.getId(), collect);
+                for (Resource resource : resources) {
+                    permissionsToEvaluate.put(resource.getId(), requestedScopes);
+                    if (limit != null) {
+                        limit--;
+                    }
                 }
 
-                permissionsToEvaluate.put("$KC_SCOPE_PERMISSION", collect);
+                permissionsToEvaluate.put("$KC_SCOPE_PERMISSION", requestedScopes);
             }
-        });
+        }
 
         String rpt = request.getRpt();
 
-        if (rpt != null && !"".equals(rpt)) {
-            if (!Tokens.verifySignature(session, getRealm(), rpt)) {
-                throw new ErrorResponseException("invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN);
+        if (rpt != null) {
+            if (!Tokens.verifySignature(getKeycloakSession(), getRealm(), rpt)) {
+                throw new CorsErrorResponseException(cors, "invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN);
             }
 
             AccessToken requestingPartyToken;
@@ -202,7 +352,7 @@ public class AuthorizationTokenService {
             try {
                 requestingPartyToken = new JWSInput(rpt).readJsonContent(AccessToken.class);
             } catch (JWSInputException e) {
-                throw new ErrorResponseException("invalid_rpt", "Invalid RPT", Status.FORBIDDEN);
+                throw new CorsErrorResponseException(cors, "invalid_rpt", "Invalid RPT", Status.FORBIDDEN);
             }
 
             if (requestingPartyToken.isActive()) {
@@ -212,8 +362,12 @@ public class AuthorizationTokenService {
                     List<Permission> permissions = authorizationData.getPermissions();
 
                     if (permissions != null) {
-                        permissions.forEach(permission -> {
-                            Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId(), ticket.getResourceServerId());
+                        for (Permission permission : permissions) {
+                            if (limit != null && limit <= 0) {
+                                break;
+                            }
+
+                            Resource resourcePermission = resourceStore.findById(permission.getResourceId(), ticket.getAudience()[0]);
 
                             if (resourcePermission != null) {
                                 Set<String> scopes = permissionsToEvaluate.get(resourcePermission.getId());
@@ -221,6 +375,9 @@ public class AuthorizationTokenService {
                                 if (scopes == null) {
                                     scopes = new HashSet<>();
                                     permissionsToEvaluate.put(resourcePermission.getId(), scopes);
+                                    if (limit != null) {
+                                        limit--;
+                                    }
                                 }
 
                                 Set<String> scopePermission = permission.getScopes();
@@ -229,63 +386,52 @@ public class AuthorizationTokenService {
                                     scopes.addAll(scopePermission);
                                 }
                             }
-                        });
+                        }
                     }
                 }
             }
         }
 
+        ScopeStore scopeStore = storeFactory.getScopeStore();
+
         return permissionsToEvaluate.entrySet().stream()
                 .flatMap((Function<Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> {
                     String key = entry.getKey();
-
                     if ("$KC_SCOPE_PERMISSION".equals(key)) {
-                        ScopeStore scopeStore = authorization.getStoreFactory().getScopeStore();
                         List<Scope> scopes = entry.getValue().stream().map(scopeName -> scopeStore.findByName(scopeName, resourceServer.getId())).filter(scope -> Objects.nonNull(scope)).collect(Collectors.toList());
                         return Arrays.asList(new ResourcePermission(null, scopes, resourceServer)).stream();
                     } else {
-                        Resource entryResource = storeFactory.getResourceStore().findById(key, resourceServer.getId());
+                        Resource entryResource = resourceStore.findById(key, resourceServer.getId());
                         return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
                     }
                 }).collect(Collectors.toList());
     }
 
-    private RealmModel getRealm() {
-        return this.authorization.getKeycloakSession().getContext().getRealm();
-    }
-
-    private String createRequestingPartyToken(List<Permission> permissions, AccessToken accessToken, ResourceServer resourceServer) {
-        AccessToken.Authorization authorization = new AccessToken.Authorization();
-
-        authorization.setPermissions(permissions);
-        accessToken.setAuthorization(authorization);
-
-        ClientModel clientModel = this.authorization.getRealm().getClientById(resourceServer.getId());
-
-        if (!accessToken.hasAudience(clientModel.getClientId())) {
-            accessToken.audience(clientModel.getClientId());
-        }
-
-        return new TokenManager().encodeToken(session, getRealm(), accessToken);
-    }
-
-    private PermissionTicket verifyPermissionTicket(AuthorizationRequest request) {
+    private PermissionTicketToken verifyPermissionTicket(AuthorizationRequest request) {
         String ticketString = request.getTicket();
 
-        if (ticketString == null || !Tokens.verifySignature(session, getRealm(), ticketString)) {
-            throw new ErrorResponseException("invalid_ticket", "Ticket verification failed", Status.FORBIDDEN);
+        if (ticketString == null || !Tokens.verifySignature(getKeycloakSession(), getRealm(), ticketString)) {
+            throw new CorsErrorResponseException(cors, "invalid_ticket", "Ticket verification failed", Status.FORBIDDEN);
         }
 
         try {
-            PermissionTicket ticket = new JWSInput(ticketString).readJsonContent(PermissionTicket.class);
+            PermissionTicketToken ticket = new JWSInput(ticketString).readJsonContent(PermissionTicketToken.class);
 
             if (!ticket.isActive()) {
-                throw new ErrorResponseException("invalid_ticket", "Invalid permission ticket.", Status.FORBIDDEN);
+                throw new CorsErrorResponseException(cors, "invalid_ticket", "Invalid permission ticket.", Status.FORBIDDEN);
             }
 
             return ticket;
         } catch (JWSInputException e) {
-            throw new ErrorResponseException("invalid_ticket", "Could not parse permission ticket.", Status.FORBIDDEN);
+            throw new CorsErrorResponseException(cors, "invalid_ticket", "Could not parse permission ticket.", Status.FORBIDDEN);
         }
     }
+
+    private KeycloakSession getKeycloakSession() {
+        return this.authorization.getKeycloakSession();
+    }
+
+    private RealmModel getRealm() {
+        return getKeycloakSession().getContext().getRealm();
+    }
 }
diff --git a/services/src/main/java/org/keycloak/authorization/AuthorizationService.java b/services/src/main/java/org/keycloak/authorization/AuthorizationService.java
index f519b40..2d0d7d7 100644
--- a/services/src/main/java/org/keycloak/authorization/AuthorizationService.java
+++ b/services/src/main/java/org/keycloak/authorization/AuthorizationService.java
@@ -18,13 +18,11 @@
 
 package org.keycloak.authorization;
 
+import javax.ws.rs.Path;
+
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
-import org.keycloak.authorization.authorization.AuthorizationTokenService;
-import org.keycloak.authorization.entitlement.EntitlementService;
 import org.keycloak.authorization.protection.ProtectionService;
 
-import javax.ws.rs.Path;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -36,30 +34,12 @@ public class AuthorizationService {
         this.authorization = authorization;
     }
 
-    @Path("/entitlement")
-    public Object getEntitlementService() {
-        EntitlementService service = new EntitlementService(this.authorization);
-
-        ResteasyProviderFactory.getInstance().injectProperties(service);
-
-        return service;
-    }
-
     @Path("/protection")
     public Object getProtectionService() {
-        ProtectionService service = new ProtectionService(this.authorization);
+        ProtectionService service = new ProtectionService(authorization);
 
         ResteasyProviderFactory.getInstance().injectProperties(service);
 
         return service;
     }
-
-    @Path("/authorize")
-    public Object authorize() {
-        AuthorizationTokenService resource = new AuthorizationTokenService(this.authorization);
-
-        ResteasyProviderFactory.getInstance().injectProperties(resource);
-
-        return resource;
-    }
 }
diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java
index da3cb0f..047ff5a 100644
--- a/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java
+++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java
@@ -18,14 +18,14 @@
 
 package org.keycloak.authorization.common;
 
-import org.keycloak.authorization.identity.Identity;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.representations.AccessToken;
-
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Map;
 
+import org.keycloak.authorization.identity.Identity;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.representations.AccessToken;
+
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -33,10 +33,6 @@ public class KeycloakEvaluationContext extends DefaultEvaluationContext {
 
     private final KeycloakIdentity identity;
 
-    public KeycloakEvaluationContext(KeycloakSession keycloakSession) {
-        this(new KeycloakIdentity(keycloakSession), keycloakSession);
-    }
-
     public KeycloakEvaluationContext(KeycloakIdentity identity, KeycloakSession keycloakSession) {
         super(identity, keycloakSession);
         this.identity = identity;
diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
index 59963ba..654193d 100644
--- a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
+++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
@@ -22,11 +22,16 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.keycloak.authorization.attribute.Attributes;
 import org.keycloak.authorization.identity.Identity;
 import org.keycloak.authorization.util.Tokens;
+import org.keycloak.models.AuthenticatedClientSessionModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.IDToken;
 import org.keycloak.saml.common.util.StringUtil;
 import org.keycloak.services.ErrorResponseException;
 import org.keycloak.util.JsonSerialization;
@@ -35,9 +40,11 @@ import javax.ws.rs.core.Response.Status;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -53,12 +60,12 @@ public class KeycloakIdentity implements Identity {
         this(Tokens.getAccessToken(keycloakSession), keycloakSession);
     }
 
-    public KeycloakIdentity(KeycloakSession keycloakSession, AccessToken accessToken) {
-        this(accessToken, keycloakSession, keycloakSession.getContext().getRealm());
+    public KeycloakIdentity(KeycloakSession keycloakSession, IDToken token) {
+        this(token, keycloakSession, keycloakSession.getContext().getRealm());
     }
 
-    public KeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession, RealmModel realm) {
-        if (accessToken == null) {
+    public KeycloakIdentity(IDToken token, KeycloakSession keycloakSession, RealmModel realm) {
+        if (token == null) {
             throw new ErrorResponseException("invalid_bearer_token", "Could not obtain bearer access_token from request.", Status.FORBIDDEN);
         }
         if (keycloakSession == null) {
@@ -67,14 +74,13 @@ public class KeycloakIdentity implements Identity {
         if (realm == null) {
             throw new ErrorResponseException("no_keycloak_session", "No realm set", Status.FORBIDDEN);
         }
-        this.accessToken = accessToken;
         this.keycloakSession = keycloakSession;
         this.realm = realm;
 
         Map<String, Collection<String>> attributes = new HashMap<>();
 
         try {
-            ObjectNode objectNode = JsonSerialization.createObjectNode(this.accessToken);
+            ObjectNode objectNode = JsonSerialization.createObjectNode(token);
             Iterator<String> iterator = objectNode.fieldNames();
 
             while (iterator.hasNext()) {
@@ -103,13 +109,30 @@ public class KeycloakIdentity implements Identity {
                 }
             }
 
-            AccessToken.Access realmAccess = accessToken.getRealmAccess();
+            if (token instanceof AccessToken) {
+                this.accessToken = AccessToken.class.cast(token);
+            } else {
+                UserModel userById = keycloakSession.users().getUserById(token.getSubject(), realm);
+                UserSessionModel userSession = keycloakSession.sessions().getUserSession(realm, token.getSessionState());
+                ClientModel client = realm.getClientByClientId(token.getIssuedFor());
+                AuthenticatedClientSessionModel clientSessionModel = userSession.getAuthenticatedClientSessions().get(client.getId());
+                Set<RoleModel> requestedRoles = new HashSet<>();
+                for (String roleId : clientSessionModel.getRoles()) {
+                    RoleModel role = realm.getRoleById(roleId);
+                    if (role != null) {
+                        requestedRoles.add(role);
+                    }
+                }
+                this.accessToken = new TokenManager().createClientAccessToken(keycloakSession, requestedRoles, realm, client, userById, userSession, clientSessionModel);
+            }
+
+            AccessToken.Access realmAccess = this.accessToken.getRealmAccess();
 
             if (realmAccess != null) {
                 attributes.put("kc.realm.roles", realmAccess.getRoles());
             }
 
-            Map<String, AccessToken.Access> resourceAccess = accessToken.getResourceAccess();
+            Map<String, AccessToken.Access> resourceAccess = this.accessToken.getResourceAccess();
 
             if (resourceAccess != null) {
                 resourceAccess.forEach((clientId, access) -> attributes.put("kc.client." + clientId + ".roles", access.getRoles()));
diff --git a/services/src/main/java/org/keycloak/authorization/config/UmaConfiguration.java b/services/src/main/java/org/keycloak/authorization/config/UmaConfiguration.java
new file mode 100644
index 0000000..67fb296
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/config/UmaConfiguration.java
@@ -0,0 +1,89 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.authorization.config;
+
+import java.net.URI;
+
+import javax.ws.rs.core.UriBuilder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.keycloak.authorization.AuthorizationService;
+import org.keycloak.authorization.protection.ProtectionService;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.oidc.OIDCWellKnownProviderFactory;
+import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
+import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.wellknown.WellKnownProvider;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UmaConfiguration extends OIDCConfigurationRepresentation {
+
+    public static final UmaConfiguration create(KeycloakSession session) {
+        WellKnownProvider oidcProvider = session.getProvider(WellKnownProvider.class, OIDCWellKnownProviderFactory.PROVIDER_ID);
+        OIDCConfigurationRepresentation oidcConfig = OIDCConfigurationRepresentation.class.cast(oidcProvider.getConfig());
+        UmaConfiguration configuration = new UmaConfiguration();
+
+        configuration.setIssuer(oidcConfig.getIssuer());
+        configuration.setAuthorizationEndpoint(oidcConfig.getAuthorizationEndpoint());
+        configuration.setTokenEndpoint(oidcConfig.getTokenEndpoint());
+        configuration.setJwksUri(oidcConfig.getJwksUri());
+        configuration.setRegistrationEndpoint(oidcConfig.getRegistrationEndpoint());
+        configuration.setScopesSupported(oidcConfig.getScopesSupported());
+        configuration.setResponseTypesSupported(oidcConfig.getResponseTypesSupported());
+        configuration.setResponseModesSupported(oidcConfig.getResponseModesSupported());
+        configuration.setGrantTypesSupported(oidcConfig.getGrantTypesSupported());
+        configuration.setTokenEndpointAuthMethodsSupported(oidcConfig.getTokenEndpointAuthMethodsSupported());
+        configuration.setTokenEndpointAuthSigningAlgValuesSupported(oidcConfig.getTokenEndpointAuthSigningAlgValuesSupported());
+        configuration.setTokenIntrospectionEndpoint(oidcConfig.getTokenIntrospectionEndpoint());
+        configuration.setLogoutEndpoint(oidcConfig.getLogoutEndpoint());
+
+        UriBuilder uriBuilder = session.getContext().getUri().getBaseUriBuilder();
+
+        RealmModel realm = session.getContext().getRealm();
+
+        configuration.setPermissionEndpoint(uriBuilder.clone().path(RealmsResource.class).path(RealmsResource.class, "getAuthorizationService").path(AuthorizationService.class, "getProtectionService").path(ProtectionService.class, "permission").build(realm.getName()).toString());
+        configuration.setResourceRegistrationEndpoint(uriBuilder.clone().path(RealmsResource.class).path(RealmsResource.class, "getAuthorizationService").path(AuthorizationService.class, "getProtectionService").path(ProtectionService.class, "resource").build(realm.getName()).toString());
+
+        return configuration;
+    }
+
+    @JsonProperty("resource_registration_endpoint")
+    private String resourceRegistrationEndpoint;
+
+    @JsonProperty("permission_endpoint")
+    private String permissionEndpoint;
+
+    public String getResourceRegistrationEndpoint() {
+        return this.resourceRegistrationEndpoint;
+    }
+
+    void setResourceRegistrationEndpoint(String resourceRegistrationEndpoint) {
+        this.resourceRegistrationEndpoint = resourceRegistrationEndpoint;
+    }
+
+    public String getPermissionEndpoint() {
+        return this.permissionEndpoint;
+    }
+
+    void setPermissionEndpoint(String permissionEndpoint) {
+        this.permissionEndpoint = permissionEndpoint;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProvider.java b/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProvider.java
index 222e754..7410f45 100644
--- a/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProvider.java
+++ b/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProvider.java
@@ -17,17 +17,9 @@
  */
 package org.keycloak.authorization.config;
 
-import org.keycloak.common.util.PemUtils;
 import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.RealmModel;
-import org.keycloak.protocol.oidc.OIDCLoginProtocol;
-import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
-import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.wellknown.WellKnownProvider;
 
-import javax.ws.rs.core.UriInfo;
-import java.net.URI;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -41,13 +33,7 @@ public class UmaWellKnownProvider implements WellKnownProvider {
 
     @Override
     public Object getConfig() {
-        RealmModel realm = this.session.getContext().getRealm();
-        UriInfo uriInfo = this.session.getContext().getUri();
-
-        return Configuration.fromDefault(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString(), realm.getName(),
-                URI.create(RealmsResource.protocolUrl(uriInfo).path(OIDCLoginProtocolService.class, "auth").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString()),
-                URI.create(RealmsResource.protocolUrl(uriInfo).path(OIDCLoginProtocolService.class, "token").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString()),
-                PemUtils.encodeKey(session.keys().getActiveRsaKey(realm).getPublicKey()));
+        return UmaConfiguration.create(session);
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProviderFactory.java b/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProviderFactory.java
index 7776720..a5acd63 100644
--- a/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProviderFactory.java
+++ b/services/src/main/java/org/keycloak/authorization/config/UmaWellKnownProviderFactory.java
@@ -27,6 +27,9 @@ import org.keycloak.wellknown.WellKnownProviderFactory;
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class UmaWellKnownProviderFactory implements WellKnownProviderFactory {
+
+    public static final String PROVIDER_ID = "uma2-configuration";
+
     @Override
     public WellKnownProvider create(KeycloakSession session) {
         return new UmaWellKnownProvider(session);
@@ -49,6 +52,6 @@ public class UmaWellKnownProviderFactory implements WellKnownProviderFactory {
 
     @Override
     public String getId() {
-        return "uma-configuration";
+        return PROVIDER_ID;
     }
 }
diff --git a/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java b/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java
index a31e834..989dbe1 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/introspect/RPTIntrospectionProvider.java
@@ -17,6 +17,11 @@
  */
 package org.keycloak.authorization.protection.introspect;
 
+import java.io.IOException;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.jboss.logging.Logger;
 import org.keycloak.models.KeycloakSession;
@@ -25,9 +30,6 @@ import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.AccessToken.Authorization;
 import org.keycloak.util.JsonSerialization;
 
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
 /**
  * Introspects token accordingly with UMA Bearer Token Profile.
  *
@@ -45,28 +47,30 @@ public class RPTIntrospectionProvider extends AccessTokenIntrospectionProvider {
     public Response introspect(String token) {
         LOGGER.debug("Introspecting requesting party token");
         try {
-            AccessToken requestingPartyToken = toAccessToken(token);
-            boolean active = isActive(requestingPartyToken);
+            AccessToken accessToken = verifyAccessToken(token);
+
             ObjectNode tokenMetadata;
 
-            if (active) {
-                LOGGER.debug("Token is active");
-                AccessToken introspect = new AccessToken();
-                introspect.type(requestingPartyToken.getType());
-                introspect.expiration(requestingPartyToken.getExpiration());
-                introspect.issuedAt(requestingPartyToken.getIssuedAt());
-                introspect.audience(requestingPartyToken.getAudience());
-                introspect.notBefore(requestingPartyToken.getNotBefore());
-                introspect.setRealmAccess(null);
-                introspect.setResourceAccess(null);
-                tokenMetadata = JsonSerialization.createObjectNode(introspect);
-                tokenMetadata.putPOJO("permissions", requestingPartyToken.getAuthorization().getPermissions());
+            if (accessToken != null) {
+                AccessToken metadata = new AccessToken();
+
+                metadata.id(accessToken.getId());
+                metadata.setAcr(accessToken.getAcr());
+                metadata.type(accessToken.getType());
+                metadata.expiration(accessToken.getExpiration());
+                metadata.issuedAt(accessToken.getIssuedAt());
+                metadata.audience(accessToken.getAudience());
+                metadata.notBefore(accessToken.getNotBefore());
+                metadata.setRealmAccess(null);
+                metadata.setResourceAccess(null);
+
+                tokenMetadata = JsonSerialization.createObjectNode(metadata);
+                tokenMetadata.putPOJO("permissions", accessToken.getAuthorization().getPermissions());
             } else {
-                LOGGER.debug("Token is not active");
                 tokenMetadata = JsonSerialization.createObjectNode();
             }
 
-            tokenMetadata.put("active", active);
+            tokenMetadata.put("active", accessToken != null);
 
             return Response.ok(JsonSerialization.writeValueAsBytes(tokenMetadata)).type(MediaType.APPLICATION_JSON_TYPE).build();
         } catch (Exception e) {
@@ -74,11 +78,6 @@ public class RPTIntrospectionProvider extends AccessTokenIntrospectionProvider {
         }
     }
 
-    private boolean isActive(AccessToken requestingPartyToken) {
-        Authorization authorization = requestingPartyToken.getAuthorization();
-        return requestingPartyToken.isActive() && authorization != null && authorization.getPermissions() != null && !authorization.getPermissions().isEmpty();
-    }
-
     @Override
     public void close() {
 
diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java b/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
index 1e669cf..7a8e05e 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/AbstractPermissionService.java
@@ -21,16 +21,23 @@ import org.keycloak.authorization.common.KeycloakIdentity;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
-import org.keycloak.authorization.protection.permission.representation.PermissionRequest;
-import org.keycloak.authorization.protection.permission.representation.PermissionResponse;
-import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.models.ClientModel;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
+import org.keycloak.representations.idm.authorization.PermissionResponse;
+import org.keycloak.authorization.store.ResourceStore;
 import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.models.KeyManager;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.representations.idm.authorization.PermissionTicketToken;
+import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.services.ErrorResponseException;
 
 import javax.ws.rs.core.Response;
+
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -51,53 +58,46 @@ public class AbstractPermissionService {
     }
 
     public Response create(List<PermissionRequest> request) {
-        if (request == null) {
+        if (request == null || request.isEmpty()) {
             throw new ErrorResponseException("invalid_permission_request", "Invalid permission request.", Response.Status.BAD_REQUEST);
         }
 
-        List<ResourceRepresentation> resource = verifyRequestedResource(request);
-
-        return Response.status(Response.Status.CREATED).entity(new PermissionResponse(createPermissionTicket(resource))).build();
+        return Response.status(Response.Status.CREATED).entity(new PermissionResponse(createPermissionTicket(request))).build();
     }
 
     private List<ResourceRepresentation> verifyRequestedResource(List<PermissionRequest> request) {
-        StoreFactory storeFactory = authorization.getStoreFactory();
-        return request.stream().map(request1 -> {
-            String resourceSetId = request1.getResourceSetId();
-            String resourceSetName = request1.getResourceSetName();
-            boolean resourceNotProvided = resourceSetId == null && resourceSetName == null;
-
-            if (resourceNotProvided) {
-                if ((request1.getScopes() == null || request1.getScopes().isEmpty())) {
-                    throw new ErrorResponseException("invalid_resource_set_id", "Resource id or name not provided.", Response.Status.BAD_REQUEST);
-                }
-            }
+        ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
 
+        return request.stream().map(permissionRequest -> {
+            String resourceSetId = permissionRequest.getResourceId();
             Resource resource = null;
 
-            if (!resourceNotProvided) {
-                if (resourceSetId != null) {
-                    resource = storeFactory.getResourceStore().findById(resourceSetId, resourceServer.getId());
-                } else {
-                    resource = storeFactory.getResourceStore().findByName(resourceSetName, this.resourceServer.getId());
+            if (resourceSetId == null) {
+                if (permissionRequest.getScopes() == null || permissionRequest.getScopes().isEmpty()) {
+                    throw new ErrorResponseException("invalid_resource_id", "Resource id or name not provided.", Response.Status.BAD_REQUEST);
                 }
+            } else {
+                resource = resourceStore.findById(resourceSetId, resourceServer.getId());
 
                 if (resource == null) {
-                    if (resourceSetId != null) {
-                        throw new ErrorResponseException("nonexistent_resource_set_id", "Resource set with id[" + resourceSetId + "] does not exists in this server.", Response.Status.BAD_REQUEST);
-                    } else {
-                        throw new ErrorResponseException("nonexistent_resource_set_name", "Resource set with name[" + resourceSetName + "] does not exists in this server.", Response.Status.BAD_REQUEST);
-                    }
+                    resource = resourceStore.findByName(resourceSetId, this.resourceServer.getId());
+                }
+
+                if (resource == null) {
+                    throw new ErrorResponseException("invalid_resource_id", "Resource set with id [" + resourceSetId + "] does not exists in this server.", Response.Status.BAD_REQUEST);
                 }
             }
 
-            Set<ScopeRepresentation> scopes = verifyRequestedScopes(request1, resource);
+            Set<ScopeRepresentation> scopes = verifyRequestedScopes(permissionRequest, resource);
 
             if (resource != null) {
-                if (scopes.isEmpty() && !request1.getScopes().isEmpty()) {
-                    return new ResourceRepresentation(null, request1.getScopes().stream().map(ScopeRepresentation::new).collect(Collectors.toSet()));
-                }
-                return new ResourceRepresentation(resource.getName(), scopes);
+                ResourceRepresentation representation = new ResourceRepresentation(resource.getName(), scopes);
+
+                representation.setId(resource.getId());
+                representation.setOwnerManagedAccess(resource.isOwnerManagedAccess());
+                representation.setOwner(new ResourceOwnerRepresentation(resource.getOwner()));
+
+                return representation;
             }
 
             return new ResourceRepresentation(null, scopes);
@@ -105,34 +105,52 @@ public class AbstractPermissionService {
     }
 
     private Set<ScopeRepresentation> verifyRequestedScopes(PermissionRequest request, Resource resource) {
-        return request.getScopes().stream().map(scopeName -> {
+        Set<String> requestScopes = request.getScopes();
+
+        if (requestScopes == null) {
+            return Collections.emptySet();
+        }
+
+        ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
+
+        return requestScopes.stream().map(scopeName -> {
+            Scope scope = null;
+
             if (resource != null) {
-                for (Scope scope : resource.getScopes()) {
-                    if (scope.getName().equals(scopeName)) {
-                        return new ScopeRepresentation(scopeName);
-                    }
-                }
+                scope = resource.getScopes().stream().filter(scope1 -> scope1.getName().equals(scopeName)).findFirst().orElse(null);
 
-                for (Resource baseResource : authorization.getStoreFactory().getResourceStore().findByType(resource.getType(), resourceServer.getId())) {
-                    if (baseResource.getOwner().equals(resource.getResourceServer().getId())) {
-                        for (Scope baseScope : baseResource.getScopes()) {
-                            if (baseScope.getName().equals(scopeName)) {
-                                return new ScopeRepresentation(scopeName);
-                            }
-                        }
-                    }
+                if (scope == null && resource.getType() != null) {
+                    scope = resourceStore.findByType(resource.getType(), resourceServer.getId()).stream()
+                            .filter(baseResource -> baseResource.getOwner().equals(resource.getResourceServer().getId()))
+                            .flatMap(resource1 -> resource1.getScopes().stream())
+                            .filter(baseScope -> baseScope.getName().equals(scopeName)).findFirst().orElse(null);
                 }
-
-                return null;
             } else {
-                return new ScopeRepresentation(scopeName);
+                scope = authorization.getStoreFactory().getScopeStore().findByName(scopeName, resourceServer.getId());
+            }
+
+            if (scope == null) {
+                throw new ErrorResponseException("invalid_scope", "Scope [" + scopeName + "] is invalid", Response.Status.BAD_REQUEST);
             }
-        }).filter(scopeRepresentation -> scopeRepresentation != null).collect(Collectors.toSet());
+
+            return ModelToRepresentation.toRepresentation(scope);
+        }).collect(Collectors.toSet());
     }
 
-    private String createPermissionTicket(List<ResourceRepresentation> resources) {
+    private String createPermissionTicket(List<PermissionRequest> request) {
+        List<PermissionTicketToken.ResourcePermission> permissions = verifyRequestedResource(request).stream().flatMap(resource -> {
+            List<PermissionTicketToken.ResourcePermission> perms = new ArrayList<>();
+            Set<ScopeRepresentation> scopes = resource.getScopes();
+
+            perms.add(new PermissionTicketToken.ResourcePermission(resource.getId(), scopes.stream().map(ScopeRepresentation::getName).collect(Collectors.toSet())));
+
+            return perms.stream();
+        }).collect(Collectors.toList());
+
         KeyManager.ActiveRsaKey keys = this.authorization.getKeycloakSession().keys().getActiveRsaKey(this.authorization.getRealm());
-        return new JWSBuilder().kid(keys.getKid()).jsonContent(new PermissionTicket(resources, this.resourceServer.getId(), this.identity.getAccessToken()))
+        ClientModel targetClient = authorization.getRealm().getClientById(resourceServer.getId());
+
+        return new JWSBuilder().kid(keys.getKid()).jsonContent(new PermissionTicketToken(permissions, targetClient.getClientId(), this.identity.getAccessToken()))
                 .rsa256(keys.getPrivateKey());
     }
-}
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionService.java b/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionService.java
index 4f2181f..e30f7d0 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionService.java
@@ -17,31 +17,129 @@
  */
 package org.keycloak.authorization.protection.permission;
 
+import org.keycloak.OAuthErrorException;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.common.KeycloakIdentity;
+import org.keycloak.authorization.model.PermissionTicket;
 import org.keycloak.authorization.model.ResourceServer;
-import org.keycloak.authorization.protection.permission.representation.PermissionRequest;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
+import org.keycloak.authorization.store.PermissionTicketStore;
+import org.keycloak.models.Constants;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
+import org.keycloak.services.ErrorResponseException;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
 import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
-import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class PermissionService extends AbstractPermissionService {
 
+    private final AuthorizationProvider authorization;
+    private final ResourceServer resourceServer;
+
     public PermissionService(KeycloakIdentity identity, ResourceServer resourceServer, AuthorizationProvider authorization) {
         super(identity, resourceServer, authorization);
+        this.resourceServer = resourceServer;
+        this.authorization = authorization;
     }
 
     @POST
     @Consumes("application/json")
     @Produces("application/json")
-    public Response create(PermissionRequest request) {
-        return create(Arrays.asList(request));
+    public Response create(List<PermissionRequest> request) {
+        return super.create(request);
+    }
+
+    @PUT
+    @Consumes("application/json")
+    public Response update(PermissionTicketRepresentation representation) {
+        if (representation == null || representation.getId() == null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_ticket", Response.Status.BAD_REQUEST);
+        }
+
+        PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
+        PermissionTicket ticket = ticketStore.findById(representation.getId(), resourceServer.getId());
+
+        if (ticket == null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_ticket", Response.Status.BAD_REQUEST);
+        }
+
+        RepresentationToModel.toModel(representation, resourceServer.getId(), authorization);
+
+        return Response.noContent().build();
+    }
+
+    @DELETE
+    @Consumes("application/json")
+    public Response delete(String id) {
+        if (id == null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_ticket", Response.Status.BAD_REQUEST);
+        }
+
+        PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
+        PermissionTicket ticket = ticketStore.findById(id, resourceServer.getId());
+
+        if (ticket == null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "invalid_ticket", Response.Status.BAD_REQUEST);
+        }
+
+        ticketStore.delete(id);
+
+        return Response.noContent().build();
     }
 
+    @GET
+    @Produces("application/json")
+    public Response find(@QueryParam("scopeId") String scopeId,
+                         @QueryParam("resourceId") String resourceId,
+                         @QueryParam("owner") String owner,
+                         @QueryParam("requester") String requester,
+                         @QueryParam("granted") Boolean granted,
+                         @QueryParam("returnNames") Boolean returnNames,
+                         @QueryParam("first") Integer firstResult,
+                         @QueryParam("max") Integer maxResult) {
+        PermissionTicketStore permissionTicketStore = authorization.getStoreFactory().getPermissionTicketStore();
+
+        Map<String, String> filters = new HashMap<>();
+
+        if (resourceId != null) {
+            filters.put(PermissionTicket.RESOURCE, resourceId);
+        }
+
+        if (scopeId != null) {
+            filters.put(PermissionTicket.SCOPE, scopeId);
+        }
+
+        if (owner != null) {
+            filters.put(PermissionTicket.OWNER, owner);
+        }
+
+        if (requester != null) {
+            filters.put(PermissionTicket.REQUESTER, requester);
+        }
+
+        if (granted != null) {
+            filters.put(PermissionTicket.GRANTED, granted.toString());
+        }
+
+        return Response.ok().entity(permissionTicketStore.find(filters, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS)
+                    .stream()
+                        .map(permissionTicket -> ModelToRepresentation.toRepresentation(permissionTicket, returnNames == null ? false : returnNames))
+                        .collect(Collectors.toList()))
+                .build();
+    }
 }
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionsService.java b/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionsService.java
index eea2108..37944d2 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionsService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionsService.java
@@ -20,7 +20,7 @@ package org.keycloak.authorization.protection.permission;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.common.KeycloakIdentity;
 import org.keycloak.authorization.model.ResourceServer;
-import org.keycloak.authorization.protection.permission.representation.PermissionRequest;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.POST;
diff --git a/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java b/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
index 30afbc7..8a811f1 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
@@ -22,10 +22,8 @@ import org.keycloak.OAuthErrorException;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.admin.ResourceSetService;
 import org.keycloak.authorization.common.KeycloakIdentity;
-import org.keycloak.authorization.identity.Identity;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.protection.permission.PermissionService;
-import org.keycloak.authorization.protection.permission.PermissionsService;
 import org.keycloak.authorization.protection.resource.ResourceService;
 import org.keycloak.common.ClientConnection;
 import org.keycloak.models.ClientModel;
@@ -46,6 +44,7 @@ import javax.ws.rs.core.Response.Status;
 public class ProtectionService {
 
     private final AuthorizationProvider authorization;
+
     @Context
     protected ClientConnection clientConnection;
 
@@ -55,7 +54,7 @@ public class ProtectionService {
 
     @Path("/resource_set")
     public Object resource() {
-        KeycloakIdentity identity = createIdentity();
+        KeycloakIdentity identity = createIdentity(true);
         ResourceServer resourceServer = getResourceServer(identity);
         RealmModel realm = authorization.getRealm();
         ClientModel client = realm.getClientById(identity.getId());
@@ -75,7 +74,7 @@ public class ProtectionService {
 
     @Path("/permission")
     public Object permission() {
-        KeycloakIdentity identity = createIdentity();
+        KeycloakIdentity identity = createIdentity(false);
 
         PermissionService resource = new PermissionService(identity, getResourceServer(identity), this.authorization);
 
@@ -84,43 +83,39 @@ public class ProtectionService {
         return resource;
     }
 
-    @Path("/permissions")
-    public Object permissions() {
-        KeycloakIdentity identity = createIdentity();
-
-        PermissionsService resource = new PermissionsService(identity, getResourceServer(identity), this.authorization);
-
-        ResteasyProviderFactory.getInstance().injectProperties(resource);
-
-        return resource;
-    }
-
-    private KeycloakIdentity createIdentity() {
+    private KeycloakIdentity createIdentity(boolean checkProtectionScope) {
         KeycloakIdentity identity = new KeycloakIdentity(this.authorization.getKeycloakSession());
         ResourceServer resourceServer = getResourceServer(identity);
         KeycloakSession keycloakSession = authorization.getKeycloakSession();
         RealmModel realm = keycloakSession.getContext().getRealm();
         ClientModel client = realm.getClientById(resourceServer.getId());
 
-        if (!identity.hasClientRole(client.getClientId(), "uma_protection")) {
-            throw new ErrorResponseException(OAuthErrorException.INVALID_SCOPE, "Requires uma_protection scope.", Status.FORBIDDEN);
+        if (checkProtectionScope) {
+            if (!identity.hasClientRole(client.getClientId(), "uma_protection")) {
+                throw new ErrorResponseException(OAuthErrorException.INVALID_SCOPE, "Requires uma_protection scope.", Status.FORBIDDEN);
+            }
         }
 
         return identity;
     }
 
-    private ResourceServer getResourceServer(Identity identity) {
-        RealmModel realm = this.authorization.getKeycloakSession().getContext().getRealm();
-        ClientModel clientApplication = realm.getClientById(identity.getId());
+    private ResourceServer getResourceServer(KeycloakIdentity identity) {
+        String clientId = identity.getAccessToken().getIssuedFor();
+        RealmModel realm = authorization.getKeycloakSession().getContext().getRealm();
+        ClientModel clientModel = realm.getClientByClientId(clientId);
+
+        if (clientModel == null) {
+            clientModel = realm.getClientById(clientId);
 
-        if (clientApplication == null) {
-            throw new ErrorResponseException("invalid_clientId", "Client application with id [" + identity.getId() + "] does not exist in realm [" + realm.getName() + "]", Status.BAD_REQUEST);
+            if (clientModel == null) {
+                throw new ErrorResponseException("invalid_clientId", "Client application with id [" + clientId + "] does not exist in realm [" + realm.getName() + "]", Status.BAD_REQUEST);
+            }
         }
 
-        ResourceServer resourceServer = this.authorization.getStoreFactory().getResourceServerStore().findById(identity.getId());
+        ResourceServer resourceServer = this.authorization.getStoreFactory().getResourceServerStore().findById(clientModel.getId());
 
         if (resourceServer == null) {
-            throw new ErrorResponseException("invalid_clientId", "Client application [" + clientApplication.getClientId() + "] is not registered as resource server.", Status.FORBIDDEN);
+            throw new ErrorResponseException("invalid_clientId", "Client application [" + clientModel.getClientId() + "] is not registered as a resource server.", Status.FORBIDDEN);
         }
 
         return resourceServer;
diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaResourceRepresentation.java b/services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaResourceRepresentation.java
index 2997498..fbbe08e 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaResourceRepresentation.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/representation/UmaResourceRepresentation.java
@@ -41,11 +41,14 @@ public class UmaResourceRepresentation {
     private String type;
 
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    @JsonProperty("resource_scopes")
     private Set<UmaScopeRepresentation> scopes;
 
     @JsonProperty("icon_uri")
     private String iconUri;
     private String owner;
+    private Boolean ownerManagedAccess;
+
 
     /**
      * Creates a new instance.
@@ -150,4 +153,12 @@ public class UmaResourceRepresentation {
     public void setOwner(String owner) {
         this.owner = owner;
     }
+
+    public void setOwnerManagedAccess(Boolean ownerManagedAccess) {
+        this.ownerManagedAccess = ownerManagedAccess;
+    }
+
+    public Boolean getOwnerManagedAccess() {
+        return ownerManagedAccess;
+    }
 }
diff --git a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
index c2e11dc..0cead7d 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/resource/ResourceService.java
@@ -17,9 +17,8 @@
  */
 package org.keycloak.authorization.protection.resource;
 
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import javax.ws.rs.Consumes;
@@ -36,6 +35,7 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
+import org.jboss.resteasy.annotations.cache.NoCache;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.admin.ResourceSetService;
 import org.keycloak.authorization.identity.Identity;
@@ -43,8 +43,6 @@ import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.protection.resource.representation.UmaResourceRepresentation;
 import org.keycloak.authorization.protection.resource.representation.UmaScopeRepresentation;
-import org.keycloak.authorization.store.StoreFactory;
-import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
@@ -72,14 +70,10 @@ public class ResourceService {
     @Produces("application/json")
     public Response create(@Context  UriInfo uriInfo, UmaResourceRepresentation umaResource) {
         checkResourceServerSettings();
-        ResourceRepresentation resource = toResourceRepresentation(umaResource);
-        Response response = this.resourceManager.create(uriInfo, resource);
-
-        if (response.getEntity() instanceof ResourceRepresentation) {
-            return Response.status(Status.CREATED).entity(toUmaRepresentation((ResourceRepresentation) response.getEntity())).build();
+        if (umaResource == null) {
+            return Response.status(Status.BAD_REQUEST).build();
         }
-
-        return response;
+        return this.resourceManager.create(uriInfo, toResourceRepresentation(umaResource), (Function<Resource, UmaResourceRepresentation>) this::toUmaRepresentation);
     }
 
     @Path("{id}")
@@ -107,79 +101,23 @@ public class ResourceService {
     @Path("/{id}")
     @GET
     @Produces("application/json")
-    public RegistrationResponse findById(@PathParam("id") String id) {
-        Response response = this.resourceManager.findById(id);
-        UmaResourceRepresentation resource = toUmaRepresentation((ResourceRepresentation) response.getEntity());
-
-        if (resource == null) {
-            throw new ErrorResponseException("not_found", "Resource with id [" + id + "] not found.", Status.NOT_FOUND);
-        }
-
-        return new RegistrationResponse(resource);
+    public Response findById(@PathParam("id") String id) {
+        return this.resourceManager.findById(id, (Function<Resource, UmaResourceRepresentation>) resource -> toUmaRepresentation(resource));
     }
 
     @GET
+    @NoCache
     @Produces("application/json")
-    public Set<String> find(@QueryParam("filter") String filter) {
-        if (filter == null) {
-            return findAll();
-        } else {
-            return findByFilter(filter);
-        }
-    }
-
-    private Set<String> findAll() {
-        Response response = this.resourceManager.find(null, null, null, null, null, null, true, -1, -1);
-        List<ResourceRepresentation> resources = (List<ResourceRepresentation>) response.getEntity();
-        return resources.stream().map(ResourceRepresentation::getId).collect(Collectors.toSet());
-    }
-
-    private Set<String> findByFilter(String filter) {
-        Set<ResourceRepresentation> resources = new HashSet<>();
-        StoreFactory storeFactory = authorization.getStoreFactory();
-
-        if (filter != null) {
-            for (String currentFilter : filter.split("&")) {
-                String[] parts = currentFilter.split("=");
-                String filterType = parts[0];
-                final String filterValue;
-
-                if (parts.length > 1) {
-                    filterValue = parts[1];
-                } else {
-                    filterValue = null;
-                }
-
-
-                if ("name".equals(filterType)) {
-                    Resource resource = storeFactory.getResourceStore().findByName(filterValue, this.resourceServer.getId());
-
-                    if (resource != null) {
-                        resources.add(ModelToRepresentation.toRepresentation(resource, resourceServer, authorization));
-                    }
-                } else if ("type".equals(filterType)) {
-                    resources.addAll(storeFactory.getResourceStore().findByResourceServer(this.resourceServer.getId()).stream().filter(description -> filterValue == null || filterValue.equals(description.getType())).collect(Collectors.toSet()).stream()
-                            .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization))
-                            .collect(Collectors.toList()));
-                } else if ("uri".equals(filterType)) {
-                    resources.addAll(storeFactory.getResourceStore().findByUri(filterValue, this.resourceServer.getId()).stream()
-                            .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization))
-                            .collect(Collectors.toList()));
-                } else if ("owner".equals(filterType)) {
-                    resources.addAll(storeFactory.getResourceStore().findByOwner(filterValue, this.resourceServer.getId()).stream()
-                            .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization))
-                            .collect(Collectors.toList()));
-                }
-            }
-        } else {
-            resources = storeFactory.getResourceStore().findByOwner(identity.getId(), resourceServer.getId()).stream()
-                    .map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization))
-                    .collect(Collectors.toSet());
-        }
-
-        return resources.stream()
-                .map(ResourceRepresentation::getId)
-                .collect(Collectors.toSet());
+    public Response find(@QueryParam("_id") String id,
+                         @QueryParam("name") String name,
+                         @QueryParam("uri") String uri,
+                         @QueryParam("owner") String owner,
+                         @QueryParam("type") String type,
+                         @QueryParam("scope") String scope,
+                         @QueryParam("deep") Boolean deep,
+                         @QueryParam("first") Integer firstResult,
+                         @QueryParam("max") Integer maxResult) {
+        return resourceManager.find(id, name, uri, owner, type, scope, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, String>) (resource, deep1) -> resource.getId());
     }
 
     private ResourceRepresentation toResourceRepresentation(UmaResourceRepresentation umaResource) {
@@ -190,6 +128,7 @@ public class ResourceService {
         resource.setName(umaResource.getName());
         resource.setUri(umaResource.getUri());
         resource.setType(umaResource.getType());
+        resource.setOwnerManagedAccess(umaResource.getOwnerManagedAccess());
 
         ResourceOwnerRepresentation owner = new ResourceOwnerRepresentation();
         String ownerId = umaResource.getOwner();
@@ -214,24 +153,24 @@ public class ResourceService {
         return resource;
     }
 
-    private UmaResourceRepresentation toUmaRepresentation(ResourceRepresentation representation) {
-        if (representation == null) {
+    private UmaResourceRepresentation toUmaRepresentation(Resource model) {
+        if (model == null) {
             return null;
         }
 
         UmaResourceRepresentation resource = new UmaResourceRepresentation();
 
-        resource.setId(representation.getId());
-        resource.setIconUri(representation.getIconUri());
-        resource.setName(representation.getName());
-        resource.setUri(representation.getUri());
-        resource.setType(representation.getType());
+        resource.setId(model.getId());
+        resource.setIconUri(model.getIconUri());
+        resource.setName(model.getName());
+        resource.setUri(model.getUri());
+        resource.setType(model.getType());
 
-        if (representation.getOwner() != null) {
-            resource.setOwner(representation.getOwner().getId());
+        if (model.getOwner() != null) {
+            resource.setOwner(model.getOwner());
         }
 
-        resource.setScopes(representation.getScopes().stream().map(scopeRepresentation -> {
+        resource.setScopes(model.getScopes().stream().map(scopeRepresentation -> {
             UmaScopeRepresentation umaScopeRep = new UmaScopeRepresentation();
             umaScopeRep.setId(scopeRepresentation.getId());
             umaScopeRep.setName(scopeRepresentation.getName());
diff --git a/services/src/main/java/org/keycloak/authorization/util/Permissions.java b/services/src/main/java/org/keycloak/authorization/util/Permissions.java
index 0c285bc..38b65b8 100644
--- a/services/src/main/java/org/keycloak/authorization/util/Permissions.java
+++ b/services/src/main/java/org/keycloak/authorization/util/Permissions.java
@@ -27,11 +27,11 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
+
 import javax.ws.rs.core.Response.Status;
 
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.Decision.Effect;
-import org.keycloak.authorization.authorization.representation.AuthorizationRequestMetadata;
 import org.keycloak.authorization.identity.Identity;
 import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
@@ -42,6 +42,7 @@ import org.keycloak.authorization.policy.evaluation.Result;
 import org.keycloak.authorization.store.ResourceStore;
 import org.keycloak.authorization.store.ScopeStore;
 import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest.Metadata;
 import org.keycloak.representations.idm.authorization.Permission;
 import org.keycloak.services.ErrorResponseException;
 
@@ -142,7 +143,11 @@ public final class Permissions {
         return permissions;
     }
 
-    public static List<Permission> permits(List<Result> evaluation, AuthorizationRequestMetadata metadata, AuthorizationProvider authorizationProvider, ResourceServer resourceServer) {
+    public static List<Permission> permits(List<Result> evaluation, AuthorizationProvider authorizationProvider, ResourceServer resourceServer) {
+        return permits(evaluation, null, authorizationProvider, resourceServer);
+    }
+
+    public static List<Permission> permits(List<Result> evaluation, Metadata metadata, AuthorizationProvider authorizationProvider, ResourceServer resourceServer) {
         Map<String, Permission> permissions = new LinkedHashMap<>();
 
         for (Result result : evaluation) {
@@ -159,8 +164,12 @@ public final class Permissions {
 
                 if (Effect.PERMIT.equals(policyResult.getStatus())) {
                     if (isScopePermission(policy)) {
-                        // try to grant any scope from a scope-based permission
-                        grantedScopes.addAll(policyScopes);
+                        for (Scope scope : permission.getScopes()) {
+                            if (policyScopes.contains(scope)) {
+                                // try to grant any scope from a scope-based permission
+                                grantedScopes.add(scope);
+                            }
+                        }
                     } else if (isResourcePermission(policy)) {
                         // we assume that all requested scopes should be granted given that we are processing a resource-based permission.
                         // Later they will be filtered based on any denied scope, if any.
@@ -173,38 +182,35 @@ public final class Permissions {
                         // store all scopes associated with the scope-based permission
                         deniedScopes.addAll(policyScopes);
                     } else if (isResourcePermission(policy)) {
-                        // we should not grant anything
                         resourceDenied = true;
-                        break;
+                        deniedScopes.addAll(permission.getResource().getScopes());
                     }
                 }
             }
 
-            if (!resourceDenied) {
-                // remove any scope denied from the list of granted scopes
-                if (!deniedScopes.isEmpty()) {
-                    grantedScopes.removeAll(deniedScopes);
-                }
+            // remove any scope denied from the list of granted scopes
+            if (!deniedScopes.isEmpty()) {
+                grantedScopes.removeAll(deniedScopes);
+            }
 
-                // if there are no policy results is because the permission didn't match any policy.
-                // In this case, if results is empty is because we are in permissive mode.
-                if (!results.isEmpty()) {
-                    // update the current permission with the granted scopes
-                    permission.getScopes().clear();
-                    permission.getScopes().addAll(grantedScopes);
-                }
+            // if there are no policy results is because the permission didn't match any policy.
+            // In this case, if results is empty is because we are in permissive mode.
+            if (!results.isEmpty()) {
+                // update the current permission with the granted scopes
+                permission.getScopes().clear();
+                permission.getScopes().addAll(grantedScopes);
+            }
 
-                if (deniedCount == 0) {
+            if (deniedCount == 0) {
+                result.setStatus(Effect.PERMIT);
+                grantPermission(authorizationProvider, permissions, permission, resourceServer, metadata);
+            } else {
+                // if a full deny or resource denied or the requested scopes were denied
+                if (deniedCount == results.size() || resourceDenied || (!deniedScopes.isEmpty() && grantedScopes.isEmpty())) {
+                    result.setStatus(Effect.DENY);
+                } else {
                     result.setStatus(Effect.PERMIT);
                     grantPermission(authorizationProvider, permissions, permission, resourceServer, metadata);
-                } else {
-                    // if a full deny or resource denied or the requested scopes were denied
-                    if (deniedCount == results.size() || resourceDenied || (!deniedScopes.isEmpty() && grantedScopes.isEmpty())) {
-                        result.setStatus(Effect.DENY);
-                    } else {
-                        result.setStatus(Effect.PERMIT);
-                        grantPermission(authorizationProvider, permissions, permission, resourceServer, metadata);
-                    }
                 }
             }
         }
@@ -220,7 +226,7 @@ public final class Permissions {
         return "scope".equals(policy.getType());
     }
 
-    private static void grantPermission(AuthorizationProvider authorizationProvider, Map<String, Permission> permissions, ResourcePermission permission, ResourceServer resourceServer, AuthorizationRequestMetadata metadata) {
+    private static void grantPermission(AuthorizationProvider authorizationProvider, Map<String, Permission> permissions, ResourcePermission permission, ResourceServer resourceServer, Metadata metadata) {
         List<Resource> resources = new ArrayList<>();
         Resource resource = permission.getResource();
         Set<String> scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet());
@@ -239,7 +245,7 @@ public final class Permissions {
         if (!resources.isEmpty()) {
             for (Resource allowedResource : resources) {
                 String resourceId = allowedResource.getId();
-                String resourceName = metadata == null || metadata.isIncludeResourceName() ? allowedResource.getName() : null;
+                String resourceName = metadata == null || metadata.getIncludeResourceName() ? allowedResource.getName() : null;
                 Permission evalPermission = permissions.get(allowedResource.getId());
 
                 if (evalPermission == null) {
diff --git a/services/src/main/java/org/keycloak/authorization/util/Tokens.java b/services/src/main/java/org/keycloak/authorization/util/Tokens.java
index d693a4c..745d7c7 100644
--- a/services/src/main/java/org/keycloak/authorization/util/Tokens.java
+++ b/services/src/main/java/org/keycloak/authorization/util/Tokens.java
@@ -48,10 +48,16 @@ public class Tokens {
         return null;
     }
 
-    public static String getAccessTokenAsString(KeycloakSession keycloakSession) {
+    public static AccessToken getAccessToken(String accessToken, KeycloakSession keycloakSession) {
         AppAuthManager authManager = new AppAuthManager();
+        KeycloakContext context = keycloakSession.getContext();
+        AuthResult authResult = authManager.authenticateBearerToken(accessToken, keycloakSession, context.getRealm(), context.getUri(), context.getConnection(), context.getRequestHeaders());
+
+        if (authResult != null) {
+            return authResult.getToken();
+        }
 
-        return authManager.extractAuthorizationHeaderToken(keycloakSession.getContext().getRequestHeaders());
+        return null;
     }
 
     public static boolean verifySignature(KeycloakSession keycloakSession, RealmModel realm, String token) {
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java b/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
index 64fc15b..2d68f49 100755
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
@@ -23,6 +23,7 @@ import org.keycloak.forms.account.AccountProvider;
 import org.keycloak.forms.account.freemarker.model.AccountBean;
 import org.keycloak.forms.account.freemarker.model.AccountFederatedIdentityBean;
 import org.keycloak.forms.account.freemarker.model.ApplicationsBean;
+import org.keycloak.forms.account.freemarker.model.AuthorizationBean;
 import org.keycloak.forms.account.freemarker.model.FeaturesBean;
 import org.keycloak.forms.account.freemarker.model.LogBean;
 import org.keycloak.forms.account.freemarker.model.PasswordBean;
@@ -52,6 +53,7 @@ import org.keycloak.utils.MediaType;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import java.io.IOException;
@@ -92,6 +94,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
 
     protected List<FormMessage> messages = null;
     protected MessageType messageType = MessageType.ERROR;
+    private boolean authorizationSupported;
 
     public FreeMarkerAccountProvider(KeycloakSession session, FreeMarkerUtil freeMarker) {
         this.session = session;
@@ -156,7 +159,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
             attributes.put("locale", new LocaleBean(realm, locale, b, messagesBundle));
         }
 
-        attributes.put("features", new FeaturesBean(identityProviderEnabled, eventsEnabled, passwordUpdateSupported));
+        attributes.put("features", new FeaturesBean(identityProviderEnabled, eventsEnabled, passwordUpdateSupported, authorizationSupported));
         attributes.put("account", new AccountBean(user, profileFormData));
 
         switch (page) {
@@ -179,7 +182,16 @@ public class FreeMarkerAccountProvider implements AccountProvider {
             case PASSWORD:
                 attributes.put("password", new PasswordBean(passwordSet));
                 break;
-            default:
+            case RESOURCES:
+                if (!realm.isUserManagedAccessAllowed()) {
+                    return Response.status(Status.FORBIDDEN).build();
+                }
+                attributes.put("authorization", new AuthorizationBean(session, user, uriInfo));
+            case RESOURCE_DETAIL:
+                if (!realm.isUserManagedAccessAllowed()) {
+                    return Response.status(Status.FORBIDDEN).build();
+                }
+                attributes.put("authorization", new AuthorizationBean(session, user, uriInfo));
         }
 
         return processTemplate(theme, page, attributes, locale);
@@ -187,7 +199,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
 
     /**
      * Get Theme used for page rendering.
-     * 
+     *
      * @return theme for page rendering, never null
      * @throws IOException in case of Theme loading problem
      */
@@ -197,7 +209,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
 
     /**
      * Load message bundle and place it into <code>msg</code> template attribute. Also load Theme properties and place them into <code>properties</code> template attribute.
-     * 
+     *
      * @param theme actual Theme to load bundle from
      * @param locale to load bundle for
      * @param attributes template attributes to add resources to
@@ -222,7 +234,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
 
     /**
      * Handle messages to be shown on the page - set them to template attributes
-     * 
+     *
      * @param locale to be used for message text loading
      * @param messagesBundle to be used for message text loading
      * @param attributes template attributes to messages related info to
@@ -247,7 +259,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
 
     /**
      * Process FreeMarker template and prepare Response. Some fields are used for rendering also.
-     * 
+     *
      * @param theme to be used (provided by <code>getTheme()</code>)
      * @param page to be rendered
      * @param attributes pushed to the template
@@ -358,10 +370,11 @@ public class FreeMarkerAccountProvider implements AccountProvider {
     }
 
     @Override
-    public AccountProvider setFeatures(boolean identityProviderEnabled, boolean eventsEnabled, boolean passwordUpdateSupported) {
+    public AccountProvider setFeatures(boolean identityProviderEnabled, boolean eventsEnabled, boolean passwordUpdateSupported, boolean authorizationSupported) {
         this.identityProviderEnabled = identityProviderEnabled;
         this.eventsEnabled = eventsEnabled;
         this.passwordUpdateSupported = passwordUpdateSupported;
+        this.authorizationSupported = authorizationSupported;
         return this;
     }
 
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/AuthorizationBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/AuthorizationBean.java
new file mode 100755
index 0000000..837a843
--- /dev/null
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/AuthorizationBean.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2017 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.forms.account.freemarker.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.core.UriInfo;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.PermissionTicketStore;
+import org.keycloak.common.util.Time;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class AuthorizationBean {
+
+    private final UserModel user;
+    private final AuthorizationProvider authorization;
+    private final UriInfo uriInfo;
+    private ResourceBean resource;
+    private List<ResourceBean> resources;
+    private Collection<ResourceBean> userSharedResources;
+    private Collection<ResourceBean> requestsWaitingPermission;
+    private Collection<ResourceBean> resourcesWaitingOthersApproval;
+
+    public AuthorizationBean(KeycloakSession session, UserModel user, UriInfo uriInfo) {
+        this.user = user;
+        this.uriInfo = uriInfo;
+        authorization = session.getProvider(AuthorizationProvider.class);
+        List<String> pathParameters = uriInfo.getPathParameters().get("resource_id");
+
+        if (pathParameters != null && !pathParameters.isEmpty()) {
+            Resource resource = authorization.getStoreFactory().getResourceStore().findById(pathParameters.get(0), null);
+
+            if (resource != null && !resource.getOwner().equals(user.getId())) {
+                throw new RuntimeException("User [" + user.getUsername() + "] can not access resource [" + resource.getId() + "]");
+            }
+        }
+    }
+
+    public Collection<ResourceBean> getResourcesWaitingOthersApproval() {
+        if (resourcesWaitingOthersApproval == null) {
+            HashMap<String, String> filters = new HashMap<>();
+
+            filters.put(PermissionTicket.REQUESTER, user.getId());
+            filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
+
+            resourcesWaitingOthersApproval = toResourceRepresentation(findPermissions(filters));
+        }
+
+        return resourcesWaitingOthersApproval;
+    }
+
+    public Collection<ResourceBean> getResourcesWaitingApproval() {
+        if (requestsWaitingPermission == null) {
+            HashMap<String, String> filters = new HashMap<>();
+
+            filters.put(PermissionTicket.OWNER, user.getId());
+            filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
+
+            requestsWaitingPermission = toResourceRepresentation(findPermissions(filters));
+        }
+
+        return requestsWaitingPermission;
+    }
+
+    public List<ResourceBean> getResources() {
+        if (resources == null) {
+            resources = authorization.getStoreFactory().getResourceStore().findByOwner(user.getId(), null).stream()
+                    .filter(Resource::isOwnerManagedAccess)
+                    .map(ResourceBean::new)
+                    .collect(Collectors.toList());
+        }
+        return resources;
+    }
+
+    public Collection<ResourceBean> getSharedResources() {
+        if (userSharedResources == null) {
+            HashMap<String, String> filters = new HashMap<>();
+
+            filters.put(PermissionTicket.REQUESTER, user.getId());
+            filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
+
+            PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
+
+            userSharedResources = toResourceRepresentation(ticketStore.find(filters, null, -1, -1));
+        }
+        return userSharedResources;
+    }
+
+    public ResourceBean getResource() {
+        if (resource == null) {
+            String resourceId = uriInfo.getPathParameters().getFirst("resource_id");
+
+            if (resourceId != null) {
+                resource = getResource(resourceId);
+            }
+        }
+
+        return resource;
+    }
+
+    private ResourceBean getResource(String id) {
+        return new ResourceBean(authorization.getStoreFactory().getResourceStore().findById(id, null));
+    }
+
+    public static class RequesterBean {
+
+        private final Long createdTimestamp;
+        private final Long grantedTimestamp;
+        private UserModel requester;
+        private List<PermissionScopeBean> scopes = new ArrayList<>();
+        private boolean granted;
+
+        public RequesterBean(PermissionTicket ticket, AuthorizationProvider authorization) {
+            this.requester = authorization.getKeycloakSession().users().getUserById(ticket.getRequester(), authorization.getRealm());
+            granted = ticket.isGranted();
+            createdTimestamp = ticket.getCreatedTimestamp();
+            grantedTimestamp = ticket.getGrantedTimestamp();
+        }
+
+        public UserModel getRequester() {
+            return requester;
+        }
+
+        public List<PermissionScopeBean> getScopes() {
+            return scopes;
+        }
+
+        private void addScope(PermissionTicket ticket) {
+            if (ticket != null) {
+                scopes.add(new PermissionScopeBean(ticket));
+            }
+        }
+
+        public boolean isGranted() {
+            return (granted && scopes.isEmpty()) || scopes.stream().filter(permissionScopeBean -> permissionScopeBean.isGranted()).count() > 0;
+        }
+
+        public Date getCreatedDate() {
+            return Time.toDate(createdTimestamp);
+        }
+
+        public Date getGrantedDate() {
+            if (grantedTimestamp == null) {
+                PermissionScopeBean permission = scopes.stream().filter(permissionScopeBean -> permissionScopeBean.isGranted()).findFirst().orElse(null);
+
+                if (permission == null) {
+                    return null;
+                }
+
+                return permission.getGrantedDate();
+            }
+            return Time.toDate(grantedTimestamp);
+        }
+    }
+
+    public static class PermissionScopeBean {
+
+        private final Scope scope;
+        private final PermissionTicket ticket;
+
+        public PermissionScopeBean(PermissionTicket ticket) {
+            this.ticket = ticket;
+            scope = ticket.getScope();
+        }
+
+        public String getId() {
+            return ticket.getId();
+        }
+
+        public Scope getScope() {
+            return scope;
+        }
+
+        public boolean isGranted() {
+            return ticket.isGranted();
+        }
+
+        private Date getGrantedDate() {
+            if (isGranted()) {
+                return Time.toDate(ticket.getGrantedTimestamp());
+            }
+            return null;
+        }
+    }
+
+    public class ResourceBean {
+
+        private final ResourceServerBean resourceServer;
+        private final UserModel owner;
+        private Resource resource;
+        private Map<String, RequesterBean> permissions = new HashMap<>();
+        private Collection<RequesterBean> shares;
+
+        public ResourceBean(Resource resource) {
+            RealmModel realm = authorization.getRealm();
+            resourceServer = new ResourceServerBean(realm.getClientById(resource.getResourceServer().getId()));
+            this.resource = resource;
+            owner = authorization.getKeycloakSession().users().getUserById(resource.getOwner(), realm);
+        }
+
+        public String getId() {
+            return resource.getId();
+        }
+
+        public String getName() {
+            return resource.getName();
+        }
+
+        public String getDisplayName() {
+            return resource.getDisplayName();
+        }
+
+        public String getIconUri() {
+            return resource.getIconUri();
+        }
+
+        public UserModel getOwner() {
+            return owner;
+        }
+
+        public List<ScopeRepresentation> getScopes() {
+            return resource.getScopes().stream().map(ModelToRepresentation::toRepresentation).collect(Collectors.toList());
+        }
+
+        public Collection<RequesterBean> getShares() {
+            if (shares == null) {
+                Map<String, String> filters = new HashMap<>();
+
+                filters.put(PermissionTicket.RESOURCE, resource.getId());
+                filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
+
+                shares = toPermissionRepresentation(findPermissions(filters));
+            }
+
+            return shares;
+        }
+
+        public ResourceServerBean getResourceServer() {
+            return resourceServer;
+        }
+
+        public Collection<RequesterBean> getPermissions() {
+            return permissions.values();
+        }
+
+        private void addPermission(PermissionTicket ticket, AuthorizationProvider authorization) {
+            permissions.computeIfAbsent(ticket.getRequester(), key -> new RequesterBean(ticket, authorization)).addScope(ticket);
+        }
+    }
+
+    private Collection<RequesterBean> toPermissionRepresentation(List<PermissionTicket> permissionRequests) {
+        Map<String, RequesterBean> requests = new HashMap<>();
+
+        for (PermissionTicket ticket : permissionRequests) {
+            Resource resource = ticket.getResource();
+
+            if (!resource.isOwnerManagedAccess()) {
+                continue;
+            }
+
+            requests.computeIfAbsent(ticket.getRequester(), resourceId -> new RequesterBean(ticket, authorization)).addScope(ticket);
+        }
+
+        return requests.values();
+    }
+
+    private Collection<ResourceBean> toResourceRepresentation(List<PermissionTicket> tickets) {
+        Map<String, ResourceBean> requests = new HashMap<>();
+
+        for (PermissionTicket ticket : tickets) {
+            Resource resource = ticket.getResource();
+
+            if (!resource.isOwnerManagedAccess()) {
+                continue;
+            }
+
+            requests.computeIfAbsent(resource.getId(), resourceId -> getResource(resourceId)).addPermission(ticket, authorization);
+        }
+
+        return requests.values();
+    }
+
+    private List<PermissionTicket> findPermissions(Map<String, String> filters) {
+        return authorization.getStoreFactory().getPermissionTicketStore().find(filters, null, -1, -1);
+    }
+
+    public class ResourceServerBean {
+
+        private ClientModel clientModel;
+
+        public ResourceServerBean(ClientModel clientModel) {
+            this.clientModel = clientModel;
+        }
+
+        public String getName() {
+            String name = clientModel.getName();
+
+            if (name != null) {
+                return name;
+            }
+
+            return clientModel.getClientId();
+        }
+
+        public String getRedirectUri() {
+            Set<String> redirectUris = clientModel.getRedirectUris();
+
+            if (redirectUris.isEmpty()) {
+                return null;
+            }
+
+            return redirectUris.iterator().next();
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/FeaturesBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/FeaturesBean.java
index fa41dda..262063d 100755
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/model/FeaturesBean.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/FeaturesBean.java
@@ -25,11 +25,13 @@ public class FeaturesBean {
     private final boolean identityFederation;
     private final boolean log;
     private final boolean passwordUpdateSupported;
+    private boolean authorization;
 
-    public FeaturesBean(boolean identityFederation, boolean log, boolean passwordUpdateSupported) {
+    public FeaturesBean(boolean identityFederation, boolean log, boolean passwordUpdateSupported, boolean authorization) {
         this.identityFederation = identityFederation;
         this.log = log;
         this.passwordUpdateSupported = passwordUpdateSupported;
+        this.authorization = authorization;
     }
 
     public boolean isIdentityFederation() {
@@ -43,4 +45,8 @@ public class FeaturesBean {
     public boolean isPasswordUpdateSupported() {
         return passwordUpdateSupported;
     }
+
+    public boolean isAuthorization() {
+        return authorization;
+    }
 }
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/RealmBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/RealmBean.java
index d818876..a0407aa 100755
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/model/RealmBean.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/RealmBean.java
@@ -68,4 +68,8 @@ public class RealmBean {
     public boolean isRegistrationEmailAsUsername() {
         return realm.isRegistrationEmailAsUsername();
     }
+
+    public boolean isUserManagedAccessAllowed() {
+        return realm.isUserManagedAccessAllowed();
+    }
 }
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/UrlBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/UrlBean.java
index a67db6e..0de20be 100755
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/model/UrlBean.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/UrlBean.java
@@ -74,6 +74,22 @@ public class UrlBean {
         return Urls.accountLogout(baseQueryURI, currentURI, realm).toString();
     }
 
+    public String getResourceUrl() {
+        return Urls.accountResourcesPage(baseQueryURI, realm).toString();
+    }
+
+    public String getResourceDetailUrl(String id) {
+        return Urls.accountResourceDetailPage(id, baseQueryURI, realm).toString();
+    }
+
+    public String getResourceGrant(String id) {
+        return Urls.accountResourceGrant(id, baseQueryURI, realm).toString();
+    }
+
+    public String getResourceShare(String id) {
+        return Urls.accountResourceShare(id, baseQueryURI, realm).toString();
+    }
+
     public String getResourcesPath() {
         URI uri = Urls.themeRoot(baseURI);
         return uri.getPath() + "/" + theme.getType().toString().toLowerCase() +"/" + theme.getName();
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/Templates.java b/services/src/main/java/org/keycloak/forms/account/freemarker/Templates.java
index 5f98987..4882293 100755
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/Templates.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/Templates.java
@@ -40,6 +40,10 @@ public class Templates {
                 return "sessions.ftl";
             case APPLICATIONS:
                 return "applications.ftl";
+            case RESOURCES:
+                return "resources.ftl";
+            case RESOURCE_DETAIL:
+                return "resource-detail.ftl";
             default:
                 throw new IllegalArgumentException();
         }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProvider.java
index e89db0f..6166071 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProvider.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/AccessTokenIntrospectionProvider.java
@@ -17,7 +17,14 @@
  */
 package org.keycloak.protocol.oidc;
 
+import java.io.IOException;
+import java.security.PublicKey;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.keycloak.OAuthErrorException;
 import org.keycloak.RSATokenVerifier;
 import org.keycloak.common.VerificationException;
 import org.keycloak.models.KeycloakSession;
@@ -27,10 +34,6 @@ import org.keycloak.services.ErrorResponseException;
 import org.keycloak.services.Urls;
 import org.keycloak.util.JsonSerialization;
 
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import java.security.PublicKey;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -48,42 +51,18 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
 
     public Response introspect(String token) {
         try {
-            boolean valid = true;
-
-            AccessToken toIntrospect = null;
-
-            try {
-                RSATokenVerifier verifier = RSATokenVerifier.create(token)
-                        .realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
-
-                PublicKey publicKey = session.keys().getRsaPublicKey(realm, verifier.getHeader().getKeyId());
-                if (publicKey == null) {
-                    valid = false;
-                } else {
-                    verifier.publicKey(publicKey);
-                    verifier.verify();
-                    toIntrospect = verifier.getToken();
-                }
-            } catch (VerificationException e) {
-                valid = false;
-            }
-
-            RealmModel realm = this.session.getContext().getRealm();
+            AccessToken accessToken = verifyAccessToken(token);
             ObjectNode tokenMetadata;
 
-            if (valid && toIntrospect != null) {
-                valid = tokenManager.isTokenValid(session, realm, toIntrospect);
-            }
-
-            if (valid) {
-                tokenMetadata = JsonSerialization.createObjectNode(toIntrospect);
-                tokenMetadata.put("client_id", toIntrospect.getIssuedFor());
-                tokenMetadata.put("username", toIntrospect.getPreferredUsername());
+            if (accessToken != null) {
+                tokenMetadata = JsonSerialization.createObjectNode(accessToken);
+                tokenMetadata.put("client_id", accessToken.getIssuedFor());
+                tokenMetadata.put("username", accessToken.getPreferredUsername());
             } else {
                 tokenMetadata = JsonSerialization.createObjectNode();
             }
 
-            tokenMetadata.put("active", valid);
+            tokenMetadata.put("active", accessToken != null);
 
             return Response.ok(JsonSerialization.writeValueAsBytes(tokenMetadata)).type(MediaType.APPLICATION_JSON_TYPE).build();
         } catch (Exception e) {
@@ -91,6 +70,28 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
         }
     }
 
+    protected AccessToken verifyAccessToken(String token) throws OAuthErrorException, IOException {
+        AccessToken accessToken;
+
+        try {
+            RSATokenVerifier verifier = RSATokenVerifier.create(token)
+                    .realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
+            PublicKey publicKey = session.keys().getRsaPublicKey(realm, verifier.getHeader().getKeyId());
+
+            if (publicKey == null) {
+                return null;
+            }
+
+            accessToken = verifier.publicKey(publicKey).verify().getToken();
+        } catch (VerificationException e) {
+            return null;
+        }
+
+        RealmModel realm = this.session.getContext().getRealm();
+
+        return tokenManager.isTokenValid(session, realm, accessToken) ? accessToken : null;
+    }
+
     protected AccessToken toAccessToken(String token) {
         try {
             RSATokenVerifier verifier = RSATokenVerifier.create(token)
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index cbf93bb..05f9f4c 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -24,8 +24,10 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.OAuthErrorException;
 import org.keycloak.authentication.AuthenticationProcessor;
-import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator;
-import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.authorization.AuthorizationTokenService;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.authorization.util.Tokens;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.broker.provider.ExchangeExternalToken;
 import org.keycloak.broker.provider.IdentityProvider;
@@ -47,7 +49,6 @@ import org.keycloak.jose.jws.JWSInputException;
 import org.keycloak.models.AuthenticationFlowModel;
 import org.keycloak.models.AuthenticatedClientSessionModel;
 import org.keycloak.models.ClientModel;
-import org.keycloak.models.Constants;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
@@ -57,33 +58,31 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.AuthenticationFlowResolver;
-import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.JsonWebToken;
-import org.keycloak.services.ErrorPage;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest.Metadata;
 import org.keycloak.services.CorsErrorResponseException;
-import org.keycloak.services.ErrorResponseException;
 import org.keycloak.services.ServicesLogger;
 import org.keycloak.services.Urls;
+import org.keycloak.services.managers.AppAuthManager;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.AuthenticationSessionManager;
 import org.keycloak.services.managers.BruteForceProtector;
 import org.keycloak.services.managers.ClientManager;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.RealmManager;
-import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.Cors;
 import org.keycloak.services.resources.IdentityBrokerService;
-import org.keycloak.services.resources.LoginActionsService;
 import org.keycloak.services.resources.admin.AdminAuth;
 import org.keycloak.services.resources.admin.permissions.AdminPermissions;
 import org.keycloak.services.validation.Validation;
 import org.keycloak.sessions.AuthenticationSessionModel;
 import org.keycloak.sessions.RootAuthenticationSessionModel;
+import org.keycloak.util.JsonSerialization;
 import org.keycloak.util.TokenUtil;
 import org.keycloak.utils.ProfileHelper;
 
@@ -95,8 +94,9 @@ import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
-import java.net.URI;
+
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -116,7 +116,7 @@ public class TokenEndpoint {
     private Map<String, String> clientAuthAttributes;
 
     private enum Action {
-        AUTHORIZATION_CODE, REFRESH_TOKEN, PASSWORD, CLIENT_CREDENTIALS, TOKEN_EXCHANGE
+        AUTHORIZATION_CODE, REFRESH_TOKEN, PASSWORD, CLIENT_CREDENTIALS, TOKEN_EXCHANGE, PERMISSION
     }
 
     // https://tools.ietf.org/html/rfc7636#section-4.2
@@ -166,7 +166,10 @@ public class TokenEndpoint {
         checkSsl();
         checkRealm();
         checkGrantType();
-        checkClient();
+
+        if (!action.equals(Action.PERMISSION)) {
+            checkClient();
+        }
 
         switch (action) {
             case AUTHORIZATION_CODE:
@@ -179,6 +182,8 @@ public class TokenEndpoint {
                 return clientCredentialsGrant();
             case TOKEN_EXCHANGE:
                 return tokenExchange();
+            case PERMISSION:
+                return permissionGrant();
         }
 
         throw new RuntimeException("Unknown action " + action);
@@ -247,7 +252,9 @@ public class TokenEndpoint {
         } else if (grantType.equals(OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)) {
             event.event(EventType.TOKEN_EXCHANGE);
             action = Action.TOKEN_EXCHANGE;
-
+        } else if (grantType.equals(OAuth2Constants.UMA_GRANT_TYPE)) {
+            event.event(EventType.PERMISSION_TOKEN);
+            action = Action.PERMISSION;
         } else {
             throw new CorsErrorResponseException(cors, Errors.INVALID_REQUEST, "Invalid " + OIDCLoginProtocol.GRANT_TYPE_PARAM, Response.Status.BAD_REQUEST);
         }
@@ -973,6 +980,90 @@ public class TokenEndpoint {
         return user;
     }
 
+    public Response permissionGrant() {
+        event.detail(Details.AUTH_METHOD, "oauth_credentials");
+
+        String accessTokenString = null;
+        String authorizationHeader = headers.getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
+
+        if (authorizationHeader != null && authorizationHeader.toLowerCase().startsWith("bearer")) {
+            accessTokenString = new AppAuthManager().extractAuthorizationHeaderToken(headers);
+        }
+
+        if (accessTokenString != null) {
+            AccessToken accessToken = Tokens.getAccessToken(session);
+
+            if (accessToken == null) {
+                throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Invalid bearer token", Status.UNAUTHORIZED);
+            }
+
+            cors.allowedOrigins(uriInfo, realm.getClientByClientId(accessToken.getIssuedFor()));
+        }
+
+        String claimToken = null;
+
+        // claim_token is optional, if provided we just grab it from the request
+        if (formParams.containsKey("claim_token")) {
+            claimToken = formParams.get("claim_token").get(0);
+        }
+
+        if (accessTokenString == null) {
+            // in case no bearer token is provided, we force client authentication
+            checkClient();
+            // Clients need to authenticate in order to obtain a RPT from the server.
+            // In order to support cases where the client is obtaining permissions on its on behalf, we issue a temporary access token
+            accessTokenString = AccessTokenResponse.class.cast(clientCredentialsGrant().getEntity()).getToken();
+        }
+
+        AuthorizationRequest authorizationRequest = new AuthorizationRequest(formParams.getFirst("ticket"));
+
+        authorizationRequest.setClaimToken(claimToken);
+        authorizationRequest.setClaimTokenFormat(formParams.getFirst("claim_token_format"));
+        authorizationRequest.setPct(formParams.getFirst("pct"));
+        authorizationRequest.setRpt(formParams.getFirst("rpt"));
+        authorizationRequest.setScope(formParams.getFirst("scope"));
+        authorizationRequest.setAudience(formParams.getFirst("audience"));
+        authorizationRequest.setAccessToken(accessTokenString);
+
+        String submitRequest = formParams.getFirst("submit_request");
+
+        authorizationRequest.setSubmitRequest(submitRequest == null ? true : Boolean.valueOf(submitRequest));
+
+        // permissions have a format like RESOURCE#SCOPE1,SCOPE2
+        List<String> permissions = formParams.get("permission");
+
+        if (permissions != null) {
+            for (String permission : permissions) {
+                String[] parts = permission.split("#");
+                String resource = parts[0];
+
+                if (parts.length == 1) {
+                    authorizationRequest.addPermission(resource);
+                } else {
+                    String[] scopes = parts[1].split(",");
+                    authorizationRequest.addPermission(parts[0], scopes);
+                }
+            }
+        }
+
+        Metadata metadata = new Metadata();
+
+        String responseIncludeResourceName = formParams.getFirst("response_include_resource_name");
+
+        if (responseIncludeResourceName != null) {
+            metadata.setIncludeResourceName(Boolean.parseBoolean(responseIncludeResourceName));
+        }
+
+        String responsePermissionsLimit = formParams.getFirst("response_permissions_limit");
+
+        if (responsePermissionsLimit != null) {
+            metadata.setLimit(Integer.parseInt(responsePermissionsLimit));
+        }
+
+        authorizationRequest.setMetadata(metadata);
+
+        return new AuthorizationTokenService(session.getProvider(AuthorizationProvider.class), tokenManager, event, request, cors).authorize(authorizationRequest);
+    }
 
     // https://tools.ietf.org/html/rfc7636#section-4.1
     private boolean isValidPkceCodeVerifier(String codeVerifier) {
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
index 4fc73f7..60970d8 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/installation/KeycloakOIDCClientInstallation.java
@@ -156,7 +156,6 @@ public class KeycloakOIDCClientInstallation implements ClientInstallationProvide
 
             enforcerConfig.setEnforcementMode(null);
             enforcerConfig.setCreateResources(null);
-            enforcerConfig.setOnlineIntrospection(null);
 
             rep.setEnforcerConfig(enforcerConfig);
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
index dd94094..e21f3ad 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
@@ -257,6 +257,10 @@ public class TokenManager {
         validation.clientSession.setTimestamp(currentTime);
         validation.userSession.setLastSessionRefresh(currentTime);
 
+        if (refreshToken.getAuthorization() != null) {
+            validation.newToken.setAuthorization(refreshToken.getAuthorization());
+        }
+
         AccessTokenResponseBuilder responseBuilder = responseBuilder(realm, authorizedClient, event, session, validation.userSession, validation.clientSession)
                 .accessToken(validation.newToken)
                 .generateRefreshToken();
diff --git a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
index bebcb2d..68aca41 100755
--- a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
@@ -63,7 +63,10 @@ public class AppAuthManager extends AuthenticationManager {
     }
 
     public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
-        String tokenString = extractAuthorizationHeaderToken(headers);
+        return authenticateBearerToken(extractAuthorizationHeaderToken(headers), session, realm, uriInfo, connection, headers);
+    }
+
+    public AuthResult authenticateBearerToken(String tokenString, KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
         if (tokenString == null) return null;
         AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, true, true, false, tokenString, headers);
         return authResult;
diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java b/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java
index d3c0260..d316e34 100755
--- a/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java
+++ b/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java
@@ -18,6 +18,13 @@ package org.keycloak.services.resources.account;
 
 import org.jboss.logging.Logger;
 import org.keycloak.authentication.RequiredActionContext;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.PermissionTicketStore;
+import org.keycloak.common.Profile;
+import org.keycloak.common.Profile.Feature;
 import org.keycloak.common.util.Base64Url;
 import org.keycloak.common.util.Time;
 import org.keycloak.common.util.UriUtils;
@@ -47,6 +54,7 @@ import org.keycloak.models.utils.CredentialValidation;
 import org.keycloak.models.utils.FormMessage;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.utils.RedirectUtils;
+import org.keycloak.services.ErrorResponse;
 import org.keycloak.services.ForbiddenException;
 import org.keycloak.services.ServicesLogger;
 import org.keycloak.services.Urls;
@@ -70,6 +78,7 @@ import javax.ws.rs.*;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 
@@ -78,6 +87,9 @@ import java.lang.reflect.Method;
 import java.net.URI;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -155,7 +167,7 @@ public class AccountFormService extends AbstractSecuredLocalService {
             account.setUser(auth.getUser());
         }
 
-        account.setFeatures(realm.isIdentityFederationEnabled(), eventStore != null && realm.isEventsEnabled(), true);
+        account.setFeatures(realm.isIdentityFederationEnabled(), eventStore != null && realm.isEventsEnabled(), true, Profile.isFeatureEnabled(Feature.AUTHORIZATION));
     }
 
     public static UriBuilder accountServiceBaseUrl(UriInfo uriInfo) {
@@ -684,6 +696,193 @@ public class AccountFormService extends AbstractSecuredLocalService {
         }
     }
 
+    @Path("resource")
+    @GET
+    public Response resourcesPage(@QueryParam("resource_id") String resourceId) {
+        return forwardToPage("resources", AccountPages.RESOURCES);
+    }
+
+    @Path("resource/{resource_id}")
+    @GET
+    public Response resourceDetailPage(@PathParam("resource_id") String resourceId) {
+        return forwardToPage("resource-detail", AccountPages.RESOURCE_DETAIL);
+    }
+
+    @Path("resource/{resource_id}/grant")
+    @POST
+    public Response grantPermission(@PathParam("resource_id") String resourceId, @FormParam("action") String action, @FormParam("permission_id") String[] permissionId, @FormParam("requester") String requester) {
+        AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
+        PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
+        Resource resource = authorization.getStoreFactory().getResourceStore().findById(resourceId, null);
+
+        if (resource == null) {
+            return ErrorResponse.error("Invalid resource", Response.Status.BAD_REQUEST);
+        }
+
+        if (action == null) {
+            return ErrorResponse.error("Invalid action", Response.Status.BAD_REQUEST);
+        }
+
+        boolean isGrant = "grant".equals(action);
+        boolean isDeny = "deny".equals(action);
+        boolean isRevoke = "revoke".equals(action);
+
+        Map<String, String> filters = new HashMap<>();
+
+        filters.put(PermissionTicket.RESOURCE, resource.getId());
+        filters.put(PermissionTicket.REQUESTER, session.users().getUserByUsername(requester, realm).getId());
+
+        if (isRevoke) {
+            filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
+        } else {
+            filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
+        }
+
+        List<PermissionTicket> tickets = ticketStore.find(filters, resource.getResourceServer().getId(), -1, -1);
+        Iterator<PermissionTicket> iterator = tickets.iterator();
+
+        while (iterator.hasNext()) {
+            PermissionTicket ticket = iterator.next();
+
+            if (isGrant) {
+                if (permissionId != null && permissionId.length > 0 && !Arrays.asList(permissionId).contains(ticket.getId())) {
+                    continue;
+                }
+            }
+
+            if (isGrant && !ticket.isGranted()) {
+                ticket.setGrantedTimestamp(System.currentTimeMillis());
+                iterator.remove();
+            } else if (isDeny || isRevoke) {
+                if (permissionId != null && permissionId.length > 0 && Arrays.asList(permissionId).contains(ticket.getId())) {
+                    iterator.remove();
+                }
+            }
+        }
+
+        for (PermissionTicket ticket : tickets) {
+            ticketStore.delete(ticket.getId());
+        }
+
+        if (isRevoke) {
+            return forwardToPage("resource-detail", AccountPages.RESOURCE_DETAIL);
+        }
+
+        return forwardToPage("resources", AccountPages.RESOURCES);
+    }
+
+    @Path("resource/{resource_id}/share")
+    @POST
+    public Response shareResource(@PathParam("resource_id") String resourceId, @FormParam("user_id") String[] userIds, @FormParam("scope_id") String[] scopes) {
+        AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
+        PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
+        Resource resource = authorization.getStoreFactory().getResourceStore().findById(resourceId, null);
+
+        if (resource == null) {
+            return ErrorResponse.error("Invalid resource", Response.Status.BAD_REQUEST);
+        }
+
+        if (userIds == null || userIds.length == 0) {
+            return account.setError(Status.BAD_REQUEST, Messages.MISSING_PASSWORD).createResponse(AccountPages.PASSWORD);
+        }
+
+        for (String id : userIds) {
+            UserModel user = session.users().getUserById(id, realm);
+
+            if (user == null) {
+                user = session.users().getUserByUsername(id, realm);
+            }
+
+            if (user == null) {
+                user = session.users().getUserByEmail(id, realm);
+            }
+
+            if (user == null) {
+                return account.setError(Status.BAD_REQUEST, Messages.INVALID_USER).createResponse(AccountPages.RESOURCE_DETAIL);
+            }
+
+            Map<String, String> filters = new HashMap<>();
+
+            filters.put(PermissionTicket.RESOURCE, resource.getId());
+            filters.put(PermissionTicket.OWNER, auth.getUser().getId());
+            filters.put(PermissionTicket.REQUESTER, user.getId());
+
+            List<PermissionTicket> tickets = ticketStore.find(filters, resource.getResourceServer().getId(), -1, -1);
+
+            if (tickets.isEmpty()) {
+                if (scopes != null && scopes.length > 0) {
+                    for (String scope : scopes) {
+                        PermissionTicket ticket = ticketStore.create(resourceId, scope, user.getId(), resource.getResourceServer());
+                        ticket.setGrantedTimestamp(System.currentTimeMillis());
+                    }
+                } else {
+                    if (resource.getScopes().isEmpty()) {
+                        PermissionTicket ticket = ticketStore.create(resourceId, null, user.getId(), resource.getResourceServer());
+                        ticket.setGrantedTimestamp(System.currentTimeMillis());
+                    } else {
+                        for (Scope scope : resource.getScopes()) {
+                            PermissionTicket ticket = ticketStore.create(resourceId, scope.getId(), user.getId(), resource.getResourceServer());
+                            ticket.setGrantedTimestamp(System.currentTimeMillis());
+                        }
+                    }
+                }
+            } else if (scopes != null && scopes.length > 0) {
+                List<String> grantScopes = new ArrayList<>(Arrays.asList(scopes));
+
+                for (PermissionTicket ticket : tickets) {
+                    Scope scope = ticket.getScope();
+
+                    if (scope != null) {
+                        grantScopes.remove(scope.getId());
+                    }
+                }
+
+                for (String grantScope : grantScopes) {
+                    PermissionTicket ticket = ticketStore.create(resourceId, grantScope, user.getId(), resource.getResourceServer());
+                    ticket.setGrantedTimestamp(System.currentTimeMillis());
+                }
+            }
+        }
+
+        return forwardToPage("resource-detail", AccountPages.RESOURCE_DETAIL);
+    }
+
+    @Path("resource")
+    @POST
+    public Response processResourceActions(@FormParam("resource_id") String[] resourceIds, @FormParam("action") String action) {
+        AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
+        PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
+
+        if (action == null) {
+            return ErrorResponse.error("Invalid action", Response.Status.BAD_REQUEST);
+        }
+
+        for (String resourceId : resourceIds) {
+            Resource resource = authorization.getStoreFactory().getResourceStore().findById(resourceId, null);
+
+            if (resource == null) {
+                return ErrorResponse.error("Invalid resource", Response.Status.BAD_REQUEST);
+            }
+
+            HashMap<String, String> filters = new HashMap<>();
+
+            filters.put(PermissionTicket.REQUESTER, auth.getUser().getId());
+            filters.put(PermissionTicket.RESOURCE, resource.getId());
+
+            if ("cancel".equals(action)) {
+                filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
+            } else if ("cancelRequest".equals(action)) {
+                filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
+            }
+
+            for (PermissionTicket ticket : ticketStore.find(filters, resource.getResourceServer().getId(), -1, -1)) {
+                ticketStore.delete(ticket.getId());
+            }
+        }
+
+        return forwardToPage("authorization", AccountPages.RESOURCES);
+    }
+
     public static UriBuilder loginRedirectUrl(UriBuilder base) {
         return RealmsResource.accountUrl(base).path(AccountFormService.class, "loginRedirect");
     }
diff --git a/services/src/main/java/org/keycloak/services/Urls.java b/services/src/main/java/org/keycloak/services/Urls.java
index c2d4929..ac45614 100755
--- a/services/src/main/java/org/keycloak/services/Urls.java
+++ b/services/src/main/java/org/keycloak/services/Urls.java
@@ -143,6 +143,22 @@ public class Urls {
         return realmLogout(baseUri).queryParam("redirect_uri", redirectUri).build(realmName);
     }
 
+    public static URI accountResourcesPage(URI baseUri, String realmName) {
+        return accountBase(baseUri).path(AccountFormService.class, "resourcesPage").build(realmName);
+    }
+
+    public static URI accountResourceDetailPage(String resourceId, URI baseUri, String realmName) {
+        return accountBase(baseUri).path(AccountFormService.class, "resourceDetailPage").build(realmName, resourceId);
+    }
+
+    public static URI accountResourceGrant(String resourceId, URI baseUri, String realmName) {
+        return accountBase(baseUri).path(AccountFormService.class, "grantPermission").build(realmName, resourceId);
+    }
+
+    public static URI accountResourceShare(String resourceId, URI baseUri, String realmName) {
+        return accountBase(baseUri).path(AccountFormService.class, "shareResource").build(realmName, resourceId);
+    }
+
     public static URI loginActionUpdatePassword(URI baseUri, String realmName) {
         return loginActionsBase(baseUri).path(LoginActionsService.class, "updatePassword").build(realmName);
     }
diff --git a/services/src/main/java/org/keycloak/theme/FreeMarkerUtil.java b/services/src/main/java/org/keycloak/theme/FreeMarkerUtil.java
index a8b1784..7415bf1 100755
--- a/services/src/main/java/org/keycloak/theme/FreeMarkerUtil.java
+++ b/services/src/main/java/org/keycloak/theme/FreeMarkerUtil.java
@@ -45,6 +45,7 @@ public class FreeMarkerUtil {
     public String processTemplate(Object data, String templateName, Theme theme) throws FreeMarkerException {
         try {
             Template template;
+            cache = null;
             if (cache != null) {
                 String key = theme.getName() + "/" + templateName;
                 template = cache.get(key);
diff --git a/testsuite/integration-arquillian/test-apps/hello-world-authz-service/src/main/webapp/index.jsp b/testsuite/integration-arquillian/test-apps/hello-world-authz-service/src/main/webapp/index.jsp
index 0aea6b0..c511b2d 100644
--- a/testsuite/integration-arquillian/test-apps/hello-world-authz-service/src/main/webapp/index.jsp
+++ b/testsuite/integration-arquillian/test-apps/hello-world-authz-service/src/main/webapp/index.jsp
@@ -38,8 +38,8 @@
         for (Permission permission : authzContext.getPermissions()) {
     %>
     <li>
-        <p>Resource: <%= permission.getResourceSetName() %></p>
-        <p>ID: <%= permission.getResourceSetId() %></p>
+        <p>Resource: <%= permission.getResourceName() %></p>
+        <p>ID: <%= permission.getResourceId() %></p>
     </li>
     <%
         }
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/index.html b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/index.html
index 077f9dc..692e05d 100755
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/index.html
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/index.html
@@ -23,7 +23,8 @@
 <a href data-ng-click="showRpt()">Show Requesting Party Token </a> | 
 <a href data-ng-click="showAccessToken()">Show Access Token </a> | 
 <a id="entitlements" href data-ng-click="requestEntitlements()">Request Entitlements</a> | 
-<a id="entitlement" href data-ng-click="requestEntitlement()">Request Entitlement</a> | 
+<a id="entitlement" href data-ng-click="requestEntitlement()">Request Entitlement</a> |
+<a id="my-account" href ng-click="Identity.account()">My Account</a> |
 <a href="" ng-click="Identity.logout()">Sign Out</a>
 
 <div id="content-area" class="col-md-9" role="main">
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/app.js b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/app.js
index ecdbf0b..3d2ed43 100755
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/app.js
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/app.js
@@ -60,6 +60,12 @@ module.controller('GlobalCtrl', function ($scope, $http, $route, $location, Albu
         $http.get(apiUrl + '/scope-all').success(function (data) {
         });
     }
+
+    $scope.getAllResources = function () {
+        Album.getAll(function (albums) {
+            $scope.albums = albums;
+        });
+    }
 });
 
 module.controller('TokenCtrl', function ($scope, Identity) {
@@ -78,8 +84,13 @@ module.controller('TokenCtrl', function ($scope, Identity) {
     }
     
     $scope.requestEntitlement = function () {
-        var param={"permissions" : [{"resource_set_name" : "Album Resource"}]};
-        Identity.authorization.entitlement('photoz-restful-api', param).then(function (rpt) {
+        Identity.authorization.entitlement('photoz-restful-api', {
+            "permissions": [
+                {
+                    "id" : "Album Resource"
+                }
+            ]
+        }).then(function (rpt) {
             document.getElementById("output").innerHTML = JSON.stringify(jwt_decode(rpt), null, '  ');
         });
     }
@@ -99,6 +110,14 @@ module.controller('AlbumCtrl', function ($scope, $http, $routeParams, $location,
         });
     };
 
+    $scope.createManaged = function () {
+        $scope.album.userManaged = true;
+        var newAlbum = new Album($scope.album);
+        newAlbum.$save({}, function (data) {
+            $location.path('/');
+        });
+    };
+
     $scope.createWithInvalidUser = function () {
         var newAlbum = new Album($scope.album);
         newAlbum.$save({user: 'invalidUser'}, function (data) {
@@ -127,7 +146,9 @@ module.controller('AdminAlbumCtrl', function ($scope, $http, $route, $location, 
 });
 
 module.factory('Album', ['$resource', function ($resource) {
-    return $resource(apiUrl + '/album/:id');
+    return $resource(apiUrl + '/album/:id', {id: '@id'}, {
+        getAll: {method: 'GET', params: {getAll: true}, isArray: true}
+    });
 }]);
 
 module.factory('Profile', ['$resource', function ($resource) {
@@ -162,11 +183,46 @@ module.factory('authInterceptor', function ($q, $injector, $timeout, Identity) {
                 }
 
                 if (rejection.config.url.indexOf('/authorize') == -1 && retry) {
-                    var deferred = $q.defer();
-
                     // here is the authorization logic, which tries to obtain an authorization token from the server in case the resource server
                     // returns a 403 or 401.
-                    Identity.authorization.authorize(rejection.headers('WWW-Authenticate')).then(function (rpt) {
+                    var wwwAuthenticateHeader = rejection.headers('WWW-Authenticate');
+
+                    // when using UMA, a WWW-Authenticate header should be returned by the resource server
+                    if (!wwwAuthenticateHeader) {
+                        return $q.reject(rejection);
+                    }
+
+                    // when using UMA, a WWW-Authenticate header should contain UMA data
+                    if (wwwAuthenticateHeader.indexOf('UMA') == -1) {
+                        return $q.reject(rejection);
+                    }
+
+                    var deferred = $q.defer();
+
+                    var params = wwwAuthenticateHeader.split(',');
+                    var ticket;
+
+                    // try to extract the permission ticket from the WWW-Authenticate header
+                    for (i = 0; i < params.length; i++) {
+                        var param = params[i].split('=');
+
+                        if (param[0] == 'ticket') {
+                            ticket = param[1].substring(1, param[1].length - 1).trim();
+                            break;
+                        }
+                    }
+
+                    // a permission ticket must exist in order to send an authorization request
+                    if (!ticket) {
+                        return $q.reject(rejection);
+                    }
+
+                    // prepare a authorization request with the permission ticket
+                    var authorizationRequest = {};
+                    authorizationRequest.ticket = ticket;
+
+                    // send the authorization request, if successful retry the request
+                    Identity.authorization.authorize(authorizationRequest).then(function (rpt) {
                         deferred.resolve(rejection);
                     }, function () {
                         document.getElementById("output").innerHTML = 'You can not access or perform the requested operation on this resource.';
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/identity.js b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/identity.js
index 9a018e4..3a892b5 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/identity.js
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/js/identity.js
@@ -26,6 +26,7 @@
 
         this.claims = {};
         this.claims.name = keycloak.idTokenParsed.name;
+        this.claims.sub = keycloak.idTokenParsed.sub;
 
         this.authc = {};
         this.authc.token = keycloak.token;
@@ -45,6 +46,10 @@
             return this.hasRole("admin");
         };
 
+        this.account = function () {
+            keycloak.accountManagement();
+        }
+
         this.authorization = new KeycloakAuthorization(keycloak);
     }
 
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/album/create.html b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/album/create.html
index 403adfa..ab65313 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/album/create.html
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/album/create.html
@@ -4,5 +4,6 @@
     Name: <input type="text" id="album.name" ng-model="album.name"/>
 
     <button ng-click="create()" id="save-album">Save</button>
+    <button ng-click="createManaged()" id="save-managed-album">Save Managed</button>
     <button ng-click="createWithInvalidUser()" id="save-album-invalid">Save with invalid user</button>
 </form>
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/home.html b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/home.html
index 788763b..b4208bb 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/home.html
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-html5-client/src/main/webapp/partials/home.html
@@ -3,7 +3,7 @@
 <hr/>
 <br/>
 <div data-ng-show="!Identity.isAdmin()">
-<a href="#/album/create" id="create-album">Create Album</a> | <a href="#/profile">My Profile</a> | <a href="#" id="requestPathWithAnyProtectedScope" ng-click="requestPathWithAnyProtectedScope()">Any Scope Access</a> | <a href="#" id="requestPathWithAllProtectedScope" ng-click="requestPathWithAllProtectedScope()">All Scope Access</a>
+<a href="#/album/create" id="create-album">Create Album</a> | <a href="#/profile">My Profile</a> | <a href="#" id="requestPathWithAnyProtectedScope" ng-click="requestPathWithAnyProtectedScope()">Any Scope Access</a> | <a href="#" id="requestPathWithAllProtectedScope" ng-click="requestPathWithAllProtectedScope()">All Scope Access</a> | <a href id="get-all-resources" ng-click="getAllResources()">Get All Resources</a>
 <br/>
 <br/>
 <span data-ng-show="albums.length == 0" id="resource-list-empty">You don't have any albums, yet.</span>
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json
index 7ec7e02..e44fec3 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json
@@ -1,9 +1,11 @@
 {
   "realm": "photoz",
   "enabled": true,
+  "userManagedAccessAllowed": true,
   "sslRequired": "external",
   "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
   "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+  "accessTokenLifespan": 100000,
   "requiredCredentials": [
     "password"
   ],
@@ -26,6 +28,9 @@
       "clientRoles": {
         "photoz-restful-api": [
           "manage-albums"
+        ],
+        "account": [
+          "manage-account"
         ]
       }
     },
@@ -47,6 +52,32 @@
       "clientRoles": {
         "photoz-restful-api": [
           "manage-albums"
+        ],
+        "account": [
+          "manage-account"
+        ]
+      }
+    },
+    {
+      "username": "pedroigor",
+      "enabled": true,
+      "email": "pedroigor@keycloak.org",
+      "firstName": "Pedro Igor",
+      "credentials": [
+        {
+          "type": "password",
+          "value": "pedroigor"
+        }
+      ],
+      "realmRoles": [
+        "user", "uma_authorization"
+      ],
+      "clientRoles": {
+        "photoz-restful-api": [
+          "manage-albums"
+        ],
+        "account": [
+          "manage-account"
         ]
       }
     },
@@ -71,6 +102,9 @@
         ],
         "photoz-restful-api": [
           "manage-albums"
+        ],
+        "account": [
+          "manage-account"
         ]
       }
     },
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
index 77bb3b6..22b5388 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
@@ -37,7 +37,7 @@ import java.util.List;
 @Path("/admin/album")
 public class AdminAlbumService {
 
-    public static final String SCOPE_ADMIN_ALBUM_MANAGE = "urn:photoz.com:scopes:album:admin:manage";
+    public static final String SCOPE_ADMIN_ALBUM_MANAGE = "admin:manage";
 
     @Inject
     private EntityManager entityManager;
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
index 7969492..9070416 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/AlbumService.java
@@ -39,8 +39,8 @@ public class AlbumService {
 
     private static volatile long nextId = 0;
 
-    public static final String SCOPE_ALBUM_VIEW = "urn:photoz.com:scopes:album:view";
-    public static final String SCOPE_ALBUM_DELETE = "urn:photoz.com:scopes:album:delete";
+    public static final String SCOPE_ALBUM_VIEW = "album:view";
+    public static final String SCOPE_ALBUM_DELETE = "album:delete";
 
     @Inject
     private EntityManager entityManager;
@@ -91,8 +91,12 @@ public class AlbumService {
 
     @GET
     @Produces("application/json")
-    public Response findAll() {
-        return Response.ok(this.entityManager.createQuery("from Album where userId = '" + request.getUserPrincipal().getName() + "'").getResultList()).build();
+    public Response findAll(@QueryParam("getAll") Boolean getAll) {
+        if (getAll != null && getAll) {
+            return Response.ok(this.entityManager.createQuery("from Album").getResultList()).build();
+        } else {
+            return Response.ok(this.entityManager.createQuery("from Album where userId = '" + request.getUserPrincipal().getName() + "'").getResultList()).build();
+        }
     }
 
     @GET
@@ -119,6 +123,10 @@ public class AlbumService {
 
             albumResource.setOwner(album.getUserId());
 
+            if (album.isUserManaged()) {
+                albumResource.setOwnerManagedAccess(true);
+            }
+
             getAuthzClient().protection().resource().create(albumResource);
         } catch (Exception e) {
             throw new RuntimeException("Could not register protected resource.", e);
@@ -130,13 +138,13 @@ public class AlbumService {
 
         try {
             ProtectionResource protection = getAuthzClient().protection();
-            Set<String> search = protection.resource().findByFilter("uri=" + uri);
+            List<ResourceRepresentation> search = protection.resource().findByUri(uri);
 
             if (search.isEmpty()) {
                 throw new RuntimeException("Could not find protected resource with URI [" + uri + "]");
             }
 
-            protection.resource().delete(search.iterator().next());
+            protection.resource().delete(search.get(0).getId());
         } catch (Exception e) {
             throw new RuntimeException("Could not search protected resource.", e);
         }
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
index 92e300d..6e3e3b0 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/album/ProfileService.java
@@ -34,7 +34,7 @@ import java.util.List;
 @Path("/profile")
 public class ProfileService {
 
-    private static final String PROFILE_VIEW = "urn:photoz.com:scopes:profile:view";
+    private static final String PROFILE_VIEW = "profile:view";
 
     @Inject
     private EntityManager entityManager;
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java
index cc8bea2..f887e2a 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/entity/Album.java
@@ -24,6 +24,7 @@ import javax.persistence.GeneratedValue;
 import javax.persistence.Id;
 import javax.persistence.OneToMany;
 import javax.persistence.GenerationType;
+import javax.persistence.Transient;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -45,6 +46,9 @@ public class Album {
     @Column(nullable = false)
     private String userId;
 
+    @Transient
+    private boolean userManaged = false;
+
     public Long getId() {
         return this.id;
     }
@@ -76,4 +80,12 @@ public class Album {
     public String getUserId() {
         return this.userId;
     }
+
+    public boolean isUserManaged() {
+        return userManaged;
+    }
+
+    public void setUserManaged(boolean userManaged) {
+        this.userManaged = userManaged;
+    }
 }
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
index f3db78d..a0f8711 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/webapp/WEB-INF/keycloak.json
@@ -16,32 +16,20 @@
     }
   },
   "policy-enforcer": {
-    "user-managed-access" : {},
+    "enforcement-mode": "PERMISSIVE",
+    "user-managed-access": {},
     "paths": [
       {
-        "path" : "/album/*",
-        "methods" : [
-          {
-            "method": "POST",
-            "scopes" : ["urn:photoz.com:scopes:album:create"]
-          },
-          {
-            "method": "GET",
-            "scopes" : ["urn:photoz.com:scopes:album:view"]
-          }
-        ]
-      },
-      {
         "name" : "Album Resource",
         "path" : "/album/{id}",
         "methods" : [
           {
             "method": "DELETE",
-            "scopes" : ["urn:photoz.com:scopes:album:delete"]
+            "scopes" : ["album:delete"]
           },
           {
             "method": "GET",
-            "scopes" : ["urn:photoz.com:scopes:album:view"]
+            "scopes" : ["album:view"]
           }
         ]
       },
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json
index 0b621f5..7327cba 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api-authz-service.json
@@ -8,7 +8,7 @@
       "type": "http://photoz.com/profile",
       "scopes": [
         {
-          "name": "urn:photoz.com:scopes:profile:view"
+          "name": "profile:view"
         }
       ]
     },
@@ -18,13 +18,13 @@
       "type": "http://photoz.com/album",
       "scopes": [
         {
-          "name": "urn:photoz.com:scopes:album:view"
+          "name": "album:view"
         },
         {
-          "name": "urn:photoz.com:scopes:album:delete"
+          "name": "album:delete"
         },
         {
-          "name": "urn:photoz.com:scopes:album:create"
+          "name": "album:create"
         }
       ]
     },
@@ -34,7 +34,7 @@
       "type": "http://photoz.com/admin",
       "scopes": [
         {
-          "name": "urn:photoz.com:scopes:album:admin:manage"
+          "name": "admin:manage"
         }
       ]
     },
@@ -165,7 +165,7 @@
       "decisionStrategy": "UNANIMOUS",
       "config": {
         "applyPolicies": "[\"Only From @keycloak.org or Admin\"]",
-        "scopes": "[\"urn:photoz.com:scopes:profile:view\"]"
+        "scopes": "[\"profile:view\"]"
       }
     },
     {
@@ -176,7 +176,18 @@
       "decisionStrategy": "UNANIMOUS",
       "config": {
         "applyPolicies": "[\"Only Owner and Administrators Policy\"]",
-        "scopes": "[\"urn:photoz.com:scopes:album:delete\"]"
+        "scopes": "[\"album:delete\"]"
+      }
+    },
+    {
+      "name": "View Album Permission",
+      "description": "A policy that only allows the owner to view his albums.",
+      "type": "scope",
+      "logic": "POSITIVE",
+      "decisionStrategy": "UNANIMOUS",
+      "config": {
+        "applyPolicies": "[\"Only Owner and Administrators Policy\"]",
+        "scopes": "[\"album:view\"]"
       }
     },
     {
@@ -213,19 +224,19 @@
   ],
   "scopes": [
     {
-      "name": "urn:photoz.com:scopes:profile:view"
+      "name": "profile:view"
     },
     {
-      "name": "urn:photoz.com:scopes:album:view"
+      "name": "album:view"
     },
     {
-      "name": "urn:photoz.com:scopes:album:create"
+      "name": "album:create"
     },
     {
-      "name": "urn:photoz.com:scopes:album:delete"
+      "name": "album:delete"
     },
     {
-      "name": "urn:photoz.com:scopes:album:admin:manage"
+      "name": "admin:manage"
     }
   ]
 }
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/index.jsp b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/index.jsp
index 3fbfca2..345a69d 100755
--- a/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/index.jsp
+++ b/testsuite/integration-arquillian/test-apps/servlet-authz/src/main/webapp/index.jsp
@@ -23,8 +23,8 @@
             for (Permission permission : authzContext.getPermissions()) {
         %>
         <li>
-            <p>Resource: <%= permission.getResourceSetName() %></p>
-            <p>ID: <%= permission.getResourceSetId() %></p>
+            <p>Resource: <%= permission.getResourceName() %></p>
+            <p>ID: <%= permission.getResourceId() %></p>
             <p>Scopes: <%= permission.getScopes() %></p>
         </li>
         <%
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
index 27df535..1f61268 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/PhotozClientAuthzTestApp.java
@@ -27,6 +27,7 @@ import org.keycloak.testsuite.util.URLUtils;
 import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
 
 import java.net.URL;
 
@@ -41,7 +42,7 @@ import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
 public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
 
     public static final String DEPLOYMENT_NAME = "photoz-html5-client";
-    public static final int WAIT_AFTER_OPERATION = 2000;
+    public static final int WAIT_AFTER_OPERATION = 1000;
 
     @ArquillianResource
     @OperateOnDeployment(DEPLOYMENT_NAME)
@@ -62,11 +63,22 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
     @FindBy(id = "entitlements")
     private WebElement entitlements;
 
+    @FindBy(id = "get-all-resources")
+    private WebElement viewAllAlbums;
+
     @FindBy(id = "output")
     private WebElement output;
-    
+
     public void createAlbum(String name) {
-        createAlbum(name, "save-album");
+        createAlbum(name, false);
+    }
+
+    public void createAlbum(String name, boolean managed) {
+        if (managed) {
+            createAlbum(name, "save-managed-album");
+        } else {
+            createAlbum(name, "save-album");
+        }
     }
 
     public void createAlbum(String name, String buttonId) {
@@ -118,13 +130,15 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
     }
 
     public void login(String username, String password, String... scopes) throws InterruptedException {
-        if (this.driver.getCurrentUrl().startsWith(getInjectedUrl().toString())) {
-            Thread.sleep(2000);
+        String currentUrl = this.driver.getCurrentUrl();
+
+        if (currentUrl.startsWith(getInjectedUrl().toString())) {
+            Thread.sleep(1000);
             logOut();
             navigateTo();
         }
 
-        Thread.sleep(2000);
+        Thread.sleep(1000);
 
         if (scopes.length > 0) {
             StringBuilder scopesValue = new StringBuilder();
@@ -136,7 +150,21 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
                 scopesValue.append(scope);
             }
 
-            URLUtils.navigateToUri(this.driver.getCurrentUrl() + " " + scopesValue, true);
+            scopesValue.append(" openid");
+
+            int scopeIndex = currentUrl.indexOf("scope");
+
+            if (scopeIndex != -1) {
+                StringBuilder url = new StringBuilder(currentUrl);
+
+                url.delete(scopeIndex, currentUrl.indexOf('&', scopeIndex));
+
+                url.append("&").append("scope=").append(scopesValue);
+
+                currentUrl = url.toString();
+            }
+
+            URLUtils.navigateToUri(currentUrl + " " + scopesValue, true);
         }
 
         this.loginPage.form().login(username, password);
@@ -154,12 +182,82 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
     }
 
     public void viewAlbum(String name) throws InterruptedException {
+        viewAlbum(name, true);
+    }
+
+    public void viewAllAlbums() {
+        viewAllAlbums.click();
+        pause(WAIT_AFTER_OPERATION);
+    }
+
+    public void viewAlbum(String name, boolean refresh) throws InterruptedException {
         this.driver.findElement(By.xpath("//a[text() = '" + name + "']")).click();
         waitForPageToLoad();
-        driver.navigate().refresh(); // This is sometimes necessary for loading the new policy settings
+        if (refresh) {
+            driver.navigate().refresh(); // This is sometimes necessary for loading the new policy settings
+        }
+        pause(WAIT_AFTER_OPERATION);
+    }
+
+    public void accountPage() throws InterruptedException {
+        navigateTo();
+        this.driver.findElement(By.id("my-account")).click();
         pause(WAIT_AFTER_OPERATION);
     }
 
+    public void accountMyResources() throws InterruptedException {
+        accountPage();
+        this.driver.findElement(By.xpath("//a[text() = 'My Resources']")).click();
+        waitForPageToLoad();
+        pause(WAIT_AFTER_OPERATION);
+    }
+
+    public void accountMyResource(String name) throws InterruptedException {
+        accountMyResources();
+        this.driver.findElement(By.id("detail-" + name)).click();
+        waitForPageToLoad();
+        pause(WAIT_AFTER_OPERATION);
+    }
+
+    public void accountGrantResource(String name, String requester) throws InterruptedException {
+        accountMyResources();
+        this.driver.findElement(By.id("grant-" + name + "-" + requester)).click();
+        waitForPageToLoad();
+    }
+
+    public void accountGrantRemoveScope(String name, String requester, String scope) throws InterruptedException {
+        accountMyResources();
+        this.driver.findElement(By.id("grant-remove-scope-" + name + "-" + requester + "-" + scope)).click();
+        waitForPageToLoad();
+    }
+
+    public void accountRevokeResource(String name, String requester) throws InterruptedException {
+        accountMyResource(name);
+        this.driver.findElement(By.id("revoke-" + name + "-" + requester)).click();
+        waitForPageToLoad();
+    }
+
+    public void accountShareResource(String name, String user) throws InterruptedException {
+        accountMyResource(name);
+        this.driver.findElement(By.id("user_id")).sendKeys(user);
+        this.driver.findElement(By.id("share-button")).click();
+        waitForPageToLoad();
+    }
+
+    public void accountShareRemoveScope(String name, String user, String scope) throws InterruptedException {
+        accountMyResource(name);
+        this.driver.findElement(By.id("user_id")).sendKeys(user);
+        this.driver.findElement(By.id("share-remove-scope-" + name + "-" + scope)).click();
+        this.driver.findElement(By.id("share-button")).click();
+        waitForPageToLoad();
+    }
+
+    public void accountDenyResource(String name) throws InterruptedException {
+        accountMyResource(name);
+        this.driver.findElement(By.xpath("//a[text() = 'Deny']")).click();
+        waitForPageToLoad();
+    }
+
     public void requestResourceProtectedAnyScope() throws InterruptedException {
         navigateTo();
         this.driver.findElement(By.id("requestPathWithAnyProtectedScope")).click();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java
index ba670e3..e1ad409 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPermissiveModeAdapterTest.java
@@ -33,7 +33,7 @@ public abstract class AbstractPermissiveModeAdapterTest extends AbstractServletA
     @Deployment(name = RESOURCE_SERVER_ID, managed = false)
     public static WebArchive deployment() throws IOException {
         return exampleDeployment(RESOURCE_SERVER_ID)
-                .addAsWebInfResource(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-realm.json"), "keycloak.-permissive-authz-service.json");
+                .addAsWebInfResource(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-realm.json"), "keycloak-permissive-authz-service.json");
     }
 
     @Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
index edd264c..a7cded4 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
@@ -16,6 +16,24 @@
  */
 package org.keycloak.testsuite.adapter.example.authorization;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.util.IOUtil.loadJson;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
 import org.jboss.arquillian.container.test.api.Deployer;
 import org.jboss.arquillian.container.test.api.Deployment;
 import org.jboss.arquillian.graphene.page.Page;
@@ -43,24 +61,6 @@ import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
 import org.keycloak.testsuite.adapter.page.PhotozClientAuthzTestApp;
 import org.keycloak.util.JsonSerialization;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.keycloak.testsuite.util.IOUtil.loadJson;
-import static org.keycloak.testsuite.util.IOUtil.loadRealm;
-import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -358,6 +358,9 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
 
             loginToClientPage("alice", "alice");
             assertFalse(this.clientPage.wasDenied());
+            this.clientPage.createAlbum("Alice Family Album");
+            this.clientPage.viewAlbum("Alice Family Album");
+            assertFalse(this.clientPage.wasDenied());
 
             UsersResource usersResource = realmsResouce().realm(REALM_NAME).users();
             List<UserRepresentation> users = usersResource.search("alice", null, null, null, null, null);
@@ -380,9 +383,11 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
             roleResource.update(roleRepresentation);
 
             loginToClientPage("alice", "alice");
+            this.clientPage.viewAlbum("Alice Family Album");
             assertTrue(this.clientPage.wasDenied());
 
             loginToClientPage("alice", "alice", RESOURCE_SERVER_ID + "/manage-albums");
+            this.clientPage.viewAlbum("Alice Family Album", false);
             assertFalse(this.clientPage.wasDenied());
         } finally {
             this.deployer.undeploy(RESOURCE_SERVER_ID);
@@ -398,6 +403,10 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
 
             assertFalse(this.clientPage.wasDenied());
 
+            this.clientPage.createAlbum("Alice Family Album");
+            this.clientPage.viewAlbum("Alice Family Album");
+            assertFalse(this.clientPage.wasDenied());
+
             UsersResource usersResource = realmsResouce().realm(REALM_NAME).users();
             List<UserRepresentation> users = usersResource.search("alice", null, null, null, null, null);
 
@@ -419,6 +428,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
             manageAlbumRole.update(roleRepresentation);
 
             loginToClientPage("alice", "alice");
+            this.clientPage.viewAlbum("Alice Family Album");
             assertTrue(this.clientPage.wasDenied());
 
             for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
@@ -438,6 +448,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
             }
 
             loginToClientPage("alice", "alice");
+            this.clientPage.viewAlbum("Alice Family Album");
             assertFalse(this.clientPage.wasDenied());
         } finally {
             this.deployer.undeploy(RESOURCE_SERVER_ID);
@@ -589,7 +600,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
 
             resourcesResource.resources().forEach(resource -> {
                 if (resource.getName().equals(resourceName)) {
-                    resource.setScopes(resource.getScopes().stream().filter(scope -> !scope.getName().equals("urn:photoz.com:scopes:album:view")).collect(Collectors.toSet()));
+                    resource.setScopes(resource.getScopes().stream().filter(scope -> !scope.getName().equals("album:view")).collect(Collectors.toSet()));
                     resourcesResource.resource(resource.getId()).update(resource);
                 }
             });
@@ -631,12 +642,12 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
             loginToClientPage("admin", "admin");
 
             clientPage.requestEntitlements();
-            assertTrue(driver.getPageSource().contains("urn:photoz.com:scopes:album:admin:manage"));
+            assertTrue(driver.getPageSource().contains("admin:manage"));
             
             clientPage.requestEntitlement();
             String pageSource = driver.getPageSource();
-            assertTrue(pageSource.contains("urn:photoz.com:scopes:album:view"));
-            assertFalse(pageSource.contains("urn:photoz.com:scopes:album:admin:manage"));
+            assertTrue(pageSource.contains("album:view"));
+            assertTrue(pageSource.contains("album:delete"));
         } finally {
             this.deployer.undeploy(RESOURCE_SERVER_ID);
         }
@@ -656,6 +667,105 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
         }
     }
 
+    @Test
+    public void testRequestResourceToOwner() throws Exception {
+        try {
+            this.deployer.deploy(RESOURCE_SERVER_ID);
+            loginToClientPage("alice", "alice");
+            this.clientPage.createAlbum("Alice-Family-Album", true);
+
+            loginToClientPage("jdoe", "jdoe");
+            this.clientPage.viewAllAlbums();
+            this.clientPage.viewAlbum("Alice-Family-Album");
+            assertTrue(this.clientPage.wasDenied());
+            this.clientPage.navigateTo();
+            this.clientPage.viewAllAlbums();
+            this.clientPage.deleteAlbum("Alice-Family-Album");
+            assertTrue(this.clientPage.wasDenied());
+
+            loginToClientPage("alice", "alice");
+            this.clientPage.accountGrantResource("Alice-Family-Album", "jdoe");
+
+            loginToClientPage("jdoe", "jdoe");
+            this.clientPage.viewAllAlbums();
+            this.clientPage.viewAlbum("Alice-Family-Album");
+            assertFalse(this.clientPage.wasDenied());
+            this.clientPage.navigateTo();
+            this.clientPage.viewAllAlbums();
+            this.clientPage.deleteAlbum("Alice-Family-Album");
+            assertFalse(this.clientPage.wasDenied());
+
+            loginToClientPage("alice", "alice");
+            this.clientPage.createAlbum("Alice-Family-Album", true);
+
+            loginToClientPage("jdoe", "jdoe");
+            this.clientPage.viewAllAlbums();
+            this.clientPage.viewAlbum("Alice-Family-Album");
+            assertTrue(this.clientPage.wasDenied());
+            this.clientPage.navigateTo();
+            this.clientPage.viewAllAlbums();
+            this.clientPage.deleteAlbum("Alice-Family-Album");
+            assertTrue(this.clientPage.wasDenied());
+
+            loginToClientPage("alice", "alice");
+            this.clientPage.accountGrantRemoveScope("Alice-Family-Album", "jdoe", "album:delete");
+            this.clientPage.accountGrantResource("Alice-Family-Album", "jdoe");
+
+            loginToClientPage("jdoe", "jdoe");
+            this.clientPage.viewAllAlbums();
+            this.clientPage.viewAlbum("Alice-Family-Album");
+            assertFalse(this.clientPage.wasDenied());
+            this.clientPage.navigateTo();
+            this.clientPage.viewAllAlbums();
+            this.clientPage.deleteAlbum("Alice-Family-Album");
+            assertTrue(this.clientPage.wasDenied());
+        } finally {
+            this.deployer.undeploy(RESOURCE_SERVER_ID);
+        }
+    }
+
+    @Test
+    public void testOwnerSharingResource() throws Exception {
+        try {
+            this.deployer.deploy(RESOURCE_SERVER_ID);
+            loginToClientPage("alice", "alice");
+            this.clientPage.createAlbum("Alice-Family-Album", true);
+            this.clientPage.accountShareResource("Alice-Family-Album", "jdoe");
+
+            loginToClientPage("jdoe", "jdoe");
+            this.clientPage.viewAllAlbums();
+            this.clientPage.viewAlbum("Alice-Family-Album");
+            assertFalse(this.clientPage.wasDenied());
+            this.clientPage.navigateTo();
+            this.clientPage.viewAllAlbums();
+            this.clientPage.deleteAlbum("Alice-Family-Album");
+            assertFalse(this.clientPage.wasDenied());
+
+            loginToClientPage("alice", "alice");
+            this.clientPage.createAlbum("Alice-Family-Album", true);
+            this.clientPage.accountShareRemoveScope("Alice-Family-Album", "jdoe", "album:delete");
+
+            loginToClientPage("jdoe", "jdoe");
+            this.clientPage.viewAllAlbums();
+            this.clientPage.viewAlbum("Alice-Family-Album");
+            assertFalse(this.clientPage.wasDenied());
+            this.clientPage.navigateTo();
+            this.clientPage.viewAllAlbums();
+            this.clientPage.deleteAlbum("Alice-Family-Album");
+            assertTrue(this.clientPage.wasDenied());
+
+            loginToClientPage("alice", "alice");
+            this.clientPage.accountRevokeResource("Alice-Family-Album", "jdoe");
+
+            loginToClientPage("jdoe", "jdoe");
+            this.clientPage.viewAllAlbums();
+            this.clientPage.viewAlbum("Alice-Family-Album");
+            assertTrue(this.clientPage.wasDenied());
+        } finally {
+            this.deployer.undeploy(RESOURCE_SERVER_ID);
+        }
+    }
+
     private void importResourceServerSettings() throws FileNotFoundException {
         ResourceServerRepresentation authSettings = loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/photoz/photoz-restful-api-authz-service.json")), ResourceServerRepresentation.class);
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java
index 5f07b2f..536d122 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ResourceManagementWithAuthzClientTest.java
@@ -20,10 +20,8 @@ package org.keycloak.testsuite.admin.client.authorization;
 import java.io.IOException;
 import java.util.stream.Collectors;
 
-import org.jetbrains.annotations.NotNull;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.RegistrationResponse;
 import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
@@ -42,7 +40,7 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
         org.keycloak.authorization.client.representation.ResourceRepresentation resource = toResourceRepresentation(newResource);
 
         AuthzClient authzClient = getAuthzClient();
-        RegistrationResponse response = authzClient.protection().resource().create(resource);
+        org.keycloak.authorization.client.representation.ResourceRepresentation response = authzClient.protection().resource().create(resource);
 
         return toResourceRepresentation(authzClient, response.getId());
     }
@@ -62,7 +60,7 @@ public class ResourceManagementWithAuthzClientTest extends ResourceManagementTes
     }
 
     private ResourceRepresentation toResourceRepresentation(AuthzClient authzClient, String id) {
-        org.keycloak.authorization.client.representation.ResourceRepresentation created = authzClient.protection().resource().findById(id).getResourceDescription();
+        org.keycloak.authorization.client.representation.ResourceRepresentation created = authzClient.protection().resource().findById(id);
         ResourceRepresentation resourceRepresentation = new ResourceRepresentation();
 
         resourceRepresentation.setId(created.getId());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java
index b4b493e..56d2bee 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java
@@ -294,6 +294,7 @@ public class RealmTest extends AbstractAdminTest {
         rep.setRegistrationAllowed(true);
         rep.setRegistrationEmailAsUsername(true);
         rep.setEditUsernameAllowed(true);
+        rep.setUserManagedAccessAllowed(true);
 
         realm.update(rep);
         assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep, ResourceType.REALM);
@@ -308,11 +309,13 @@ public class RealmTest extends AbstractAdminTest {
         assertEquals(Boolean.TRUE, rep.isRegistrationAllowed());
         assertEquals(Boolean.TRUE, rep.isRegistrationEmailAsUsername());
         assertEquals(Boolean.TRUE, rep.isEditUsernameAllowed());
+        assertEquals(Boolean.TRUE, rep.isUserManagedAccessAllowed());
 
         // second change
         rep.setRegistrationAllowed(false);
         rep.setRegistrationEmailAsUsername(false);
         rep.setEditUsernameAllowed(false);
+        rep.setUserManagedAccessAllowed(false);
 
         realm.update(rep);
         assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep, ResourceType.REALM);
@@ -321,7 +324,7 @@ public class RealmTest extends AbstractAdminTest {
         assertEquals(Boolean.FALSE, rep.isRegistrationAllowed());
         assertEquals(Boolean.FALSE, rep.isRegistrationEmailAsUsername());
         assertEquals(Boolean.FALSE, rep.isEditUsernameAllowed());
-
+        assertEquals(Boolean.FALSE, rep.isUserManagedAccessAllowed());
     }
 
     @Test
@@ -527,6 +530,7 @@ public class RealmTest extends AbstractAdminTest {
             assertEquals(realm.getAttributes(), attributes);
         }
 
+        if (realm.isUserManagedAccessAllowed() != null) assertEquals(realm.isUserManagedAccessAllowed(), storedRealm.isUserManagedAccessAllowed());
     }
 
     @Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AbstractAuthzTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AbstractAuthzTest.java
index 02d1865..77952be 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AbstractAuthzTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AbstractAuthzTest.java
@@ -1,7 +1,6 @@
 package org.keycloak.testsuite.authz;
 
 import org.junit.BeforeClass;
-import org.keycloak.authorization.client.representation.EntitlementResponse;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.JWSInputException;
 import org.keycloak.representations.AccessToken;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AbstractResourceServerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AbstractResourceServerTest.java
new file mode 100644
index 0000000..237d5e3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AbstractResourceServerTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2017 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.authz;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.ws.rs.core.Response;
+
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.Configuration;
+import org.keycloak.authorization.client.resource.ProtectionResource;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
+import org.keycloak.representations.idm.authorization.Permission;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
+import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.RoleBuilder;
+import org.keycloak.testsuite.util.RolesBuilder;
+import org.keycloak.testsuite.util.UserBuilder;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractResourceServerTest extends AbstractKeycloakTest {
+
+    protected static final String REALM_NAME = "authz-test";
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        testRealms.add(RealmBuilder.create().name(REALM_NAME)
+                .roles(RolesBuilder.create()
+                        .realmRole(RoleBuilder.create().name("uma_authorization").build())
+                        .realmRole(RoleBuilder.create().name("uma_protection").build())
+                )
+                .user(UserBuilder.create().username("marta").password("password")
+                        .addRoles("uma_authorization", "uma_protection")
+                        .role("resource-server-test", "uma_protection"))
+                .user(UserBuilder.create().username("kolo").password("password"))
+                .client(ClientBuilder.create().clientId("resource-server-test")
+                        .secret("secret")
+                        .authorizationServicesEnabled(true)
+                        .redirectUris("http://localhost/resource-server-test")
+                        .defaultRoles("uma_protection")
+                        .directAccessGrants())
+                .client(ClientBuilder.create().clientId("test-app")
+                        .redirectUris("http://localhost:8180/auth/realms/master/app/auth")
+                        .publicClient())
+                .build());
+    }
+
+    protected AuthorizationResponse authorize(String resourceName, String[] scopeNames, String claimToken) {
+        return authorize(null, null, resourceName, scopeNames, null, null, claimToken);
+    }
+
+    protected AuthorizationResponse authorize(String resourceName, String[] scopeNames, String claimToken, String tokenFormat) {
+        return authorize(null, null, null, null, null, claimToken, tokenFormat, new PermissionRequest(resourceName, scopeNames));
+    }
+
+    protected AuthorizationResponse authorize(String resourceName, String[] scopeNames) {
+        return authorize(null, null, resourceName, scopeNames, null, null, null);
+    }
+
+    protected AuthorizationResponse authorize(String userName, String password, String resourceName, String[] scopeNames) {
+        return authorize(userName, password, resourceName, scopeNames, null, null, null);
+    }
+
+    protected AuthorizationResponse authorize(String userName, String password, PermissionRequest... permissions) {
+        return authorize(userName, password, null, null, null, null, null, permissions);
+    }
+
+    protected AuthorizationResponse authorize(String userName, String password, String resourceName, String[] scopeNames, String rpt) {
+        return authorize(userName, password, resourceName, scopeNames, null, rpt, null);
+    }
+
+    protected AuthorizationResponse authorize(String userName, String password, String resourceName, String[] scopeNames, String[] additionalScopes) {
+        return authorize(userName, password, resourceName, scopeNames, additionalScopes, null, null);
+    }
+
+    protected AuthorizationResponse authorize(String userName, String password, String resourceName, String[] scopeNames, String[] additionalScopes, String rpt, String claimToken) {
+        return authorize(userName, password, additionalScopes, rpt, null, claimToken, null, new PermissionRequest(resourceName, scopeNames));
+    }
+
+    protected AuthorizationResponse authorize(String userName, String password, String[] additionalScopes, String rpt, String accessToken, String claimToken, String tokenFormat, PermissionRequest... permissions) {
+        ProtectionResource protection;
+
+        if (userName != null) {
+            protection = getAuthzClient().protection(userName, password);
+        } else {
+            protection = getAuthzClient().protection();
+        }
+
+        String ticket = protection.permission().create(Arrays.asList(permissions)).getTicket();
+
+        AuthorizationRequest authorizationRequest = new AuthorizationRequest(ticket);
+
+        if (additionalScopes != null) {
+            StringBuilder builder = new StringBuilder();
+
+            for (String scope : additionalScopes) {
+                if (builder.length() > 0) {
+                    builder.append(" ");
+                }
+                builder.append(scope);
+            }
+
+            authorizationRequest.setScope(builder.toString());
+        }
+
+        authorizationRequest.setRpt(rpt);
+        authorizationRequest.setClaimTokenFormat(tokenFormat);
+        authorizationRequest.setClaimToken(claimToken);
+
+        org.keycloak.authorization.client.resource.AuthorizationResource authorization;
+
+        if (userName != null) {
+            authorization = getAuthzClient().authorization(userName, password);
+        } else if (accessToken != null) {
+            authorization = getAuthzClient().authorization(accessToken);
+        } else {
+            authorization = getAuthzClient().authorization();
+        }
+
+        return authorization.authorize(authorizationRequest);
+    }
+
+    protected RealmResource getRealm() throws Exception {
+        return adminClient.realm("authz-test");
+    }
+
+    protected ClientResource getClient(RealmResource realm) {
+        ClientsResource clients = realm.clients();
+        return clients.findByClientId("resource-server-test").stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]"));
+    }
+
+    protected AuthzClient getAuthzClient() {
+        try {
+            return AuthzClient.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/default-keycloak-uma2.json"), Configuration.class));
+        } catch (IOException cause) {
+            throw new RuntimeException("Failed to create authz client", cause);
+        }
+    }
+
+    protected AccessToken toAccessToken(String rpt) throws Exception {
+        return JsonSerialization.readValue(new JWSInput(rpt).getContent(), AccessToken.class);
+    }
+
+    protected void assertPermissions(List<Permission> permissions, String expectedResource, String... expectedScopes) {
+        Iterator<Permission> iterator = permissions.iterator();
+
+        while (iterator.hasNext()) {
+            Permission permission = iterator.next();
+
+            if (permission.getResourceName().equalsIgnoreCase(expectedResource)) {
+                Set<String> scopes = permission.getScopes();
+
+                assertEquals(expectedScopes.length, scopes.size());
+
+                if (scopes.containsAll(Arrays.asList(expectedScopes))) {
+                    iterator.remove();
+                }
+            }
+        }
+    }
+
+    protected ResourceRepresentation addResource(String resourceName, String... scopeNames) throws Exception {
+        return addResource(resourceName, null, false, scopeNames);
+    }
+
+    protected ResourceRepresentation addResource(String resourceName, boolean ownerManagedAccess, String... scopeNames) throws Exception {
+        return addResource(resourceName, null, ownerManagedAccess, scopeNames);
+    }
+
+    protected ResourceRepresentation addResource(String resourceName, String owner, boolean ownerManagedAccess, String... scopeNames) throws Exception {
+        ClientResource client = getClient(getRealm());
+        AuthorizationResource authorization = client.authorization();
+        ResourceRepresentation resource = new ResourceRepresentation(resourceName);
+
+        if (owner != null) {
+            resource.setOwner(new ResourceOwnerRepresentation(owner));
+        }
+
+        resource.setOwnerManagedAccess(ownerManagedAccess);
+        resource.addScope(scopeNames);
+
+        Response response = authorization.resources().create(resource);
+        ResourceRepresentation temp = response.readEntity(ResourceRepresentation.class);
+        resource.setId(temp.getId());
+        response.close();
+
+        return resource;
+    }
+
+    @Override
+    protected boolean isImportAfterEachMethod() {
+        return true;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java
index 5529cfd..61d2773 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java
@@ -18,10 +18,8 @@ package org.keycloak.testsuite.authz;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
 
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.List;
 
 import javax.ws.rs.core.Response;
@@ -32,17 +30,14 @@ import org.keycloak.admin.client.resource.AuthorizationResource;
 import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.admin.client.resource.ClientsResource;
 import org.keycloak.admin.client.resource.RealmResource;
-import org.keycloak.authorization.client.AuthorizationDeniedException;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.AuthorizationRequest;
-import org.keycloak.authorization.client.representation.AuthorizationResponse;
-import org.keycloak.authorization.client.representation.PermissionRequest;
-import org.keycloak.authorization.client.util.HttpResponseException;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.RealmRepresentation;
-import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.testsuite.util.ClientBuilder;
@@ -108,45 +103,12 @@ public class AuthorizationAPITest extends AbstractAuthzTest {
     @Test
     public void testAccessTokenWithUmaAuthorization() {
         AuthzClient authzClient = getAuthzClient();
-        PermissionRequest request = new PermissionRequest();
-
-        request.setResourceSetName("Resource A");
+        PermissionRequest request = new PermissionRequest("Resource A");
 
-        String ticket = authzClient.protection().permission().forResource(request).getTicket();
+        String ticket = authzClient.protection().permission().create(request).getTicket();
         AuthorizationResponse response = authzClient.authorization("marta", "password").authorize(new AuthorizationRequest(ticket));
 
-        assertNotNull(response.getRpt());
-    }
-
-    @Test
-    public void failAccessTokenWithoutUmaAuthorization() {
-        AuthzClient authzClient = getAuthzClient();
-        PermissionRequest request = new PermissionRequest();
-
-        request.setResourceSetName("Resource A");
-
-        String ticket = authzClient.protection().permission().forResource(request).getTicket();
-
-        try {
-            authzClient.authorization("kolo", "password").authorize(new AuthorizationRequest(ticket));
-            fail("Should fail because user does not have uma_authorization");
-        } catch (AuthorizationDeniedException cause) {
-            assertEquals(403, ((HttpResponseException) cause.getCause()).getStatusCode());
-        }
-    }
-
-    @Test
-    public void failClientMockingUmaAuthorization() throws Exception {
-        RealmResource realm = getRealm();
-        ClientResource client = getClient(realm);
-        RoleRepresentation umaAuthorizationRole = new RoleRepresentation("uma_authorization", "", false);
-
-        client.roles().create(umaAuthorizationRole);
-        umaAuthorizationRole = client.roles().get(umaAuthorizationRole.getName()).toRepresentation();
-
-        realm.users().get(realm.users().search("kolo").get(0).getId()).roles().clientLevel(client.toRepresentation().getId()).add(Arrays.asList(umaAuthorizationRole));
-
-        failAccessTokenWithoutUmaAuthorization();
+        assertNotNull(response.getToken());
     }
 
     @Test
@@ -154,14 +116,14 @@ public class AuthorizationAPITest extends AbstractAuthzTest {
         AuthzClient authzClient = getAuthzClient();
         PermissionRequest request = new PermissionRequest();
 
-        request.setResourceSetName("Resource A");
+        request.setResourceId("Resource A");
 
         String accessToken = new OAuthClient().realm("authz-test").clientId("test-client").doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
-        String ticket = authzClient.protection().permission().forResource(request).getTicket();
+        String ticket = authzClient.protection().permission().create(request).getTicket();
         AuthorizationResponse response = authzClient.authorization(accessToken).authorize(new AuthorizationRequest(ticket));
 
-        assertNotNull(response.getRpt());
-        AccessToken rpt = toAccessToken(response.getRpt());
+        assertNotNull(response.getToken());
+        AccessToken rpt = toAccessToken(response.getToken());
         assertEquals("resource-server-test", rpt.getAudience()[0]);
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
index cfeb153..7fece47 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
@@ -19,12 +19,16 @@ package org.keycloak.testsuite.authz;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.InputStream;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -35,15 +39,9 @@ import org.keycloak.admin.client.Keycloak;
 import org.keycloak.admin.client.resource.AuthorizationResource;
 import org.keycloak.admin.client.resource.ClientsResource;
 import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
-import org.keycloak.authorization.client.AuthorizationDeniedException;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.ClientAuthenticator;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.AuthorizationRequest;
-import org.keycloak.authorization.client.representation.AuthorizationResponse;
-import org.keycloak.authorization.client.representation.PermissionRequest;
-import org.keycloak.authorization.client.representation.PermissionResponse;
-import org.keycloak.authorization.client.representation.RegistrationResponse;
 import org.keycloak.authorization.client.representation.ResourceRepresentation;
 import org.keycloak.authorization.client.resource.ProtectionResource;
 import org.keycloak.authorization.client.util.HttpResponseException;
@@ -53,9 +51,12 @@ import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.representations.idm.UserSessionRepresentation;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.Permission;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
+import org.keycloak.representations.idm.authorization.PermissionResponse;
 import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
-import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.RealmBuilder;
 import org.keycloak.testsuite.util.RolesBuilder;
@@ -111,15 +112,12 @@ public class AuthzClientCredentialsTest extends AbstractAuthzTest {
     public void testSuccessfulAuthorizationRequest() throws Exception {
         AuthzClient authzClient = getAuthzClient("keycloak-with-jwt-authentication.json");
         ProtectionResource protection = authzClient.protection();
-        PermissionRequest request = new PermissionRequest();
-
-        request.setResourceSetName("Default Resource");
-
-        PermissionResponse ticketResponse = protection.permission().forResource(request);
+        PermissionRequest request = new PermissionRequest("Default Resource");
+        PermissionResponse ticketResponse = protection.permission().create(request);
         String ticket = ticketResponse.getTicket();
 
         AuthorizationResponse authorizationResponse = authzClient.authorization("marta", "password").authorize(new AuthorizationRequest(ticket));
-        String rpt = authorizationResponse.getRpt();
+        String rpt = authorizationResponse.getToken();
 
         assertNotNull(rpt);
 
@@ -132,35 +130,17 @@ public class AuthzClientCredentialsTest extends AbstractAuthzTest {
         List<Permission> permissions = authorization.getPermissions();
 
         assertFalse(permissions.isEmpty());
-        assertEquals("Default Resource", permissions.get(0).getResourceSetName());
-    }
-
-    @Test
-    public void failUserWithoutUmaAuthorizationScope() throws Exception {
-        AuthzClient authzClient = getAuthzClient("keycloak-with-jwt-authentication.json");
-        ProtectionResource protection = authzClient.protection();
-        PermissionRequest request = new PermissionRequest();
-
-        request.setResourceSetName("Default Resource");
-
-        PermissionResponse ticketResponse = protection.permission().forResource(request);
-        String ticket = ticketResponse.getTicket();
-
-        try {
-            authzClient.authorization("kolo", "password").authorize(new AuthorizationRequest(ticket));
-            fail("Should fail because user does not have uma_authorization");
-        } catch (AuthorizationDeniedException cause) {
-            assertEquals(403, ((HttpResponseException) cause.getCause()).getStatusCode());
-        }
+        assertEquals("Default Resource", permissions.get(0).getResourceName());
     }
 
     @Test
     public void failJWTAuthentication() {
         try {
-            getAuthzClient("keycloak-with-invalid-keys-jwt-authentication.json").protection();
+            getAuthzClient("keycloak-with-invalid-keys-jwt-authentication.json").protection().resource().findAll();
             fail("Should fail due to invalid signature");
-        } catch (HttpResponseException cause) {
-            assertEquals(400, cause.getStatusCode());
+        } catch (Exception cause) {
+            assertTrue(HttpResponseException.class.isInstance(cause.getCause().getCause()));
+            assertEquals(400, HttpResponseException.class.cast(cause.getCause().getCause()).getStatusCode());
         }
     }
 
@@ -181,14 +161,14 @@ public class AuthzClientCredentialsTest extends AbstractAuthzTest {
         AuthzClient authzClient = getAuthzClient("default-session-keycloak.json");
         ProtectionResource protection = authzClient.protection();
 
-        protection.resource().findByFilter("name=Default Resource");
+        protection.resource().findByName("Default Resource");
         userSessions = clients.get(clientRepresentation.getId()).getUserSessions(null, null);
 
         assertEquals(1, userSessions.size());
 
         Thread.sleep(2000);
         protection = authzClient.protection();
-        protection.resource().findByFilter("name=Default Resource");
+        protection.resource().findByName("Default Resource");
 
         userSessions = clients.get(clientRepresentation.getId()).getUserSessions(null, null);
 
@@ -211,8 +191,7 @@ public class AuthzClientCredentialsTest extends AbstractAuthzTest {
         ResourceRepresentation expected = new ResourceRepresentation("Resource A", Collections.emptySet());
 
         String id = protection.resource().create(expected).getId();
-        RegistrationResponse response = protection.resource().findById(id);
-        ResourceRepresentation actual = response.getResourceDescription();
+        ResourceRepresentation actual = protection.resource().findById(id);
 
         assertNotNull(actual);
         assertEquals(expected.getName(), actual.getName());
@@ -224,8 +203,12 @@ public class AuthzClientCredentialsTest extends AbstractAuthzTest {
 
         return AuthzClient.create(new Configuration(deployment.getAuthServerBaseUrl(), deployment.getRealm(), deployment.getResourceName(), deployment.getResourceCredentials(), deployment.getClient()), new ClientAuthenticator() {
             @Override
-            public void configureClientCredentials(HashMap<String, String> requestParams, HashMap<String, String> requestHeaders) {
-                ClientCredentialsProviderUtils.setClientCredentials(deployment, requestHeaders, requestParams);
+            public void configureClientCredentials(Map<String, List<String>> requestParams, Map<String, String> requestHeaders) {
+                Map<String, String> formparams = new HashMap<>();
+                ClientCredentialsProviderUtils.setClientCredentials(deployment, requestHeaders, formparams);
+                for (Entry<String, String> param : formparams.entrySet()) {
+                    requestParams.put(param.getKey(), Arrays.asList(param.getValue()));
+                }
             }
         });
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
index d7f8c6b..b55f348 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/ConflictingScopePermissionTest.java
@@ -38,18 +38,17 @@ import org.keycloak.admin.client.resource.ClientsResource;
 import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.EntitlementResponse;
 import org.keycloak.authorization.client.representation.ResourceRepresentation;
 import org.keycloak.authorization.client.representation.ScopeRepresentation;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.JWSInputException;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.Permission;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
 import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
-import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.RealmBuilder;
 import org.keycloak.testsuite.util.UserBuilder;
@@ -96,7 +95,7 @@ public class ConflictingScopePermissionTest extends AbstractAuthzTest {
         List<Permission> permissions = getEntitlements("marta", "password");
 
         for (Permission permission : new ArrayList<>(permissions)) {
-            String resourceSetName = permission.getResourceSetName();
+            String resourceSetName = permission.getResourceName();
 
             switch (resourceSetName) {
                 case "Resource A":
@@ -122,11 +121,11 @@ public class ConflictingScopePermissionTest extends AbstractAuthzTest {
 
     private List<Permission> getEntitlements(String username, String password) {
         AuthzClient authzClient = getAuthzClient();
-        EntitlementResponse response = authzClient.entitlement(authzClient.obtainAccessToken(username, password).getToken()).getAll("resource-server-test");
+        AuthorizationResponse response = authzClient.authorization(username, password).authorize();
         AccessToken accessToken;
 
         try {
-            accessToken = new JWSInput(response.getRpt()).readJsonContent(AccessToken.class);
+            accessToken = new JWSInput(response.getToken()).readJsonContent(AccessToken.class);
         } catch (JWSInputException cause) {
             throw new RuntimeException("Failed to deserialize RPT", cause);
         }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java
index c0a8868..54bcd2e 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java
@@ -25,8 +25,6 @@ import java.io.IOException;
 import java.util.List;
 import java.util.function.Supplier;
 
-import javax.ws.rs.core.Response;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.AuthorizationResource;
@@ -35,20 +33,15 @@ import org.keycloak.admin.client.resource.ClientsResource;
 import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.AuthorizationRequestMetadata;
-import org.keycloak.authorization.client.representation.EntitlementRequest;
-import org.keycloak.authorization.client.representation.EntitlementResponse;
-import org.keycloak.authorization.client.representation.PermissionRequest;
-import org.keycloak.jose.jws.JWSInput;
-import org.keycloak.jose.jws.JWSInputException;
-import org.keycloak.protocol.oidc.utils.OIDCResponseType;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest.Metadata;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
 import org.keycloak.representations.idm.authorization.Permission;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
-import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.OAuthClient;
 import org.keycloak.testsuite.util.RealmBuilder;
@@ -113,76 +106,76 @@ public class EntitlementAPITest extends AbstractAuthzTest {
 
     @Test
     public void testRptRequestWithoutResourceName() {
-        AuthorizationRequestMetadata metadata = new AuthorizationRequestMetadata();
+        Metadata metadata = new Metadata();
 
         metadata.setIncludeResourceName(false);
 
         assertResponse(metadata, () -> {
-            EntitlementRequest request = new EntitlementRequest();
+            AuthorizationRequest request = new AuthorizationRequest();
 
             request.setMetadata(metadata);
-            request.addPermission(new PermissionRequest("Resource 1"));
+            request.addPermission("Resource 1");
 
-            return getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request);
+            return getAuthzClient().authorization("marta", "password").authorize(request);
         });
     }
 
     @Test
     public void testRptRequestWithResourceName() {
-        AuthorizationRequestMetadata metadata = new AuthorizationRequestMetadata();
+        Metadata metadata = new Metadata();
 
         metadata.setIncludeResourceName(true);
 
-        assertResponse(metadata, () -> getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).getAll("resource-server-test"));
+        assertResponse(metadata, () -> getAuthzClient().authorization("marta", "password").authorize());
 
-        EntitlementRequest request = new EntitlementRequest();
+        AuthorizationRequest request = new AuthorizationRequest();
 
         request.setMetadata(metadata);
-        request.addPermission(new PermissionRequest("Resource 13"));
+        request.addPermission("Resource 13");
 
-        assertResponse(metadata, () -> getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request));
+        assertResponse(metadata, () -> getAuthzClient().authorization("marta", "password").authorize(request));
 
         request.setMetadata(null);
 
-        assertResponse(metadata, () -> getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request));
+        assertResponse(metadata, () -> getAuthzClient().authorization("marta", "password").authorize(request));
     }
 
     @Test
     public void testPermissionLimit() {
-        EntitlementRequest request = new EntitlementRequest();
+        AuthorizationRequest request = new AuthorizationRequest();
 
         for (int i = 1; i <= 10; i++) {
-            request.addPermission(new PermissionRequest("Resource " + i));
+            request.addPermission("Resource " + i);
         }
 
-        AuthorizationRequestMetadata metadata = new AuthorizationRequestMetadata();
+        Metadata metadata = new Metadata();
 
         metadata.setLimit(10);
 
         request.setMetadata(metadata);
 
-        EntitlementResponse response = getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request);
-        AccessToken rpt = toAccessToken(response.getRpt());
+        AuthorizationResponse response = getAuthzClient().authorization("marta", "password").authorize(request);
+        AccessToken rpt = toAccessToken(response.getToken());
 
         List<Permission> permissions = rpt.getAuthorization().getPermissions();
 
         assertEquals(10, permissions.size());
 
         for (int i = 0; i < 10; i++) {
-            assertEquals("Resource " + (i + 1), permissions.get(i).getResourceSetName());
+            assertEquals("Resource " + (i + 1), permissions.get(i).getResourceName());
         }
 
-        request = new EntitlementRequest();
+        request = new AuthorizationRequest();
 
         for (int i = 11; i <= 15; i++) {
-            request.addPermission(new PermissionRequest("Resource " + i));
+            request.addPermission("Resource " + i);
         }
 
         request.setMetadata(metadata);
-        request.setRpt(response.getRpt());
+        request.setRpt(response.getToken());
 
-        response = getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request);
-        rpt = toAccessToken(response.getRpt());
+        response = getAuthzClient().authorization("marta", "password").authorize(request);
+        rpt = toAccessToken(response.getToken());
 
         permissions = rpt.getAuthorization().getPermissions();
 
@@ -190,72 +183,72 @@ public class EntitlementAPITest extends AbstractAuthzTest {
 
         for (int i = 0; i < 10; i++) {
             if (i < 5) {
-                assertEquals("Resource " + (i + 11), permissions.get(i).getResourceSetName());
+                assertEquals("Resource " + (i + 11), permissions.get(i).getResourceName());
             } else {
-                assertEquals("Resource " + (i - 4), permissions.get(i).getResourceSetName());
+                assertEquals("Resource " + (i - 4), permissions.get(i).getResourceName());
             }
         }
 
-        request = new EntitlementRequest();
+        request = new AuthorizationRequest();
 
         for (int i = 16; i <= 18; i++) {
-            request.addPermission(new PermissionRequest("Resource " + i));
+            request.addPermission("Resource " + i);
         }
 
         request.setMetadata(metadata);
-        request.setRpt(response.getRpt());
+        request.setRpt(response.getToken());
 
-        response = getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request);
-        rpt = toAccessToken(response.getRpt());
+        response = getAuthzClient().authorization("marta", "password").authorize(request);
+        rpt = toAccessToken(response.getToken());
 
         permissions = rpt.getAuthorization().getPermissions();
 
         assertEquals(10, permissions.size());
-        assertEquals("Resource 16", permissions.get(0).getResourceSetName());
-        assertEquals("Resource 17", permissions.get(1).getResourceSetName());
-        assertEquals("Resource 18", permissions.get(2).getResourceSetName());
-        assertEquals("Resource 11", permissions.get(3).getResourceSetName());
-        assertEquals("Resource 12", permissions.get(4).getResourceSetName());
-        assertEquals("Resource 13", permissions.get(5).getResourceSetName());
-        assertEquals("Resource 14", permissions.get(6).getResourceSetName());
-        assertEquals("Resource 15", permissions.get(7).getResourceSetName());
-        assertEquals("Resource 1", permissions.get(8).getResourceSetName());
-        assertEquals("Resource 2", permissions.get(9).getResourceSetName());
-
-        request = new EntitlementRequest();
+        assertEquals("Resource 16", permissions.get(0).getResourceName());
+        assertEquals("Resource 17", permissions.get(1).getResourceName());
+        assertEquals("Resource 18", permissions.get(2).getResourceName());
+        assertEquals("Resource 11", permissions.get(3).getResourceName());
+        assertEquals("Resource 12", permissions.get(4).getResourceName());
+        assertEquals("Resource 13", permissions.get(5).getResourceName());
+        assertEquals("Resource 14", permissions.get(6).getResourceName());
+        assertEquals("Resource 15", permissions.get(7).getResourceName());
+        assertEquals("Resource 1", permissions.get(8).getResourceName());
+        assertEquals("Resource 2", permissions.get(9).getResourceName());
+
+        request = new AuthorizationRequest();
 
         metadata.setLimit(5);
         request.setMetadata(metadata);
-        request.setRpt(response.getRpt());
+        request.setRpt(response.getToken());
 
-        response = getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request);
-        rpt = toAccessToken(response.getRpt());
+        response = getAuthzClient().authorization("marta", "password").authorize(request);
+        rpt = toAccessToken(response.getToken());
 
         permissions = rpt.getAuthorization().getPermissions();
 
         assertEquals(5, permissions.size());
-        assertEquals("Resource 16", permissions.get(0).getResourceSetName());
-        assertEquals("Resource 17", permissions.get(1).getResourceSetName());
-        assertEquals("Resource 18", permissions.get(2).getResourceSetName());
-        assertEquals("Resource 11", permissions.get(3).getResourceSetName());
-        assertEquals("Resource 12", permissions.get(4).getResourceSetName());
+        assertEquals("Resource 16", permissions.get(0).getResourceName());
+        assertEquals("Resource 17", permissions.get(1).getResourceName());
+        assertEquals("Resource 18", permissions.get(2).getResourceName());
+        assertEquals("Resource 11", permissions.get(3).getResourceName());
+        assertEquals("Resource 12", permissions.get(4).getResourceName());
     }
 
     @Test
     public void testResourceServerAsAudience() throws Exception {
-        EntitlementRequest request = new EntitlementRequest();
+        AuthorizationRequest request = new AuthorizationRequest();
 
-        request.addPermission(new PermissionRequest("Resource 1"));
+        request.addPermission("Resource 1");
 
         String accessToken = new OAuthClient().realm("authz-test").clientId("test-client").doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
-        EntitlementResponse response = getAuthzClient().entitlement(accessToken).get("resource-server-test", request);
-        AccessToken rpt = toAccessToken(response.getRpt());
+        AuthorizationResponse response = getAuthzClient().authorization(accessToken).authorize(request);
+        AccessToken rpt = toAccessToken(response.getToken());
 
         assertEquals("resource-server-test", rpt.getAudience()[0]);
     }
 
-    private void assertResponse(AuthorizationRequestMetadata metadata, Supplier<EntitlementResponse> responseSupplier) {
-        AccessToken.Authorization authorization = toAccessToken(responseSupplier.get().getRpt()).getAuthorization();
+    private void assertResponse(Metadata metadata, Supplier<AuthorizationResponse> responseSupplier) {
+        AccessToken.Authorization authorization = toAccessToken(responseSupplier.get().getToken()).getAuthorization();
 
         List<Permission> permissions = authorization.getPermissions();
 
@@ -263,10 +256,10 @@ public class EntitlementAPITest extends AbstractAuthzTest {
         assertFalse(permissions.isEmpty());
 
         for (Permission permission : permissions) {
-            if (metadata.isIncludeResourceName()) {
-                assertNotNull(permission.getResourceSetName());
+            if (metadata.getIncludeResourceName()) {
+                assertNotNull(permission.getResourceName());
             } else {
-                assertNull(permission.getResourceSetName());
+                assertNull(permission.getResourceName());
             }
         }
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupNamePolicyTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupNamePolicyTest.java
index 256c24c..f4fcce5 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupNamePolicyTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupNamePolicyTest.java
@@ -37,9 +37,6 @@ import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.authorization.client.AuthorizationDeniedException;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.AuthorizationRequest;
-import org.keycloak.authorization.client.representation.AuthorizationResponse;
-import org.keycloak.authorization.client.representation.PermissionRequest;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.mappers.GroupMembershipMapper;
 import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
@@ -47,10 +44,12 @@ import org.keycloak.representations.idm.GroupRepresentation;
 import org.keycloak.representations.idm.ProtocolMapperRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
-import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.util.AdminClientUtil;
 import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.GroupBuilder;
@@ -138,14 +137,11 @@ public class GroupNamePolicyTest extends AbstractAuthzTest {
     @Test
     public void testExactNameMatch() {
         AuthzClient authzClient = getAuthzClient();
-        PermissionRequest request = new PermissionRequest();
-
-        request.setResourceSetName("Resource A");
-
-        String ticket = authzClient.protection().permission().forResource(request).getTicket();
+        PermissionRequest request = new PermissionRequest("Resource A");
+        String ticket = authzClient.protection().permission().create(request).getTicket();
         AuthorizationResponse response = authzClient.authorization("marta", "password").authorize(new AuthorizationRequest(ticket));
 
-        assertNotNull(response.getRpt());
+        assertNotNull(response.getToken());
 
         try {
             authzClient.authorization("kolo", "password").authorize(new AuthorizationRequest(ticket));
@@ -166,11 +162,8 @@ public class GroupNamePolicyTest extends AbstractAuthzTest {
     public void testOnlyChildrenPolicy() throws Exception {
         RealmResource realm = getRealm();
         AuthzClient authzClient = getAuthzClient();
-        PermissionRequest request = new PermissionRequest();
-
-        request.setResourceSetName("Resource B");
-
-        String ticket = authzClient.protection().permission().forResource(request).getTicket();
+        PermissionRequest request = new PermissionRequest("Resource B");
+        String ticket = authzClient.protection().permission().create(request).getTicket();
 
         try {
             authzClient.authorization("kolo", "password").authorize(new AuthorizationRequest(ticket));
@@ -181,7 +174,7 @@ public class GroupNamePolicyTest extends AbstractAuthzTest {
 
         AuthorizationResponse response = authzClient.authorization("alice", "password").authorize(new AuthorizationRequest(ticket));
 
-        assertNotNull(response.getRpt());
+        assertNotNull(response.getToken());
 
         try {
             authzClient.authorization("marta", "password").authorize(new AuthorizationRequest(ticket));
@@ -190,15 +183,10 @@ public class GroupNamePolicyTest extends AbstractAuthzTest {
 
         }
 
-        request = new PermissionRequest();
-
-        request.setResourceSetName("Resource C");
-
-        ticket = authzClient.protection().permission().forResource(request).getTicket();
-
+        request = new PermissionRequest("Resource C");
+        ticket = authzClient.protection().permission().create(request).getTicket();
         response = authzClient.authorization("kolo", "password").authorize(new AuthorizationRequest(ticket));
-
-        assertNotNull(response.getRpt());
+        assertNotNull(response.getToken());
     }
 
     private void createGroupPolicy(String name, String groupPath, boolean extendChildren) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathPolicyTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathPolicyTest.java
index 9b3b728..ab0bfe9 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathPolicyTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathPolicyTest.java
@@ -22,7 +22,6 @@ import static org.junit.Assert.fail;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
@@ -38,22 +37,19 @@ import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.authorization.client.AuthorizationDeniedException;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.AuthorizationRequest;
-import org.keycloak.authorization.client.representation.AuthorizationResponse;
-import org.keycloak.authorization.client.representation.PermissionRequest;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.mappers.GroupMembershipMapper;
 import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
 import org.keycloak.representations.idm.GroupRepresentation;
 import org.keycloak.representations.idm.ProtocolMapperRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
-import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
-import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
-import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.util.AdminClientUtil;
 import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.GroupBuilder;
@@ -128,14 +124,11 @@ public class GroupPathPolicyTest extends AbstractAuthzTest {
     @Test
     public void testAllowParentAndChildren() {
         AuthzClient authzClient = getAuthzClient();
-        PermissionRequest request = new PermissionRequest();
-
-        request.setResourceSetName("Resource A");
-
-        String ticket = authzClient.protection().permission().forResource(request).getTicket();
+        PermissionRequest request = new PermissionRequest("Resource A");
+        String ticket = authzClient.protection().permission().create(request).getTicket();
         AuthorizationResponse response = authzClient.authorization("marta", "password").authorize(new AuthorizationRequest(ticket));
 
-        assertNotNull(response.getRpt());
+        assertNotNull(response.getToken());
 
         RealmResource realm = getRealm();
         GroupRepresentation group = getGroup("/Group A/Group B/Group C");
@@ -143,21 +136,18 @@ public class GroupPathPolicyTest extends AbstractAuthzTest {
 
         realm.users().get(user.getId()).joinGroup(group.getId());
 
-        ticket = authzClient.protection().permission().forResource(request).getTicket();
+        ticket = authzClient.protection().permission().create(request).getTicket();
         response = authzClient.authorization("kolo", "password").authorize(new AuthorizationRequest(ticket));
 
-        assertNotNull(response.getRpt());
+        assertNotNull(response.getToken());
     }
 
     @Test
     public void testOnlyChildrenPolicy() throws Exception {
         RealmResource realm = getRealm();
         AuthzClient authzClient = getAuthzClient();
-        PermissionRequest request = new PermissionRequest();
-
-        request.setResourceSetName("Resource B");
-
-        String ticket = authzClient.protection().permission().forResource(request).getTicket();
+        PermissionRequest request = new PermissionRequest("Resource B");
+        String ticket = authzClient.protection().permission().create(request).getTicket();
 
         try {
             authzClient.authorization("kolo", "password").authorize(new AuthorizationRequest(ticket));
@@ -173,7 +163,7 @@ public class GroupPathPolicyTest extends AbstractAuthzTest {
 
         AuthorizationResponse response = authzClient.authorization("kolo", "password").authorize(new AuthorizationRequest(ticket));
 
-        assertNotNull(response.getRpt());
+        assertNotNull(response.getToken());
 
         try {
             authzClient.authorization("marta", "password").authorize(new AuthorizationRequest(ticket));
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionClaimTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionClaimTest.java
index cde7fc2..7a9600f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionClaimTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionClaimTest.java
@@ -32,19 +32,16 @@ import org.keycloak.admin.client.resource.AuthorizationResource;
 import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.admin.client.resource.ClientsResource;
 import org.keycloak.admin.client.resource.RealmResource;
-import org.keycloak.authorization.client.AuthorizationDeniedException;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.AuthorizationRequest;
-import org.keycloak.authorization.client.representation.AuthorizationResponse;
-import org.keycloak.authorization.client.representation.PermissionRequest;
-import org.keycloak.authorization.client.util.HttpResponseException;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.AccessToken.Authorization;
 import org.keycloak.representations.idm.RealmRepresentation;
-import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
 import org.keycloak.representations.idm.authorization.Permission;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.testsuite.util.ClientBuilder;
@@ -121,15 +118,15 @@ public class PermissionClaimTest extends AbstractAuthzTest {
 
         PermissionRequest request = new PermissionRequest();
 
-        request.setResourceSetName(resource.getName());
+        request.setResourceId(resource.getName());
 
         String accessToken = new OAuthClient().realm("authz-test").clientId("test-client").doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
         AuthzClient authzClient = getAuthzClient();
-        String ticket = authzClient.protection().permission().forResource(request).getTicket();
+        String ticket = authzClient.protection().permission().create(request).getTicket();
         AuthorizationResponse response = authzClient.authorization(accessToken).authorize(new AuthorizationRequest(ticket));
 
-        assertNotNull(response.getRpt());
-        AccessToken rpt = toAccessToken(response.getRpt());
+        assertNotNull(response.getToken());
+        AccessToken rpt = toAccessToken(response.getToken());
         Authorization authorizationClaim = rpt.getAuthorization();
         List<Permission> permissions = authorizationClaim.getPermissions();
 
@@ -157,15 +154,15 @@ public class PermissionClaimTest extends AbstractAuthzTest {
 
         PermissionRequest request = new PermissionRequest();
 
-        request.setResourceSetName(resource.getName());
+        request.setResourceId(resource.getName());
 
         String accessToken = new OAuthClient().realm("authz-test").clientId("test-client").doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
         AuthzClient authzClient = getAuthzClient();
         String ticket = authzClient.protection().permission().forResource(request).getTicket();
         AuthorizationResponse response = authzClient.authorization(accessToken).authorize(new AuthorizationRequest(ticket));
 
-        assertNotNull(response.getRpt());
-        AccessToken rpt = toAccessToken(response.getRpt());
+        assertNotNull(response.getToken());
+        AccessToken rpt = toAccessToken(response.getToken());
         Authorization authorizationClaim = rpt.getAuthorization();
         List<Permission> permissions = authorizationClaim.getPermissions();
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionManagementTest.java
new file mode 100644
index 0000000..21bb9ee
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionManagementTest.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2017 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.authz;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ResourceScopesResource;
+import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.util.HttpResponseException;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
+import org.keycloak.representations.idm.authorization.PermissionResponse;
+import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
+import org.keycloak.representations.idm.authorization.PermissionTicketToken;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionManagementTest extends AbstractResourceServerTest {
+
+    @Test
+    public void testCreatePermissionTicketWithResourceName() throws Exception {
+        ResourceRepresentation resource = addResource("Resource A", "kolo", true);
+        AuthzClient authzClient = getAuthzClient();
+        PermissionResponse response = authzClient.protection("marta", "password").permission().create(new PermissionRequest(resource.getName()));
+        AuthorizationRequest request = new AuthorizationRequest();
+        request.setTicket(response.getTicket());
+        request.setClaimToken(authzClient.obtainAccessToken("marta", "password").getToken());
+        try {
+            authzClient.authorization().authorize(request);
+        } catch (Exception e) {
+
+        }
+        assertPersistence(response, resource);
+    }
+
+    @Test
+    public void testCreatePermissionTicketWithResourceId() throws Exception {
+        ResourceRepresentation resource = addResource("Resource A", "kolo", true);
+        AuthzClient authzClient = getAuthzClient();
+        PermissionResponse response = authzClient.protection("marta", "password").permission().create(new PermissionRequest(resource.getId()));
+        AuthorizationRequest request = new AuthorizationRequest();
+        request.setTicket(response.getTicket());
+        request.setClaimToken(authzClient.obtainAccessToken("marta", "password").getToken());
+
+        try {
+            authzClient.authorization().authorize(request);
+        } catch (Exception e) {
+
+        }
+        assertNotNull(response.getTicket());
+        assertFalse(authzClient.protection().permission().findByResource(resource.getId()).isEmpty());
+    }
+
+    @Test
+    public void testCreatePermissionTicketWithScopes() throws Exception {
+        ResourceRepresentation resource = addResource("Resource A", "kolo", true, "ScopeA", "ScopeB", "ScopeC");
+        AuthzClient authzClient = getAuthzClient();
+        PermissionResponse response = authzClient.protection("marta", "password").permission().create(new PermissionRequest(resource.getId(), "ScopeA", "ScopeB", "ScopeC"));
+        AuthorizationRequest request = new AuthorizationRequest();
+        request.setTicket(response.getTicket());
+        request.setClaimToken(authzClient.obtainAccessToken("marta", "password").getToken());
+
+        try {
+            authzClient.authorization().authorize(request);
+        } catch (Exception e) {
+
+        }
+        assertPersistence(response, resource, "ScopeA", "ScopeB", "ScopeC");
+    }
+
+    @Test
+    public void testDeleteResourceAndPermissionTicket() throws Exception {
+        ResourceRepresentation resource = addResource("Resource A", true);
+        PermissionResponse response = getAuthzClient().protection().permission().create(new PermissionRequest(resource.getName()));
+        assertNotNull(response.getTicket());
+
+        getAuthzClient().protection().resource().delete(resource.getId());
+        assertTrue(getAuthzClient().protection().permission().findByResource(resource.getId()).isEmpty());
+    }
+
+    @Test
+    public void testMultiplePermissionRequest() throws Exception {
+        List<PermissionRequest> permissions = new ArrayList<>();
+
+        permissions.add(new PermissionRequest(addResource("Resource A", true).getName()));
+        permissions.add(new PermissionRequest(addResource("Resource B", true).getName()));
+        permissions.add(new PermissionRequest(addResource("Resource C", true).getName()));
+        permissions.add(new PermissionRequest(addResource("Resource D", true).getName()));
+
+        PermissionResponse response = getAuthzClient().protection().permission().create(permissions);
+        assertNotNull(response.getTicket());
+    }
+
+    @Test
+    public void testDeleteScopeAndPermissionTicket() throws Exception {
+        ResourceRepresentation resource = addResource("Resource A", "kolo", true, "ScopeA", "ScopeB", "ScopeC");
+        PermissionRequest permissionRequest = new PermissionRequest(resource.getName());
+
+        permissionRequest.setScopes(new HashSet<>(Arrays.asList("ScopeA", "ScopeB", "ScopeC")));
+
+        AuthzClient authzClient = getAuthzClient();
+        PermissionResponse response = authzClient.protection("marta", "password").permission().create(permissionRequest);
+        assertNotNull(response.getTicket());
+
+        AuthorizationRequest request = new AuthorizationRequest();
+        request.setTicket(response.getTicket());
+        request.setClaimToken(authzClient.obtainAccessToken("marta", "password").getToken());
+
+        try {
+            authzClient.authorization().authorize(request);
+        } catch (Exception e) {
+
+        }
+
+        assertEquals(3, authzClient.protection().permission().findByResource(resource.getId()).size());
+
+        AuthorizationResource authorization = getClient(getRealm()).authorization();
+        ResourceScopesResource scopes = authorization.scopes();
+        ScopeRepresentation scope = scopes.findByName("ScopeA");
+
+        List permissions = authzClient.protection().permission().findByScope(scope.getId());
+        assertFalse(permissions.isEmpty());
+        assertEquals(1, permissions.size());
+
+        resource.setScopes(Collections.emptySet());
+        authorization.resources().resource(resource.getId()).update(resource);
+        scopes.scope(scope.getId()).remove();
+
+        assertTrue(authzClient.protection().permission().findByScope(scope.getId()).isEmpty());
+        assertEquals(0, authzClient.protection().permission().findByResource(resource.getId()).size());
+    }
+
+    @Test
+    public void testRemoveScopeFromResource() throws Exception {
+        ResourceRepresentation resource = addResource("Resource A", "kolo", true, "ScopeA", "ScopeB");
+        PermissionRequest permissionRequest = new PermissionRequest(resource.getName(), "ScopeA", "ScopeB");
+        AuthzClient authzClient = getAuthzClient();
+        PermissionResponse response = authzClient.protection("marta", "password").permission().create(permissionRequest);
+
+        assertNotNull(response.getTicket());
+
+        AuthorizationRequest request = new AuthorizationRequest();
+        request.setTicket(response.getTicket());
+        request.setClaimToken(authzClient.obtainAccessToken("marta", "password").getToken());
+
+        try {
+            authzClient.authorization().authorize(request);
+        } catch (Exception e) {
+
+        }
+
+        AuthorizationResource authorization = getClient(getRealm()).authorization();
+        ResourceScopesResource scopes = authorization.scopes();
+        ScopeRepresentation removedScope = scopes.findByName("ScopeA");
+        List permissions = authzClient.protection().permission().findByScope(removedScope.getId());
+        assertFalse(permissions.isEmpty());
+
+        resource.setScopes(new HashSet<>());
+        resource.addScope("ScopeB");
+
+        authorization.resources().resource(resource.getId()).update(resource);
+        permissions = authzClient.protection().permission().findByScope(removedScope.getId());
+        assertTrue(permissions.isEmpty());
+
+        ScopeRepresentation scopeB = scopes.findByName("ScopeB");
+        permissions = authzClient.protection().permission().findByScope(scopeB.getId());
+        assertFalse(permissions.isEmpty());
+    }
+
+    @Test
+    public void testCreatePermissionTicketWithResourceWithoutManagedAccess() throws Exception {
+        ResourceRepresentation resource = addResource("Resource A");
+        PermissionResponse response = getAuthzClient().protection().permission().create(new PermissionRequest(resource.getName()));
+        assertNotNull(response.getTicket());
+        assertTrue(getAuthzClient().protection().permission().findByResource(resource.getId()).isEmpty());
+    }
+
+    @Test
+    public void testTicketNotCreatedWhenResourceOwner() throws Exception {
+        ResourceRepresentation resource = addResource("Resource A", "marta", true);
+        AuthzClient authzClient = getAuthzClient();
+        PermissionResponse response = authzClient.protection("marta", "password").permission().create(new PermissionRequest(resource.getId()));
+        assertNotNull(response.getTicket());
+        AuthorizationRequest request = new AuthorizationRequest();
+        request.setTicket(response.getTicket());
+        request.setClaimToken(authzClient.obtainAccessToken("marta", "password").getToken());
+
+        try {
+            authzClient.authorization().authorize(request);
+        } catch (Exception e) {
+
+        }
+
+        List permissions = authzClient.protection().permission().findByResource(resource.getId());
+        assertTrue(permissions.isEmpty());
+
+        response = authzClient.protection("kolo", "password").permission().create(new PermissionRequest(resource.getId()));
+        assertNotNull(response.getTicket());
+        request = new AuthorizationRequest();
+        request.setTicket(response.getTicket());
+        request.setClaimToken(authzClient.obtainAccessToken("kolo", "password").getToken());
+
+        try {
+            authzClient.authorization().authorize(request);
+        } catch (Exception e) {
+
+        }
+        permissions = authzClient.protection().permission().findByResource(resource.getId());
+        assertFalse(permissions.isEmpty());
+        assertEquals(1, permissions.size());
+    }
+
+    @Test
+    public void testPermissionForTypedScope() throws Exception {
+        ResourceRepresentation typedResource = addResource("Typed Resource", "ScopeC");
+
+        typedResource.setType("typed-resource");
+
+        getClient(getRealm()).authorization().resources().resource(typedResource.getId()).update(typedResource);
+
+        ResourceRepresentation resourceA = addResource("Resource A", "marta", true, "ScopeA", "ScopeB");
+
+        resourceA.setType(typedResource.getType());
+
+        getClient(getRealm()).authorization().resources().resource(resourceA.getId()).update(resourceA);
+
+        PermissionRequest permissionRequest = new PermissionRequest("Resource A");
+
+        permissionRequest.setScopes(new HashSet<>(Arrays.asList("ScopeA", "ScopeC")));
+
+        AuthzClient authzClient = getAuthzClient();
+        PermissionResponse response = authzClient.protection("kolo", "password").permission().create(permissionRequest);
+
+        AuthorizationRequest request = new AuthorizationRequest();
+        request.setTicket(response.getTicket());
+        request.setClaimToken(authzClient.obtainAccessToken("kolo", "password").getToken());
+
+        try {
+            authzClient.authorization().authorize(request);
+        } catch (Exception e) {
+
+        }
+
+        assertPersistence(response, resourceA, "ScopeA", "ScopeC");
+    }
+
+    @Test
+    public void testSameTicketForSamePermissionRequest() throws Exception {
+        ResourceRepresentation resource = addResource("Resource A", true);
+        PermissionResponse response = getAuthzClient().protection("marta", "password").permission().create(new PermissionRequest(resource.getName()));
+        assertNotNull(response.getTicket());
+    }
+
+    private void assertPersistence(PermissionResponse response, ResourceRepresentation resource, String... scopeNames) throws Exception {
+        String ticket = response.getTicket();
+        assertNotNull(ticket);
+
+        int expectedPermissions = scopeNames.length > 0 ? scopeNames.length : 1;
+        List<PermissionTicketRepresentation> tickets = getAuthzClient().protection().permission().findByResource(resource.getId());
+        assertEquals(expectedPermissions, tickets.size());
+
+        PermissionTicketToken token = new JWSInput(ticket).readJsonContent(PermissionTicketToken.class);
+
+        List<PermissionTicketToken.ResourcePermission> tokenPermissions = token.getResources();
+        assertNotNull(tokenPermissions);
+        assertEquals(expectedPermissions, scopeNames.length > 0 ? scopeNames.length : tokenPermissions.size());
+
+        Iterator<PermissionTicketToken.ResourcePermission> permissionIterator = tokenPermissions.iterator();
+
+        while (permissionIterator.hasNext()) {
+            PermissionTicketToken.ResourcePermission resourcePermission = permissionIterator.next();
+            long count = tickets.stream().filter(representation -> representation.getResource().equals(resourcePermission.getResourceId())).count();
+            if (count == (scopeNames.length > 0 ? scopeNames.length : 1)) {
+                permissionIterator.remove();
+            }
+        }
+
+        assertTrue(tokenPermissions.isEmpty());
+
+        ArrayList<PermissionTicketRepresentation> expectedTickets = new ArrayList<>(tickets);
+        Iterator<PermissionTicketRepresentation> ticketIterator = expectedTickets.iterator();
+
+        while (ticketIterator.hasNext()) {
+            PermissionTicketRepresentation ticketRep = ticketIterator.next();
+
+            assertFalse(ticketRep.isGranted());
+
+            if (ticketRep.getScope() != null) {
+                ScopeRepresentation scope = getClient(getRealm()).authorization().scopes().scope(ticketRep.getScope()).toRepresentation();
+
+                if (Arrays.asList(scopeNames).contains(scope.getName())) {
+                    ticketIterator.remove();
+                }
+            } else if (ticketRep.getResource().equals(resource.getId())) {
+                ticketIterator.remove();
+            }
+        }
+
+        assertTrue(expectedTickets.isEmpty());
+    }
+
+    @Test
+    public void failInvalidResource() {
+        try {
+            getAuthzClient().protection().permission().create(new PermissionRequest("Invalid Resource"));
+            fail("Should fail, resource does not exist");
+        } catch (RuntimeException cause) {
+            assertTrue(HttpResponseException.class.isInstance(cause.getCause()));
+            assertEquals(400, HttpResponseException.class.cast(cause.getCause()).getStatusCode());
+            assertTrue(new String(HttpResponseException.class.cast(cause.getCause()).getBytes()).contains("invalid_resource_id"));
+        }
+        try {
+            getAuthzClient().protection().permission().create(new PermissionRequest());
+            fail("Should fail, resource is empty");
+        } catch (RuntimeException cause) {
+            cause.printStackTrace();
+            assertTrue(HttpResponseException.class.isInstance(cause.getCause()));
+            assertEquals(400, HttpResponseException.class.cast(cause.getCause()).getStatusCode());
+            assertTrue(new String((HttpResponseException.class.cast(cause.getCause()).getBytes())).contains("invalid_resource_id"));
+        }
+    }
+
+    @Test
+    public void failInvalidScope() throws Exception {
+        addResource("Resource A", "ScopeA", "ScopeB");
+        try {
+            PermissionRequest permissionRequest = new PermissionRequest("Resource A");
+
+            permissionRequest.setScopes(new HashSet<>(Arrays.asList("ScopeA", "ScopeC")));
+
+            getAuthzClient().protection().permission().create(permissionRequest);
+            fail("Should fail, resource does not exist");
+        } catch (RuntimeException cause) {
+            assertTrue(HttpResponseException.class.isInstance(cause.getCause()));
+            assertEquals(400, HttpResponseException.class.cast(cause.getCause()).getStatusCode());
+            assertTrue(new String((HttpResponseException.class.cast(cause.getCause()).getBytes())).contains("invalid_scope"));
+        }
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/RolePolicyTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/RolePolicyTest.java
index 994e52e..e1a0fe6 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/RolePolicyTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/RolePolicyTest.java
@@ -16,14 +16,12 @@
  */
 package org.keycloak.testsuite.authz;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
-import java.util.function.Predicate;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -34,19 +32,16 @@ import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.authorization.client.AuthorizationDeniedException;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.Configuration;
-import org.keycloak.authorization.client.representation.AuthorizationRequest;
-import org.keycloak.authorization.client.representation.AuthorizationResponse;
-import org.keycloak.authorization.client.representation.PermissionRequest;
-import org.keycloak.authorization.client.util.HttpResponseException;
 import org.keycloak.representations.idm.GroupRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
-import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
 import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
-import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.util.AdminClientUtil;
 import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.GroupBuilder;
@@ -102,24 +97,19 @@ public class RolePolicyTest extends AbstractAuthzTest {
     @Test
     public void testUserWithExpectedRole() {
         AuthzClient authzClient = getAuthzClient();
-        PermissionRequest request = new PermissionRequest();
-
-        request.setResourceSetName("Resource A");
+        PermissionRequest request = new PermissionRequest("Resource A");
 
-        String ticket = authzClient.protection().permission().forResource(request).getTicket();
+        String ticket = authzClient.protection().permission().create(request).getTicket();
         AuthorizationResponse response = authzClient.authorization("marta", "password").authorize(new AuthorizationRequest(ticket));
 
-        assertNotNull(response.getRpt());
+        assertNotNull(response.getToken());
     }
 
     @Test
     public void testUserWithoutExpectedRole() {
         AuthzClient authzClient = getAuthzClient();
-        PermissionRequest request = new PermissionRequest();
-
-        request.setResourceSetName("Resource A");
-
-        String ticket = authzClient.protection().permission().forResource(request).getTicket();
+        PermissionRequest request = new PermissionRequest("Resource A");
+        String ticket = authzClient.protection().permission().create(request).getTicket();
 
         try {
             authzClient.authorization("kolo", "password").authorize(new AuthorizationRequest(ticket));
@@ -128,16 +118,16 @@ public class RolePolicyTest extends AbstractAuthzTest {
 
         }
 
-        request.setResourceSetName("Resource B");
-        ticket = authzClient.protection().permission().forResource(request).getTicket();
+        request.setResourceId("Resource B");
+        ticket = authzClient.protection().permission().create(request).getTicket();
         assertNotNull(authzClient.authorization("kolo", "password").authorize(new AuthorizationRequest(ticket)));
 
         UserRepresentation user = getRealm().users().search("kolo").get(0);
         RoleRepresentation roleA = getRealm().roles().get("Role A").toRepresentation();
         getRealm().users().get(user.getId()).roles().realmLevel().add(Arrays.asList(roleA));
 
-        request.setResourceSetName("Resource A");
-        ticket = authzClient.protection().permission().forResource(request).getTicket();
+        request.setResourceId("Resource A");
+        ticket = authzClient.protection().permission().create(request).getTicket();
         assertNotNull(authzClient.authorization("kolo", "password").authorize(new AuthorizationRequest(ticket)));
     }
 
@@ -146,9 +136,9 @@ public class RolePolicyTest extends AbstractAuthzTest {
         AuthzClient authzClient = getAuthzClient();
         PermissionRequest request = new PermissionRequest();
 
-        request.setResourceSetName("Resource C");
+        request.setResourceId("Resource C");
 
-        String ticket = authzClient.protection().permission().forResource(request).getTicket();
+        String ticket = authzClient.protection().permission().create(request).getTicket();
         assertNotNull(authzClient.authorization("alice", "password").authorize(new AuthorizationRequest(ticket)));
 
         UserRepresentation user = getRealm().users().search("alice").get(0);
@@ -162,8 +152,8 @@ public class RolePolicyTest extends AbstractAuthzTest {
 
         }
 
-        request.setResourceSetName("Resource A");
-        ticket = authzClient.protection().permission().forResource(request).getTicket();
+        request.setResourceId("Resource A");
+        ticket = authzClient.protection().permission().create(request).getTicket();
 
         try {
             authzClient.authorization("alice", "password").authorize(new AuthorizationRequest(ticket));
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaDiscoveryDocumentTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaDiscoveryDocumentTest.java
new file mode 100644
index 0000000..65c7f6a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaDiscoveryDocumentTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018 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.authz;
+
+import static org.junit.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.junit.Test;
+import org.keycloak.authorization.config.UmaConfiguration;
+import org.keycloak.authorization.config.UmaWellKnownProviderFactory;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.admin.AbstractAdminTest;
+import org.keycloak.testsuite.util.OAuthClient;
+
+public class UmaDiscoveryDocumentTest extends AbstractKeycloakTest {
+
+    @ArquillianResource
+    protected OAuthClient oauth;
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        RealmRepresentation realm = AbstractAdminTest.loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
+        testRealms.add(realm);
+    }
+
+    @Test
+    public void testFetchDiscoveryDocument() {
+        Client client = ClientBuilder.newClient();
+        UriBuilder builder = UriBuilder.fromUri(OAuthClient.AUTH_SERVER_ROOT);
+        URI oidcDiscoveryUri = RealmsResource.wellKnownProviderUrl(builder).build("test", UmaWellKnownProviderFactory.PROVIDER_ID);
+        WebTarget oidcDiscoveryTarget = client.target(oidcDiscoveryUri);
+
+        Response response = oidcDiscoveryTarget.request().get();
+
+        assertEquals("no-cache, must-revalidate, no-transform, no-store", response.getHeaders().getFirst("Cache-Control"));
+
+        UmaConfiguration configuration = response.readEntity(UmaConfiguration.class);
+
+        assertEquals(configuration.getAuthorizationEndpoint(), OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(OAuthClient.AUTH_SERVER_ROOT)).build("test").toString());
+        assertEquals(configuration.getTokenEndpoint(), oauth.getAccessTokenUrl());
+        assertEquals(configuration.getJwksUri(), oauth.getCertsUrl("test"));
+        assertEquals(configuration.getTokenIntrospectionEndpoint(), oauth.getTokenIntrospectionUrl());
+
+        String registrationUri = UriBuilder
+                .fromUri(OAuthClient.AUTH_SERVER_ROOT)
+                .path(RealmsResource.class).path(RealmsResource.class, "getRealmResource").build(realmsResouce().realm("test").toRepresentation().getRealm()).toString();
+
+        assertEquals(registrationUri + "/authz/protection/permission", configuration.getPermissionEndpoint().toString());
+        assertEquals(registrationUri + "/authz/protection/resource_set", configuration.getResourceRegistrationEndpoint().toString());
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaGrantTypeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaGrantTypeTest.java
new file mode 100644
index 0000000..41448cf
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UmaGrantTypeTest.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2017 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.authz;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.testsuite.util.OAuthClient.AUTH_SERVER_ROOT;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.RefreshToken;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Permission;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.testsuite.util.OAuthClient;
+import org.keycloak.util.BasicAuthHelper;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UmaGrantTypeTest extends AbstractResourceServerTest {
+
+    private ResourceRepresentation resourceA;
+
+    @Before
+    public void configureAuthorization() throws Exception {
+        ClientResource client = getClient(getRealm());
+        AuthorizationResource authorization = client.authorization();
+
+        JSPolicyRepresentation policy = new JSPolicyRepresentation();
+
+        policy.setName("Default Policy");
+        policy.setCode("$evaluation.grant();");
+
+        Response response = authorization.policies().js().create(policy);
+        response.close();
+
+        ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
+        resourceA = addResource("Resource A", "ScopeA", "ScopeB", "ScopeC");
+
+        permission.setName(resourceA.getName() + " Permission");
+        permission.addResource(resourceA.getName());
+        permission.addPolicy(policy.getName());
+
+        response = authorization.permissions().resource().create(permission);
+        response.close();
+    }
+
+    @Test
+    public void testObtainRptWithClientAdditionalScopes() throws Exception {
+        AuthorizationResponse response = authorize("marta", "password", "Resource A", new String[] {"ScopeA", "ScopeB"}, new String[] {"ScopeC"});
+        AccessToken accessToken = toAccessToken(response.getToken());
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB", "ScopeC");
+        assertTrue(permissions.isEmpty());
+    }
+
+    @Test
+    public void testObtainRptWithUpgrade() throws Exception {
+        AuthorizationResponse response = authorize("marta", "password", "Resource A", new String[] {"ScopeA", "ScopeB"});
+        String rpt = response.getToken();
+        AccessToken.Authorization authorization = toAccessToken(rpt).getAuthorization();
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB");
+        assertTrue(permissions.isEmpty());
+
+        response = authorize("marta", "password", "Resource A", new String[] {"ScopeC"}, rpt);
+        assertTrue(response.isUpgraded());
+
+        authorization = toAccessToken(response.getToken()).getAuthorization();
+        permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB", "ScopeC");
+        assertTrue(permissions.isEmpty());
+    }
+
+    @Test
+    public void testObtainRptWithOwnerManagedResource() throws Exception {
+        ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
+        ResourceRepresentation resourceA = addResource("Resource Marta", "marta", true, "ScopeA", "ScopeB", "ScopeC");
+
+        permission.setName(resourceA.getName() + " Permission");
+        permission.addResource(resourceA.getName());
+        permission.addPolicy("Default Policy");
+
+        getClient(getRealm()).authorization().permissions().resource().create(permission).close();
+
+        ResourceRepresentation resourceB = addResource("Resource B", "marta", "ScopeA", "ScopeB", "ScopeC");
+
+        permission.setName(resourceB.getName() + " Permission");
+        permission.addResource(resourceB.getName());
+        permission.addPolicy("Default Policy");
+
+        getClient(getRealm()).authorization().permissions().resource().create(permission).close();
+
+        AuthorizationResponse response = authorize("marta", "password",
+                new PermissionRequest(resourceA.getName(), "ScopeA", "ScopeB"),
+                new PermissionRequest(resourceB.getName(), "ScopeC"));
+        String rpt = response.getToken();
+        AccessToken.Authorization authorization = toAccessToken(rpt).getAuthorization();
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, resourceA.getName(), "ScopeA", "ScopeB");
+        assertPermissions(permissions, resourceB.getName(), "ScopeC");
+        assertTrue(permissions.isEmpty());
+    }
+
+    @Test
+    public void testObtainRptWithClientCredentials() throws Exception {
+        AuthorizationResponse response = authorize("Resource A", new String[] {"ScopeA", "ScopeB"});
+        String rpt = response.getToken();
+
+        assertNotNull(rpt);
+        assertFalse(response.isUpgraded());
+
+        AccessToken accessToken = toAccessToken(rpt);
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB");
+
+        assertTrue(permissions.isEmpty());
+    }
+
+    @Test
+    public void testObtainRptUsingAccessToken() throws Exception {
+        AccessTokenResponse accessTokenResponse = getAuthzClient().obtainAccessToken("marta", "password");
+        AuthorizationResponse response = authorize(null, null, null, null, accessTokenResponse.getToken(), null, null, new PermissionRequest("Resource A", "ScopeA", "ScopeB"));
+        String rpt = response.getToken();
+
+        assertNotNull(rpt);
+        assertFalse(response.isUpgraded());
+
+        AccessToken accessToken = toAccessToken(rpt);
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB");
+        assertTrue(permissions.isEmpty());
+    }
+
+    @Test
+    public void testRefreshRpt() throws Exception {
+        AccessTokenResponse accessTokenResponse = getAuthzClient().obtainAccessToken("marta", "password");
+        AuthorizationResponse response = authorize(null, null, null, null, accessTokenResponse.getToken(), null, null, new PermissionRequest("Resource A", "ScopeA", "ScopeB"));
+        String rpt = response.getToken();
+
+        assertNotNull(rpt);
+
+        AccessToken accessToken = toAccessToken(rpt);
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB");
+        assertTrue(permissions.isEmpty());
+
+        String refreshToken = response.getRefreshToken();
+
+        assertNotNull(refreshToken);
+
+        Client client = ClientBuilder.newClient();
+        UriBuilder builder = UriBuilder.fromUri(AUTH_SERVER_ROOT);
+        URI uri = OIDCLoginProtocolService.tokenUrl(builder).build(REALM_NAME);
+        WebTarget target = client.target(uri);
+
+        Form parameters = new Form();
+
+        parameters.param("grant_type", OAuth2Constants.REFRESH_TOKEN);
+        parameters.param(OAuth2Constants.REFRESH_TOKEN, refreshToken);
+
+        AccessTokenResponse refreshTokenResponse = target.request()
+                .header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader("resource-server-test", "secret"))
+                .post(Entity.form(parameters)).readEntity(AccessTokenResponse.class);
+
+        assertNotNull(refreshTokenResponse.getToken());
+
+        AccessToken refreshedToken = toAccessToken(rpt);
+        authorization = refreshedToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB");
+        assertTrue(permissions.isEmpty());
+    }
+
+    @Test
+    public void testObtainRptWithIDToken() throws Exception {
+        String idToken = getIdToken("marta", "password");
+        AuthorizationResponse response = authorize("Resource A", new String[] {"ScopeA", "ScopeB"}, idToken, "http://openid.net/specs/openid-connect-core-1_0.html#IDToken");
+        String rpt = response.getToken();
+
+        assertNotNull(rpt);
+        assertFalse(response.isUpgraded());
+
+        AccessToken accessToken = toAccessToken(rpt);
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB");
+
+        assertTrue(permissions.isEmpty());
+    }
+
+    private String getIdToken(String username, String password) {
+        oauth.realm("authz-test");
+        oauth.clientId("test-app");
+        oauth.openLoginForm();
+        OAuthClient.AuthorizationEndpointResponse resp = oauth.doLogin(username, password);
+        String code = resp.getCode();
+        OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, password);
+        return response.getIdToken();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedAccessTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedAccessTest.java
new file mode 100644
index 0000000..f8dd3de
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedAccessTest.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2017 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.authz;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.core.Response;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.AuthorizationResource;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.authorization.client.AuthorizationDeniedException;
+import org.keycloak.authorization.client.resource.PermissionResource;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Permission;
+import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
+import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UserManagedAccessTest extends AbstractResourceServerTest {
+
+    private ResourceRepresentation resource;
+
+    @Before
+    public void configureAuthorization() throws Exception {
+        ClientResource client = getClient(getRealm());
+        AuthorizationResource authorization = client.authorization();
+
+        JSPolicyRepresentation policy = new JSPolicyRepresentation();
+
+        policy.setName("Only Owner Policy");
+        policy.setCode("print($evaluation.getPermission().getResource().getOwner());print($evaluation.getContext().getIdentity().getId());if ($evaluation.getContext().getIdentity().getId() == $evaluation.getPermission().getResource().getOwner()) {$evaluation.grant();}");
+
+        Response response = authorization.policies().js().create(policy);
+        response.close();
+    }
+
+    @Test
+    public void testOnlyOwnerCanAccess() throws Exception {
+        ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
+        resource = addResource("Resource A", "marta", true, "ScopeA", "ScopeB");
+
+        permission.setName(resource.getName() + " Permission");
+        permission.addResource(resource.getName());
+        permission.addPolicy("Only Owner Policy");
+
+        getClient(getRealm()).authorization().permissions().resource().create(permission).close();
+
+        AuthorizationResponse response = authorize("marta", "password", resource.getName(), new String[] {"ScopeA", "ScopeB"});
+        String rpt = response.getToken();
+
+        assertNotNull(rpt);
+        assertFalse(response.isUpgraded());
+
+        AccessToken accessToken = toAccessToken(rpt);
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, resource.getName(), "ScopeA", "ScopeB");
+        assertTrue(permissions.isEmpty());
+
+        try {
+            response = authorize("kolo", "password", resource.getName(), new String[] {"ScopeA", "ScopeB"});
+            fail("User should have access to resource from another user");
+        } catch (AuthorizationDeniedException ade) {
+
+        }
+    }
+
+    @Test
+    public void testUserGrantsAccessToResource() throws Exception {
+        ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
+        resource = addResource("Resource A", "marta", true, "ScopeA", "ScopeB");
+
+        permission.setName(resource.getName() + " Permission");
+        permission.addResource(resource.getName());
+        permission.addPolicy("Only Owner Policy");
+
+        getClient(getRealm()).authorization().permissions().resource().create(permission).close();
+
+        AuthorizationResponse response = authorize("marta", "password", "Resource A", new String[] {"ScopeA", "ScopeB"});
+        String rpt = response.getToken();
+
+        assertNotNull(rpt);
+        assertFalse(response.isUpgraded());
+
+        AccessToken accessToken = toAccessToken(rpt);
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB");
+        assertTrue(permissions.isEmpty());
+
+        try {
+            response = authorize("kolo", "password", "Resource A", new String[] {});
+            fail("User should have access to resource from another user");
+        } catch (AuthorizationDeniedException ade) {
+
+        }
+
+        PermissionResource permissionResource = getAuthzClient().protection().permission();
+        List<PermissionTicketRepresentation> permissionTickets = permissionResource.findByResource(resource.getId());
+
+        assertFalse(permissionTickets.isEmpty());
+        assertEquals(2, permissionTickets.size());
+
+        for (PermissionTicketRepresentation ticket : permissionTickets) {
+            assertFalse(ticket.isGranted());
+
+            ticket.setGranted(true);
+
+            permissionResource.update(ticket);
+        }
+
+        permissionTickets = permissionResource.findByResource(resource.getId());
+
+        assertFalse(permissionTickets.isEmpty());
+        assertEquals(2, permissionTickets.size());
+
+        for (PermissionTicketRepresentation ticket : permissionTickets) {
+            assertTrue(ticket.isGranted());
+        }
+
+        response = authorize("kolo", "password", resource.getName(), new String[] {"ScopeA", "ScopeB"});
+        rpt = response.getToken();
+
+        assertNotNull(rpt);
+        assertFalse(response.isUpgraded());
+
+        accessToken = toAccessToken(rpt);
+        authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, resource.getName(), "ScopeA", "ScopeB");
+        assertTrue(permissions.isEmpty());
+    }
+
+    @Test
+    public void testUserGrantsAccessToResourceWithoutScopes() throws Exception {
+        ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
+        resource = addResource("Resource A", "marta", true);
+
+        permission.setName(resource.getName() + " Permission");
+        permission.addResource(resource.getName());
+        permission.addPolicy("Only Owner Policy");
+
+        getClient(getRealm()).authorization().permissions().resource().create(permission).close();
+
+        AuthorizationResponse response = authorize("marta", "password", "Resource A", new String[] {});
+        String rpt = response.getToken();
+
+        assertNotNull(rpt);
+        assertFalse(response.isUpgraded());
+
+        AccessToken accessToken = toAccessToken(rpt);
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A");
+        assertTrue(permissions.isEmpty());
+
+        try {
+            response = authorize("kolo", "password", "Resource A", new String[] {});
+            fail("User should have access to resource from another user");
+        } catch (AuthorizationDeniedException ade) {
+
+        }
+
+        PermissionResource permissionResource = getAuthzClient().protection().permission();
+        List<PermissionTicketRepresentation> permissionTickets = permissionResource.findByResource(resource.getId());
+
+        assertFalse(permissionTickets.isEmpty());
+        assertEquals(1, permissionTickets.size());
+
+        for (PermissionTicketRepresentation ticket : permissionTickets) {
+            assertFalse(ticket.isGranted());
+
+            ticket.setGranted(true);
+
+            permissionResource.update(ticket);
+        }
+
+        permissionTickets = permissionResource.findByResource(resource.getId());
+
+        assertFalse(permissionTickets.isEmpty());
+        assertEquals(1, permissionTickets.size());
+
+        for (PermissionTicketRepresentation ticket : permissionTickets) {
+            assertTrue(ticket.isGranted());
+        }
+
+        response = authorize("kolo", "password", resource.getName(), new String[] {});
+        rpt = response.getToken();
+
+        assertNotNull(rpt);
+        assertFalse(response.isUpgraded());
+
+        accessToken = toAccessToken(rpt);
+        authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, resource.getName());
+        assertTrue(permissions.isEmpty());
+
+        response = authorize("kolo", "password", resource.getName(), new String[] {});
+        rpt = response.getToken();
+
+        assertNotNull(rpt);
+        assertFalse(response.isUpgraded());
+
+        accessToken = toAccessToken(rpt);
+        authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, resource.getName());
+        assertTrue(permissions.isEmpty());
+
+        permissionTickets = permissionResource.findByResource(resource.getId());
+
+        assertFalse(permissionTickets.isEmpty());
+        assertEquals(1, permissionTickets.size());
+
+        for (PermissionTicketRepresentation ticket : permissionTickets) {
+            assertTrue(ticket.isGranted());
+        }
+    }
+
+    @Test
+    public void testUserGrantsAccessToScope() throws Exception {
+        ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
+        resource = addResource("Resource A", "marta", true, "ScopeA", "ScopeB");
+
+        permission.setName(resource.getName() + " Permission");
+        permission.addResource(resource.getName());
+        permission.addPolicy("Only Owner Policy");
+
+        getClient(getRealm()).authorization().permissions().resource().create(permission).close();
+
+        AuthorizationResponse response = authorize("marta", "password", "Resource A", new String[] {"ScopeA", "ScopeB"});
+        String rpt = response.getToken();
+
+        assertNotNull(rpt);
+        assertFalse(response.isUpgraded());
+
+        AccessToken accessToken = toAccessToken(rpt);
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB");
+        assertTrue(permissions.isEmpty());
+
+        try {
+            response = authorize("kolo", "password", "Resource A", new String[] {"ScopeA"});
+            fail("User should have access to resource from another user");
+        } catch (AuthorizationDeniedException ade) {
+
+        }
+
+        PermissionResource permissionResource = getAuthzClient().protection().permission();
+        List<PermissionTicketRepresentation> permissionTickets = permissionResource.findByResource(resource.getId());
+
+        assertFalse(permissionTickets.isEmpty());
+        assertEquals(1, permissionTickets.size());
+
+        PermissionTicketRepresentation ticket = permissionTickets.get(0);
+        assertFalse(ticket.isGranted());
+
+        ticket.setGranted(true);
+
+        permissionResource.update(ticket);
+
+        response = authorize("kolo", "password", resource.getName(), new String[] {"ScopeA", "ScopeB"});
+        rpt = response.getToken();
+
+        assertNotNull(rpt);
+        assertFalse(response.isUpgraded());
+
+        accessToken = toAccessToken(rpt);
+        authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        permissions = authorization.getPermissions();
+
+        assertNotNull(permissions);
+        assertPermissions(permissions, resource.getName(), "ScopeA");
+        assertTrue(permissions.isEmpty());
+
+        permissionTickets = permissionResource.findByResource(resource.getId());
+
+        assertFalse(permissionTickets.isEmpty());
+        // must have two permission tickets, one persisted during the first authorize call for ScopeA and another for the second call to authorize for ScopeB
+        assertEquals(2, permissionTickets.size());
+
+        for (PermissionTicketRepresentation representation : new ArrayList<>(permissionTickets)) {
+            if (representation.isGranted()) {
+                permissionTickets.remove(representation);
+            }
+        }
+
+        assertEquals(1, permissionTickets.size());
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak-uma2.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak-uma2.json
new file mode 100644
index 0000000..7308c71
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak-uma2.json
@@ -0,0 +1,8 @@
+{
+    "realm": "authz-test",
+    "auth-server-url" : "http://localhost:8180/auth",
+    "resource" : "resource-server-test",
+    "credentials": {
+        "secret": "secret"
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
index d631859..a90ff60 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
@@ -331,7 +331,7 @@
                                                 <artifactId>integration-arquillian-test-apps-dist</artifactId>
                                                 <version>${project.version}</version>
                                                 <type>zip</type>
-                                                <includes>**/*realm.json,**/*authz-service.json,**/testsaml.json</includes>
+                                                <includes>**/*realm.json,**/*authz-service.json,**/testsaml.json,**/*-keycloak.json</includes>
                                             </artifactItem>
                                         </artifactItems>
                                         <outputDirectory>${examples.home}</outputDirectory>
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/ResourceForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/ResourceForm.java
index c4d2b2b..59b2177 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/ResourceForm.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/resource/ResourceForm.java
@@ -40,6 +40,9 @@ public class ResourceForm extends Form {
     @FindBy(id = "name")
     private WebElement name;
 
+    @FindBy(id = "displayName")
+    private WebElement displayName;
+
     @FindBy(id = "type")
     private WebElement type;
 
@@ -63,6 +66,7 @@ public class ResourceForm extends Form {
 
     public void populate(ResourceRepresentation expected) {
         setInputValue(name, expected.getName());
+        setInputValue(displayName, expected.getDisplayName());
         setInputValue(type, expected.getType());
         setInputValue(uri, expected.getUri());
         setInputValue(iconUri, expected.getIconUri());
@@ -102,6 +106,7 @@ public class ResourceForm extends Form {
         ResourceRepresentation representation = new ResourceRepresentation();
 
         representation.setName(getInputValue(name));
+        representation.setDisplayName(getInputValue(displayName));
         representation.setType(getInputValue(type));
         representation.setUri(getInputValue(uri));
         representation.setIconUri(getInputValue(iconUri));
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scope.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scope.java
index 5137125..3206078 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scope.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scope.java
@@ -17,6 +17,7 @@
 package org.keycloak.testsuite.console.page.clients.authorization.scope;
 
 import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -29,4 +30,8 @@ public class Scope {
     public ScopeForm form() {
         return form;
     }
+
+    public ScopeRepresentation toRepresentation() {
+        return form.toRepresentation();
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/ScopeForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/ScopeForm.java
index 29ec514..afc6e85 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/ScopeForm.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/ScopeForm.java
@@ -30,6 +30,9 @@ public class ScopeForm extends Form {
     @FindBy(id = "name")
     private WebElement name;
 
+    @FindBy(id = "displayName")
+    private WebElement displayName;
+
     @FindBy(id = "iconUri")
     private WebElement iconUri;
 
@@ -41,6 +44,7 @@ public class ScopeForm extends Form {
 
     public void populate(ScopeRepresentation expected) {
         setInputValue(name, expected.getName());
+        setInputValue(displayName, expected.getDisplayName());
         setInputValue(iconUri, expected.getIconUri());
         save();
     }
@@ -49,4 +53,14 @@ public class ScopeForm extends Form {
         deleteButton.click();
         modalDialog.confirmDeletion();
     }
+
+    public ScopeRepresentation toRepresentation() {
+        ScopeRepresentation representation = new ScopeRepresentation();
+
+        representation.setName(getInputValue(name));
+        representation.setDisplayName(getInputValue(displayName));
+        representation.setIconUri(getInputValue(iconUri));
+
+        return representation;
+    }
 }
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scopes.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scopes.java
index 7df2fb5..06b3201 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scopes.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/authorization/scope/Scopes.java
@@ -16,16 +16,18 @@
  */
 package org.keycloak.testsuite.console.page.clients.authorization.scope;
 
+import static org.keycloak.testsuite.util.UIUtils.clickLink;
+import static org.openqa.selenium.By.tagName;
+
 import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.testsuite.console.page.fragment.ModalDialog;
 import org.keycloak.testsuite.page.Form;
+import org.keycloak.testsuite.util.WaitUtils;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
 
-import static org.keycloak.testsuite.util.UIUtils.clickLink;
-import static org.openqa.selenium.By.tagName;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
@@ -81,4 +83,16 @@ public class Scopes extends Form {
             }
         }
     }
+
+    public Scope name(String name) {
+        for (WebElement row : scopes().rows()) {
+            ScopeRepresentation actual = scopes().toRepresentation(row);
+            if (actual.getName().equalsIgnoreCase(name)) {
+                clickLink(row.findElements(tagName("a")).get(0));
+                WaitUtils.waitForPageToLoad();
+                return scope;
+            }
+        }
+        return null;
+    }
 }
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ResourceManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ResourceManagementTest.java
index 3d29c03..e9e57f4 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ResourceManagementTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ResourceManagementTest.java
@@ -53,6 +53,7 @@ public class ResourceManagementTest extends AbstractAuthorizationSettingsTest {
         String previousName = expected.getName();
 
         expected.setName("changed");
+        expected.setDisplayName("changed");
         expected.setType("changed");
         expected.setUri("changed");
         expected.setScopes(Arrays.asList("Scope A", "Scope B", "Scope C").stream().map(name -> new ScopeRepresentation(name)).collect(Collectors.toSet()));
@@ -93,6 +94,7 @@ public class ResourceManagementTest extends AbstractAuthorizationSettingsTest {
         ResourceRepresentation expected = new ResourceRepresentation();
 
         expected.setName("Test Resource");
+        expected.setDisplayName("Test Display Name");
         expected.setType("Test Type");
         expected.setUri("/test/resource");
 
@@ -112,6 +114,9 @@ public class ResourceManagementTest extends AbstractAuthorizationSettingsTest {
         assertEquals(expected.getIconUri(), actual.getIconUri());
 
         ResourceRepresentation resource = authorizationPage.authorizationTabs().resources().name(expected.getName()).toRepresentation();
+
+        assertEquals(expected.getDisplayName(), resource.getDisplayName());
+
         Set<ScopeRepresentation> associatedScopes = resource.getScopes();
 
         if (expected.getScopes() != null) {
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopeManagementTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopeManagementTest.java
index 9bd5738..8897e63 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopeManagementTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authorization/ScopeManagementTest.java
@@ -21,6 +21,8 @@ import static org.junit.Assert.assertNull;
 
 import org.junit.Test;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.testsuite.console.page.clients.authorization.resource.Resource;
+import org.keycloak.testsuite.console.page.clients.authorization.scope.Scope;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -33,9 +35,11 @@ public class ScopeManagementTest extends AbstractAuthorizationSettingsTest {
         String previousName = expected.getName();
 
         expected.setName("changed");
+        expected.setDisplayName("changed");
 
         authorizationPage.navigateTo();
         authorizationPage.authorizationTabs().scopes().update(previousName, expected);
+
         assertAlertSuccess();
         assertScope(expected);
     }
@@ -62,9 +66,11 @@ public class ScopeManagementTest extends AbstractAuthorizationSettingsTest {
         ScopeRepresentation expected = new ScopeRepresentation();
 
         expected.setName("Test Scope");
+        expected.setDisplayName("Test Scope Display Name");
 
         authorizationPage.authorizationTabs().scopes().create(expected);
         assertAlertSuccess();
+        assertScope(expected);
 
         return expected;
     }
@@ -75,5 +81,9 @@ public class ScopeManagementTest extends AbstractAuthorizationSettingsTest {
 
         assertEquals(expected.getName(), actual.getName());
         assertEquals(expected.getIconUri(), actual.getIconUri());
+
+        ScopeRepresentation scope = authorizationPage.authorizationTabs().scopes().name(expected.getName()).toRepresentation();
+
+        assertEquals(expected.getDisplayName(), scope.getDisplayName());
     }
 }
diff --git a/themes/src/main/resources/theme/base/account/messages/messages_en.properties b/themes/src/main/resources/theme/base/account/messages/messages_en.properties
index 0281d68..93f8a39 100755
--- a/themes/src/main/resources/theme/base/account/messages/messages_en.properties
+++ b/themes/src/main/resources/theme/base/account/messages/messages_en.properties
@@ -123,6 +123,7 @@ missingLastNameMessage=Please specify last name.
 missingEmailMessage=Please specify email.
 missingPasswordMessage=Please specify password.
 notMatchPasswordMessage=Passwords don''t match.
+invalidUserMessage=Invalid user
 
 missingTotpMessage=Please specify authenticator code.
 invalidPasswordExistingMessage=Invalid existing password.
@@ -169,6 +170,31 @@ invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last
 invalidPasswordBlacklistedMessage=Invalid password: password is blacklisted.
 invalidPasswordGenericMessage=Invalid password: new password doesn''t match password policies.
 
+# Authorization
+myResources=My Resources
+myResourcesSub=My resources
+doDeny=Deny
+doRevoke=Revoke
+doApprove=Approve
+doRemoveSharing=Remove Sharing
+doRemoveRequest=Remove Request
+peopleAccessResource=People with access to this resource
+name=Name
+scopes=Scopes
+resource=Resource
+user=User
+peopleSharingThisResource=People sharing this resource
+shareWithOthers=Share with others
+needMyApproval=Need my approval
+requestsWaitingApproval=Your requests waiting approval
+icon=Icon
+requestor=Requestor
+owner=Owner
+resourcesSharedWithMe=Resources shared with me
+permissionRequestion=Permission Requestion
+permission=Permission
+shares=share(s)
+
 locale_ca=Catal\u00E0
 locale_de=Deutsch
 locale_en=English
diff --git a/themes/src/main/resources/theme/base/account/resource-detail.ftl b/themes/src/main/resources/theme/base/account/resource-detail.ftl
new file mode 100755
index 0000000..fd4e5ec
--- /dev/null
+++ b/themes/src/main/resources/theme/base/account/resource-detail.ftl
@@ -0,0 +1,225 @@
+<#import "template.ftl" as layout>
+<@layout.mainLayout active='authorization' bodyClass='authorization'; section>
+
+    <style>
+        .search-box,.close-icon,.search-wrapper {
+            position: relative;
+        }
+        .search-wrapper {
+            width: 500px;
+            margin: auto;
+            margin-top: 50px;
+        }
+        .search-box {
+            font-weight: 600;
+            color: white;
+            border: 1px solid #006e9c;
+            outline: 0;
+            border-radius: 15px;
+            background-color: #0085cf;
+            padding: 2px 5px;
+
+        }
+        .search-box:focus {
+            box-shadow: 0 0 15px 5px #b0e0ee;
+            border: 2px solid #bebede;
+        }
+        .close-icon {
+            border:1px solid transparent;
+            background-color: transparent;
+            display: inline-block;
+            float: right;
+          outline: 0;
+          cursor: pointer;
+        }
+        .close-icon:after {
+            display: block;
+            width: 15px;
+            height: 15px;
+            background-color: #FA9595;
+            z-index:1;
+            right: 35px;
+            top: 0;
+            bottom: 0;
+            margin: auto;
+            padding: 2px;
+            border-radius: 50%;
+            text-align: center;
+            color: white;
+            font-weight: normal;
+            font-size: 12px;
+            box-shadow: 0 0 2px #E50F0F;
+            cursor: pointer;
+        }
+        .search-box:not(:valid) ~ .close-icon {
+            display: none;
+        }
+    </style>
+    <script>
+        function removeScopeElm(elm) {
+            var td = elm.parentNode;
+            var tr = td.parentNode;
+            var tbody = tr.parentNode;
+
+            td.removeChild(elm);
+
+            var childCount = td.childNodes.length - 1;
+
+            for (i = 0; i < td.childNodes.length; i++) {
+                if (!td.childNodes[i].tagName || td.childNodes[i].tagName.toUpperCase() != 'DIV') {
+                    td.removeChild(td.childNodes[i]);
+                    childCount--;
+                }
+            }
+
+            if (childCount <= 0) {
+                tbody.removeChild(tr);
+            }
+        }
+
+        function removeAllScopes(id) {
+            var scopesElm = document.getElementsByName('removeScope-' + id);
+
+            for (i = 0; i < scopesElm.length; i++) {
+                var td = scopesElm[i].parentNode.parentNode;
+                var tr = td.parentNode;
+                var tbody = tr.parentNode;
+                tbody.removeChild(tr);
+            }
+        }
+
+        function getChildren(parent, childId) {
+            var childNodes = [];
+
+            for (i = 0; i < parent.childNodes.length; i++) {
+                if (parent.childNodes[i].id == childId) {
+                    childNodes.push(parent.childNodes[i]);
+                }
+            }
+
+            return childNodes;
+        }
+    </script>
+
+    <div class="row">
+        <div class="col-md-10">
+            <h2>
+                <a href="${url.resourceUrl}">My Resources</a> <i class="fa fa-angle-right"></i> <#if authorization.resource.displayName??>${authorization.resource.displayName}<#else>${authorization.resource.name}</#if>
+            </h2>
+        </div>
+    </div>
+
+    <#if authorization.resource.iconUri??>
+        <img src="${authorization.resource.iconUri}">
+        <br/>
+    </#if>
+
+    <div class="row">
+        <div class="col-md-10">
+            <h3>
+                ${msg("peopleAccessResource")}
+            </h3>
+        </div>
+    </div>
+    <div class="row">
+        <div class="col-md-12">
+                <table class="table table-striped table-bordered">
+                    <thead>
+                        <tr>
+                            <th>${msg("user")}</th>
+                            <th>${msg("permission")}</th>
+                            <th>${msg("date")}</th>
+                            <th>${msg("action")}</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <#if authorization.resource.shares?size != 0>
+                            <#list authorization.resource.shares as permission>
+                                <form action="${url.getResourceGrant(authorization.resource.id)}" name="revokeForm-${authorization.resource.id}-${permission.requester.username}" method="post">
+                                    <input type="hidden" name="action" value="revoke">
+                                    <input type="hidden" name="requester" value="${permission.requester.username}">
+                                    <tr>
+                                        <td>
+                                            <#if permission.requester.email??>${permission.requester.email}<#else>${permission.requester.username}</#if>
+                                        </td>
+                                        <td>
+                                            <#if permission.scopes?size != 0>
+                                                <#list permission.scopes as scope>
+                                                    <#if scope.granted>
+                                                        <div class="search-box">
+                                                            <#if scope.scope.displayName??>
+                                                                ${scope.scope.displayName}
+                                                            <#else>
+                                                                ${scope.scope.name}
+                                                            </#if>
+                                                            <button class="close-icon" type="button" name="removeScope-${authorization.resource.id}-${permission.requester.username}" onclick="removeScopeElm(this.parentNode);document.forms['revokeForm-${authorization.resource.id}-${permission.requester.username}'].submit();"><i class="fa fa-times" aria-hidden="true"></i></button>
+                                                            <input type="hidden" name="permission_id" value="${scope.id}"/>
+                                                        </div>
+                                                    </#if>
+                                                </#list>
+                                            <#else>
+                                                Any action
+                                            </#if>
+                                        </td>
+                                        <td>
+                                            ${permission.createdDate?datetime}
+                                        </td>
+                                        <td width="20%" align="middle" style="vertical-align: middle">
+                                            <a href="#" id="revoke-${authorization.resource.name}-${permission.requester.username}" onclick="removeAllScopes('${authorization.resource.id}-${permission.requester.username}');document.forms['revokeForm-${authorization.resource.id}-${permission.requester.username}'].submit();" type="submit" class="btn btn-primary">${msg("doRevoke")}</a>
+                                        </td>
+                                    </tr>
+                                </form>
+                            </#list>
+                        <#else>
+                            <tr>
+                                <td colspan="4">The resource is not being shared</td>
+                            </tr>
+                        </#if>
+                    </tbody>
+                </table>
+            </form>
+        </div>
+    </div>
+    <div class="row">
+        <div class="col-md-10">
+            <h3>
+                ${msg("shareWithOthers")}
+            </h3>
+        </div>
+    </div>
+    <div class="row">
+        <div class="col-md-10">
+            <form action="${url.getResourceShare(authorization.resource.id)}" name="shareForm" method="post">
+                <div class="col-sm-3 col-md-3">
+                    <label for="password" class="control-label">${msg("username")} or ${msg("email")} </label> <span class="required">*</span>
+                </div>
+                <div class="col-sm-8 col-md-8">
+                    <div class="row">
+                        <div class="col-md-12">
+                            <input type="text" class="form-control" id="user_id" name="user_id" autofocus autocomplete="off">
+                        </div>
+                        <div class="col-md-12">
+                            <br/>
+                            <#list authorization.resource.scopes as scope>
+                                <div id="scope" class="search-box">
+                                    <#if scope.displayName??>
+                                        ${scope.displayName}
+                                    <#else>
+                                        ${scope.name}
+                                    </#if>
+                                    <button class="close-icon" id="share-remove-scope-${authorization.resource.name}-${scope.name}" type="button" onclick="if (getChildren(this.parentNode.parentNode, 'scope').length > 1) {removeScopeElm(this.parentNode)}"><i class="fa fa-times" aria-hidden="true"></i></button>
+                                    <input type="hidden" name="scope_id" value="${scope.id}"/>
+                                </div>
+                            </#list>
+                        </div>
+                        <div class="col-md-12">
+                            <br/>
+                            <a href="#" onclick="document.forms['shareForm'].submit()" type="submit" id="share-button" class="btn btn-primary">${msg("Share")}</a>
+                        </div>
+                    </div>
+                </div>
+            </form>
+        </div>
+    </div>
+    <br/>
+</@layout.mainLayout>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/account/resources.ftl b/themes/src/main/resources/theme/base/account/resources.ftl
new file mode 100755
index 0000000..31b1d3f
--- /dev/null
+++ b/themes/src/main/resources/theme/base/account/resources.ftl
@@ -0,0 +1,385 @@
+<#import "template.ftl" as layout>
+<@layout.mainLayout active='authorization' bodyClass='authorization'; section>
+    <style>
+        .search-box,.close-icon,.search-wrapper {
+            position: relative;
+        }
+        .search-wrapper {
+            width: 500px;
+            margin: auto;
+            margin-top: 50px;
+        }
+        .search-box {
+            font-weight: 600;
+            color: white;
+            border: 1px solid #006e9c;
+            outline: 0;
+            border-radius: 15px;
+            background-color: #0085cf;
+            padding: 2px 5px;
+        }
+        .search-box:focus {
+            box-shadow: 0 0 15px 5px #b0e0ee;
+            border: 2px solid #bebede;
+        }
+        .close-icon {
+            border:1px solid transparent;
+            background-color: transparent;
+            display: inline-block;
+            float: right;
+          outline: 0;
+          cursor: pointer;
+        }
+        .close-icon:after {
+            display: block;
+            width: 15px;
+            height: 15px;
+            background-color: #FA9595;
+            z-index:1;
+            right: 35px;
+            top: 0;
+            bottom: 0;
+            margin: auto;
+            padding: 2px;
+            border-radius: 50%;
+            text-align: center;
+            color: white;
+            font-weight: normal;
+            font-size: 12px;
+            box-shadow: 0 0 2px #E50F0F;
+            cursor: pointer;
+        }
+        .search-box:not(:valid) ~ .close-icon {
+            display: none;
+        }
+    </style>
+    <script>
+        function showHideActions(elm) {
+            if (elm.style.display == 'none') {
+                elm.style.display = '';
+            } else {
+                elm.style.display = 'none';
+            }
+        }
+        function removeScopeElm(elm) {
+            var td = elm.parentNode;
+            var tr = td.parentNode;
+            var tbody = tr.parentNode;
+
+            td.removeChild(elm);
+
+            var childCount = td.childNodes.length - 1;
+
+            for (i = 0; i < td.childNodes.length; i++) {
+                if (!td.childNodes[i].tagName || td.childNodes[i].tagName.toUpperCase() != 'DIV') {
+                    td.removeChild(td.childNodes[i]);
+                    childCount--;
+                }
+            }
+
+            if (childCount <= 0) {
+                tbody.removeChild(tr);
+            }
+        }
+
+        function removeAllScopes(id) {
+            var scopesElm = document.getElementsByName('removeScope-' + id);
+
+            for (i = 0; i < scopesElm.length; i++) {
+                var td = scopesElm[i].parentNode.parentNode;
+                var tr = td.parentNode;
+                var tbody = tr.parentNode;
+                tbody.removeChild(tr);
+            }
+        }
+
+        function selectAllCheckBoxes(formName, elm, name) {
+            var shares = document.forms[formName].getElementsByTagName('input');
+
+            for (i = 0; i < shares.length; i++) {
+                if (shares[i].name == name) {
+                    shares[i].checked = elm.checked;
+                }
+            }
+        }
+    </script>
+    <div class="row">
+        <div class="col-md-10">
+            <h2>
+                ${msg("myResources")}
+            </h2>
+        </div>
+    </div>
+
+    <#if authorization.resourcesWaitingApproval?size != 0>
+        <div class="row">
+            <div class="col-md-12">
+                <h3>
+                    ${msg("needMyApproval")}
+                </h3>
+            </div>
+        </div>
+        <div class="row">
+            <div class="col-md-12">
+                <table class="table table-striped table-bordered">
+                    <thead>
+                        <tr>
+                            <th>${msg("resource")}</th>
+                            <th>${msg("requestor")}</th>
+                            <th>${msg("permissionRequestion")}</th>
+                            <th>${msg("action")}</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <#list authorization.resourcesWaitingApproval as resource>
+                            <#list resource.permissions as permission>
+                                <form action="${url.getResourceGrant(resource.id)}" name="approveForm-${resource.id}-${permission.requester.username}" method="post">
+                                    <input type="hidden" name="action" value="grant">
+                                    <input type="hidden" name="requester" value="${permission.requester.username}">
+                                    <tr>
+                                        <td>
+                                            <#if resource.displayName??>${resource.displayName}<#else>${resource.name}</#if>
+                                        </td>
+                                        <td>
+                                            <#if permission.requester.email??>${permission.requester.email}<#else>${permission.requester.username}</#if>
+                                        </td>
+                                        <td>
+                                            <#list permission.scopes as scope>
+                                                <div class="search-box">
+                                                    <#if scope.scope.displayName??>
+                                                        ${scope.scope.displayName}
+                                                    <#else>
+                                                        ${scope.scope.name}
+                                                    </#if>
+                                                    <button class="close-icon" type="button" id="grant-remove-scope-${resource.name}-${permission.requester.username}-${scope.scope.name}" name="removeScope-${resource.id}-${permission.requester.username}" onclick="removeScopeElm(this.parentNode);document.forms['approveForm-${resource.id}-${permission.requester.username}']['action'].value = 'deny';document.forms['approveForm-${resource.id}-${permission.requester.username}'].submit();"><i class="fa fa-times" aria-hidden="true"></i></button>
+                                                    <input type="hidden" name="permission_id" value="${scope.id}"/>
+                                                </div>
+                                            </#list>
+                                        </td>
+                                        <td width="20%" align="middle" style="vertical-align: middle">
+                                            <a href="#" id="grant-${resource.name}-${permission.requester.username}" onclick="document.forms['approveForm-${resource.id}-${permission.requester.username}']['action'].value = 'grant';document.forms['approveForm-${resource.id}-${permission.requester.username}'].submit();" type="submit" class="btn btn-primary">${msg("doApprove")}</a>
+                                            <a href="#" id="deny-${resource.name}-${permission.requester.username}" onclick="removeAllScopes('${resource.id}-${permission.requester.username}');document.forms['approveForm-${resource.id}-${permission.requester.username}']['action'].value = 'deny';document.forms['approveForm-${resource.id}-${permission.requester.username}'].submit();" type="submit" class="btn btn-danger">${msg("doDeny")}</a>
+                                        </td>
+                                    </tr>
+                                </form>
+                            </#list>
+                        </#list>
+                    </tbody>
+                </table>
+            </div>
+        </div>
+    </#if>
+
+    <div class="row">
+        <div class="col-md-12">
+            <h3>
+                ${msg("myResourcesSub")}
+            </h3>
+        </div>
+    </div>
+    <div class="row">
+        <div class="col-md-12">
+            <table class="table table-striped table-bordered">
+                <thead>
+                <tr>
+                    <th>${msg("resource")}</th>
+                    <th>${msg("application")}</th>
+                    <th>${msg("peopleSharingThisResource")}</th>
+                </tr>
+                </thead>
+
+                <tbody>
+                <#if authorization.resources?size != 0>
+                    <#list authorization.resources as resource>
+                        <tr>
+                            <td>
+                                <a id="detail-${resource.name}" href="${url.getResourceDetailUrl(resource.id)}">
+                                    <#if resource.displayName??>${resource.displayName}<#else>${resource.name}</#if>
+                                </a>
+                            </td>
+                            <td>
+                                <a href="${resource.resourceServer.redirectUri}">${resource.resourceServer.name}</a>
+                            </td>
+                            <td>
+                                <#if resource.shares?size != 0>
+                                    <a href="${url.getResourceDetailUrl(resource.id)}">${resource.shares?size} <i class="fa fa-users"></i></a>
+                                <#else>
+                                    This resource is not being shared.
+                                </#if>
+                            </td>
+                        </tr>
+                    </#list>
+                <#else>
+                    <tr>
+                        <td colspan="4">You don't have any resource</td>
+                    </tr>
+                </#if>
+                </tbody>
+            </table>
+        </div>
+    </div>
+
+    <div class="row">
+        <div class="col-md-12">
+            <h3>
+                ${msg("resourcesSharedWithMe")}
+            </h3>
+        </div>
+    </div>
+    <div class="row">
+        <div class="col-md-12">
+            <form action="${url.resourceUrl}" name="shareForm" method="post">
+                <input type="hidden" name="action" value="cancel"/>
+                <table class="table table-striped table-bordered">
+                    <thead>
+                        <tr>
+                            <th width="5%"><input type="checkbox" onclick="selectAllCheckBoxes('shareForm', this, 'resource_id');" <#if authorization.sharedResources?size == 0>disabled="true"</#if></td>
+                            <th>${msg("resource")}</th>
+                            <th>${msg("owner")}</th>
+                            <th>${msg("application")}</th>
+                            <th>${msg("permission")}</th>
+                            <th>${msg("date")}</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <#if authorization.sharedResources?size != 0>
+                            <#list authorization.sharedResources as resource>
+                                <tr>
+                                    <td>
+                                        <input type="checkbox" name="resource_id" value="${resource.id}"/>
+                                    </td>
+                                    <td>
+                                        <#if resource.displayName??>${resource.displayName}<#else>${resource.name}</#if>
+                                    </td>
+                                    <td>
+                                        <#if resource.owner.email??>${resource.owner.email}<#else>${resource.owner.username}</#if>
+                                    </td>
+                                    <td>
+                                        <a href="${resource.resourceServer.redirectUri}">${resource.resourceServer.name}</a>
+                                    </td>
+                                    <td>
+                                        <#if resource.permissions?size != 0>
+                                            <ul>
+                                                <#list resource.permissions as permission>
+                                                    <#list permission.scopes as scope>
+                                                        <#if scope.granted>
+                                                            <li>
+                                                                <#if scope.scope.displayName??>
+                                                                    ${scope.scope.displayName}
+                                                                <#else>
+                                                                    ${scope.scope.name}
+                                                                </#if>
+                                                            </li>
+                                                        </#if>
+                                                    </#list>
+                                                </#list>
+                                            </ul>
+                                        <#else>
+                                            Any action
+                                        </#if>
+                                    </td>
+                                    <td>
+                                        ${resource.permissions[0].grantedDate?datetime}
+                                    </td>
+                                </tr>
+                            </#list>
+                        <#else>
+                            <tr>
+                                <td colspan="5">There are no resources shared with you</td>
+                            </tr>
+                        </#if>
+                    </tbody>
+                </table>
+            </form>
+        </div>
+        <#if authorization.sharedResources?size != 0>
+        <div class="col-md-12">
+            <a href="#" onclick="document.forms['shareForm'].submit();" type="submit" class="btn btn-danger">${msg("doRemoveSharing")}</a>
+        </div>
+        </#if>
+    </div>
+
+    <#if authorization.resourcesWaitingOthersApproval?size != 0>
+        <br/>
+        <div class="row">
+            <div class="col-md-12">
+                <h3>
+                    ${msg("requestsWaitingApproval")}
+                </h3>
+            </div>
+        </div>
+        <div class="row">
+            <div class="col-md-12">
+                <#if authorization.resourcesWaitingOthersApproval?size != 0>
+                    <i class="pficon pficon-info"></i> You have ${authorization.resourcesWaitingOthersApproval?size} permission request(s) <a href="#" onclick="document.getElementById('waitingApproval').style.display=''">waiting</a> for approval.
+                <#else>
+                    You have no permission requests waiting for approval.
+                </#if>
+                <div class="row">
+                    <div class="col-md-12"></div>
+                </div>
+                <div class="row">
+                    <div class="col-md-12"></div>
+                </div>
+                <div class="row">
+                    <div class="col-md-12"></div>
+                </div>
+                <div class="row" id="waitingApproval" style="display:none">
+                    <div class="col-md-12">
+                        <form action="${url.resourceUrl}" name="waitingApprovalForm" method="post">
+                            <input type="hidden" name="action" value="cancelRequest"/>
+                            <table class="table table-striped table-bordered">
+                                <thead>
+                                    <tr>
+                                        <th width="5%"><input type="checkbox" onclick="selectAllCheckBoxes('waitingApprovalForm', this, 'resource_id');" <#if authorization.resourcesWaitingOthersApproval?size == 0>disabled="true"</#if></th>
+                                        <th>${msg("resource")}</th>
+                                        <th>${msg("owner")}</th>
+                                        <th>${msg("action")}</th>
+                                        <th>${msg("date")}</th>
+                                    </tr>
+                                </thead>
+                                <tbody>
+                                    <#list authorization.resourcesWaitingOthersApproval as resource>
+                                        <tr>
+                                            <td>
+                                                <input type="checkbox" name="resource_id" value="${resource.id}"/>
+                                            </td>
+                                            <td>
+                                                <#if resource.displayName??>${resource.displayName}<#else>${resource.name}</#if>
+                                            </td>
+                                            <td>
+                                                <#if resource.owner.email??>${resource.owner.email}<#else>${resource.owner.username}</#if>
+                                            </td>
+                                            <td>
+                                                <ul>
+                                                    <#list resource.permissions as permission>
+                                                        <#list permission.scopes as scope>
+                                                            <li>
+                                                                <#if scope.scope.displayName??>
+                                                                    ${scope.scope.displayName}
+                                                                <#else>
+                                                                    ${scope.scope.name}
+                                                                </#if>
+                                                            </li>
+                                                        </#list>
+                                                    </#list>
+                                                </ul>
+                                            </td>
+                                            <td>
+                                                ${resource.permissions[0].createdDate?datetime}
+                                            </td>
+                                        </tr>
+                                    </#list>
+                                </tbody>
+                            </table>
+                        </form>
+                    </div>
+                    <div class="col-md-12">
+                        <a href="#" onclick="document.forms['waitingApprovalForm'].submit();" type="submit" class="btn btn-danger">${msg("doRemoveRequest")}</a>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </#if>
+
+</@layout.mainLayout>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/account/template.ftl b/themes/src/main/resources/theme/base/account/template.ftl
index aa0ca41..e49c664 100644
--- a/themes/src/main/resources/theme/base/account/template.ftl
+++ b/themes/src/main/resources/theme/base/account/template.ftl
@@ -61,6 +61,7 @@
                 <li class="<#if active=='sessions'>active</#if>"><a href="${url.sessionsUrl}">${msg("sessions")}</a></li>
                 <li class="<#if active=='applications'>active</#if>"><a href="${url.applicationsUrl}">${msg("applications")}</a></li>
                 <#if features.log><li class="<#if active=='log'>active</#if>"><a href="${url.logUrl}">${msg("log")}</a></li></#if>
+                <#if realm.userManagedAccessAllowed && features.authorization><li class="<#if active=='authorization'>active</#if>"><a href="${url.resourceUrl}">${msg("myResources")}</a></li></#if>
             </ul>
         </div>
 
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 9dcf980..ba0412f 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -24,6 +24,8 @@ endpoints=Endpoints
 # Realm settings
 realm-detail.enabled.tooltip=Users and clients can only access a realm if it's enabled
 realm-detail.oidc-endpoints.tooltip=Shows the configuration of the OpenID Connect endpoints
+realm-detail.userManagedAccess.tooltip=If enabled, users are allowed to manage their resources and permissions using the Account Management Console.
+userManagedAccess=User-Managed Access
 registrationAllowed=User registration
 registrationAllowed.tooltip=Enable/disable the registration page. A link for registration will show on login page too.
 registrationEmailAsUsername=Email as username
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html
index e3bda1e..c68b0e2 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-resource-detail.html
@@ -22,6 +22,13 @@
                 </div>
                 <kc-tooltip>{{:: 'authz-resource-name.tooltip' | translate}}</kc-tooltip>
             </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="name">{{:: 'displayName' | translate}} <span class="required" data-ng-show="create">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="displayName" name="displayName" data-ng-model="resource.displayName">
+                </div>
+                <kc-tooltip>{{:: 'authz-resource-name.tooltip' | translate}}</kc-tooltip>
+            </div>
             <div class="form-group" data-ng-hide="create">
                 <label class="col-md-2 control-label" for="resource.owner.name">{{:: 'authz-owner' | translate}} </label>
                 <div class="col-sm-6">
@@ -59,6 +66,13 @@
                 </div>
                 <kc-tooltip>{{:: 'authz-icon-uri.tooltip' | translate}}</kc-tooltip>
             </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="resource.ownerManagedAccess">User-Managed Access Enabled</label>
+                <div class="col-md-6">
+                    <input ng-model="resource.ownerManagedAccess" id="resource.ownerManagedAccess" onoffswitch />
+                </div>
+                <kc-tooltip>{{:: 'authz-permission-resource-apply-to-resource-type.tooltip' | translate}}</kc-tooltip>
+            </div>
         </fieldset>
 
         <div class="form-group" data-ng-show="access.manageAuthorization">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-detail.html
index a505bbc..d296abd 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/resource-server-scope-detail.html
@@ -23,6 +23,13 @@
                 <kc-tooltip>{{:: 'authz-scope-name.tooltip' | translate}}</kc-tooltip>
             </div>
             <div class="form-group">
+                <label class="col-md-2 control-label" for="displayName">{{:: 'displayName' | translate}} </label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="displayName" name="displayName" data-ng-model="scope.displayName">
+                </div>
+                <kc-tooltip>{{:: 'authz-scope-name.tooltip' | translate}}</kc-tooltip>
+            </div>
+            <div class="form-group">
                 <label class="col-md-2 control-label" for="name">{{:: 'authz-icon-uri' | translate}} </label>
                 <div class="col-sm-6">
                     <input class="form-control" type="text" id="iconUri" name="name" data-ng-model="scope.iconUri" autofocus>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html
index 88416f3..dadb0c7 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-detail.html
@@ -32,6 +32,14 @@
             </div>
 
             <div class="form-group">
+                <label class="col-md-2 control-label" for="enabled">{{:: 'userManagedAccess' | translate}}</label>
+                <div class="col-md-6">
+                    <input ng-model="realm.userManagedAccessAllowed" name="userManagedAccessAllowed" id="userManagedAccessAllowed" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
+                </div>
+                <kc-tooltip>{{:: 'realm-detail.userManagedAccess.tooltip' | translate}}</kc-tooltip>
+            </div>
+
+            <div class="form-group">
                 <label class="col-md-2 control-label">{{:: 'endpoints' | translate}}</label>
                 <div class="col-md-6">
                     <a class="form-control" ng-href="{{authUrl}}/realms/{{realm.realm}}/.well-known/openid-configuration" target="_blank">OpenID Endpoint Configuration</a>