keycloak-uncached

Details

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 e0a8a99..64f0f6f 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
@@ -25,8 +25,8 @@ 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.Permission;
 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>
@@ -80,13 +80,13 @@ public class HttpMethodAuthenticator<R> {
         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("rpt", request.getRptToken());
         method.param("scope", request.getScope());
         method.param("audience", request.getAudience());
         method.param("subject_token", request.getSubjectToken());
 
         if (permissions != null) {
-            for (ResourcePermission permission : permissions.getResources()) {
+            for (Permission permission : permissions.getPermissions()) {
                 String resourceId = permission.getResourceId();
                 Set<String> scopes = permission.getScopes();
                 StringBuilder value = new StringBuilder();
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
index 90cba85..a50bf2d 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationRequest.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationRequest.java
@@ -23,7 +23,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 
-import org.keycloak.representations.idm.authorization.PermissionTicketToken.ResourcePermission;
+import org.keycloak.representations.AccessToken;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -31,7 +31,6 @@ import org.keycloak.representations.idm.authorization.PermissionTicketToken.Reso
 public class AuthorizationRequest {
 
     private String ticket;
-    private String rpt;
     private String claimToken;
     private String claimTokenFormat;
     private String pct;
@@ -42,6 +41,8 @@ public class AuthorizationRequest {
     private String subjectToken;
     private boolean submitRequest;
     private Map<String, List<String>> claims;
+    private AccessToken rpt;
+    private String rptToken;
 
     public AuthorizationRequest(String ticket) {
         this.ticket = ticket;
@@ -59,14 +60,22 @@ public class AuthorizationRequest {
         this.ticket = ticket;
     }
 
-    public String getRpt() {
+    public AccessToken getRpt() {
         return this.rpt;
     }
 
-    public void setRpt(String rpt) {
+    public void setRpt(AccessToken rpt) {
         this.rpt = rpt;
     }
 
+    public void setRpt(String rpt) {
+        this.rptToken = rpt;
+    }
+
+    public String getRptToken() {
+        return rptToken;
+    }
+
     public void setClaimToken(String claimToken) {
         this.claimToken = claimToken;
     }
@@ -145,12 +154,12 @@ public class AuthorizationRequest {
 
     public void addPermission(String resourceId, String... scopes) {
         if (permissions == null) {
-            permissions = new PermissionTicketToken(new ArrayList<ResourcePermission>());
+            permissions = new PermissionTicketToken(new ArrayList<Permission>());
         }
 
-        ResourcePermission permission = null;
+        Permission permission = null;
 
-        for (ResourcePermission resourcePermission : permissions.getResources()) {
+        for (Permission resourcePermission : permissions.getPermissions()) {
             if (resourcePermission.getResourceId() != null && resourcePermission.getResourceId().equals(resourceId)) {
                 permission = resourcePermission;
                 break;
@@ -158,8 +167,8 @@ public class AuthorizationRequest {
         }
 
         if (permission == null) {
-            permission = new ResourcePermission(resourceId, new HashSet<String>());
-            permissions.getResources().add(permission);
+            permission = new Permission(resourceId, new HashSet<String>());
+            permissions.getPermissions().add(permission);
         }
 
         permission.getScopes().addAll(Arrays.asList(scopes));
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 53760c3..c1afa01 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
@@ -33,7 +33,7 @@ public class Permission {
     private String resourceId;
 
     @JsonProperty("rsname")
-    private final String resourceName;
+    private String resourceName;
 
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
     private Set<String> scopes;
@@ -45,6 +45,10 @@ public class Permission {
         this(null, null, null, null);
     }
 
+    public Permission(final String resourceId, final Set<String> scopes) {
+        this(resourceId, null, scopes, null);
+    }
+
     public Permission(final String resourceId, String resourceName, final Set<String> scopes, Map<String, Set<String>> claims) {
         this.resourceId = resourceId;
         this.resourceName = resourceName;
@@ -52,10 +56,18 @@ public class Permission {
         this.claims = claims;
     }
 
+    public void setResourceId(String resourceId) {
+        this.resourceId = resourceId;
+    }
+
     public String getResourceId() {
         return this.resourceId;
     }
 
+    public void setResourceName(String resourceName) {
+        this.resourceName = resourceName;
+    }
+
     public String getResourceName() {
         return this.resourceName;
     }
@@ -75,11 +87,29 @@ public class Permission {
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
+        if (o == null || !getClass().isAssignableFrom(o.getClass())) return false;
 
         Permission that = (Permission) o;
 
-        return getResourceId().equals(that.resourceId);
+        if (getResourceId() != null || getResourceName() != null) {
+            if (!getResourceId().equals(that.resourceId)) {
+                return false;
+            }
+
+            if (getScopes().isEmpty() && that.getScopes().isEmpty()) {
+                return true;
+            }
+        } else if (that.resourceId != null) {
+            return false;
+        }
+
+        for (String scope : that.getScopes()) {
+            if (getScopes().contains(scope)) {
+                return true;
+            }
+        }
+
+        return false;
     }
 
     @Override
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/PermissionTicketToken.java b/core/src/main/java/org/keycloak/representations/idm/authorization/PermissionTicketToken.java
index a9f6ba5..b20728d 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/PermissionTicketToken.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/PermissionTicketToken.java
@@ -33,16 +33,16 @@ import org.keycloak.representations.JsonWebToken;
  */
 public class PermissionTicketToken extends JsonWebToken {
 
-    private final List<ResourcePermission> resources;
+    private final List<Permission> permissions;
 
     @JsonDeserialize(using = StringListMapDeserializer.class)
     private Map<String, List<String>> claims;
 
     public PermissionTicketToken() {
-        this(new ArrayList<ResourcePermission>());
+        this(new ArrayList<Permission>());
     }
 
-    public PermissionTicketToken(List<ResourcePermission> resources, String audience, AccessToken accessToken) {
+    public PermissionTicketToken(List<Permission> permissions, String audience, AccessToken accessToken) {
         if (accessToken != null) {
             id(TokenIdGenerator.generateId());
             subject(accessToken.getSubject());
@@ -54,15 +54,15 @@ public class PermissionTicketToken extends JsonWebToken {
         if (audience != null) {
             audience(audience);
         }
-        this.resources = resources;
+        this.permissions = permissions;
     }
 
-    public PermissionTicketToken(List<ResourcePermission> resources) {
+    public PermissionTicketToken(List<Permission> resources) {
         this(resources, null, null);
     }
 
-    public List<ResourcePermission> getResources() {
-        return this.resources;
+    public List<Permission> getPermissions() {
+        return this.permissions;
     }
 
     public Map<String, List<String>> getClaims() {
@@ -72,29 +72,4 @@ public class PermissionTicketToken extends JsonWebToken {
     public void setClaims(Map<String, List<String>> claims) {
         this.claims = claims;
     }
-
-    public static class ResourcePermission {
-
-        @JsonProperty("id")
-        private String resourceId;
-
-        @JsonProperty("scopes")
-        private Set<String> scopes;
-
-        public ResourcePermission() {
-        }
-
-        public ResourcePermission(String resourceId, Set<String> scopes) {
-            this.resourceId = resourceId;
-            this.scopes = scopes;
-        }
-
-        public String getResourceId() {
-            return resourceId;
-        }
-
-        public Set<String> getScopes() {
-            return scopes;
-        }
-    }
 }
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
index 6ab219b..6b671e3 100644
--- 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
@@ -31,11 +31,11 @@ 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.Permission;
 import org.keycloak.representations.idm.authorization.PermissionTicketToken;
 
 /**
@@ -75,12 +75,12 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionResult
 
             if ("uma".equals(policy.getType())) {
                 ResourcePermission grantedPermission = evaluation.getPermission();
-                List<PermissionTicketToken.ResourcePermission> permissions = ticket.getResources();
+                List<Permission> permissions = ticket.getPermissions();
 
-                Iterator<PermissionTicketToken.ResourcePermission> itPermissions = permissions.iterator();
+                Iterator<Permission> itPermissions = permissions.iterator();
 
                 while (itPermissions.hasNext()) {
-                    PermissionTicketToken.ResourcePermission permission = itPermissions.next();
+                    Permission permission = itPermissions.next();
 
                     if (permission.getResourceId().equals(grantedPermission.getResource().getId())) {
                         Set<String> scopes = permission.getScopes();
@@ -109,10 +109,10 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionResult
         if (request.isSubmitRequest()) {
             StoreFactory storeFactory = authorization.getStoreFactory();
             ResourceStore resourceStore = storeFactory.getResourceStore();
-            List<PermissionTicketToken.ResourcePermission> permissions = ticket.getResources();
+            List<Permission> permissions = ticket.getPermissions();
 
             if (permissions != null) {
-                for (PermissionTicketToken.ResourcePermission permission : permissions) {
+                for (Permission permission : permissions) {
                     Resource resource = resourceStore.findById(permission.getResourceId(), resourceServer.getId());
 
                     if (resource == null) {
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 97e6727..05a732f 100644
--- a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
+++ b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
@@ -28,10 +28,7 @@ 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.function.Predicate;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.core.MediaType;
@@ -70,7 +67,6 @@ 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;
@@ -149,7 +145,7 @@ public class AuthorizationTokenService {
         }
 
         // it is not secure to allow public clients to push arbitrary claims because message can be tampered
-        if (isPublicClientRequestingEntitlemesWithClaims(request)) {
+        if (isPublicClientRequestingEntitlementWithClaims(request)) {
             throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Public clients are not allowed to send claims", Status.FORBIDDEN);
         }
 
@@ -163,9 +159,9 @@ public class AuthorizationTokenService {
             KeycloakIdentity identity = KeycloakIdentity.class.cast(evaluationContext.getIdentity());
             List<Result> evaluation;
 
-            if (ticket.getResources().isEmpty() && request.getRpt() == null) {
+            if (ticket.getPermissions().isEmpty() && request.getRpt() == null) {
                 evaluation = evaluateAllPermissions(request, resourceServer, evaluationContext, identity);
-            } else if(!request.getPermissions().getResources().isEmpty()) {
+            } else if(!request.getPermissions().getPermissions().isEmpty()) {
                 evaluation = evaluatePermissions(request, ticket, resourceServer, evaluationContext, identity);
             } else {
                 evaluation = evaluateUserManagedPermissions(request, ticket, resourceServer, evaluationContext, identity);
@@ -173,21 +169,21 @@ public class AuthorizationTokenService {
 
             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);
-                }
-            }
+            if (isGranted(ticket, request, permissions)) {
+                ClientModel targetClient = this.authorization.getRealm().getClientById(resourceServer.getId());
+                AuthorizationResponse response = createAuthorizationResponse(identity, permissions, request, targetClient);
 
-            ClientModel targetClient = this.authorization.getRealm().getClientById(resourceServer.getId());
-            AuthorizationResponse response = new AuthorizationResponse(createRequestingPartyToken(identity, permissions, request, targetClient), request.getRpt() != null);
+                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();
+            }
 
-            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();
+            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);
+            }
         } catch (ErrorResponseException | CorsErrorResponseException cause) {
             if (logger.isDebugEnabled()) {
                 logger.debug("Error while evaluating permissions", cause);
@@ -199,7 +195,7 @@ public class AuthorizationTokenService {
         }
     }
 
-    private boolean isPublicClientRequestingEntitlemesWithClaims(AuthorizationRequest request) {
+    private boolean isPublicClientRequestingEntitlementWithClaims(AuthorizationRequest request) {
         return request.getClaimToken() != null && getKeycloakSession().getContext().getClient().isPublicClient() && request.getTicket() == null;
     }
 
@@ -221,7 +217,7 @@ public class AuthorizationTokenService {
                 .evaluate();
     }
 
-    private AccessTokenResponse createRequestingPartyToken(KeycloakIdentity identity, List<Permission> entitlements, AuthorizationRequest request, ClientModel targetClient) {
+    private AuthorizationResponse createAuthorizationResponse(KeycloakIdentity identity, List<Permission> entitlements, AuthorizationRequest request, ClientModel targetClient) {
         KeycloakSession keycloakSession = getKeycloakSession();
         AccessToken accessToken = identity.getAccessToken();
         UserSessionModel userSessionModel = keycloakSession.sessions().getUserSession(getRealm(), accessToken.getSessionState());
@@ -253,7 +249,31 @@ public class AuthorizationTokenService {
             rpt.audience(targetClient.getClientId());
         }
 
-        return responseBuilder.build();
+        return new AuthorizationResponse(responseBuilder.build(), isUpgraded(request, authorization));
+    }
+
+    private boolean isUpgraded(AuthorizationRequest request, Authorization authorization) {
+        AccessToken previousRpt = request.getRpt();
+
+        if (previousRpt == null) {
+            return false;
+        }
+
+        Authorization previousAuthorization = previousRpt.getAuthorization();
+
+        if (previousAuthorization != null) {
+            List<Permission> previousPermissions = previousAuthorization.getPermissions();
+
+            if (previousPermissions != null) {
+                for (Permission previousPermission : previousPermissions) {
+                    if (!authorization.getPermissions().contains(previousPermission)) {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        return true;
     }
 
     private PermissionTicketToken getPermissionTicket(AuthorizationRequest request) {
@@ -320,7 +340,7 @@ public class AuthorizationTokenService {
         Metadata metadata = request.getMetadata();
         Integer limit = metadata != null ? metadata.getLimit() : null;
 
-        for (PermissionTicketToken.ResourcePermission requestedResource : ticket.getResources()) {
+        for (Permission requestedResource : ticket.getPermissions()) {
             if (limit != null && limit <= 0) {
                 break;
             }
@@ -339,16 +359,19 @@ public class AuthorizationTokenService {
                 if (resource != null) {
                     existingResources.add(resource);
                 } else {
-                    Resource ownerResource = resourceStore.findByName(requestedResource.getResourceId(), identity.getId(), resourceServer.getId());
+                    String resourceName = requestedResource.getResourceId();
+                    Resource ownerResource = resourceStore.findByName(resourceName, identity.getId(), resourceServer.getId());
 
                     if (ownerResource != null) {
+                        requestedResource.setResourceId(ownerResource.getId());
                         existingResources.add(ownerResource);
                     }
 
                     if (!identity.isResourceServer()) {
-                        Resource serverResource = resourceStore.findByName(requestedResource.getResourceId(), resourceServer.getId());
+                        Resource serverResource = resourceStore.findByName(resourceName, resourceServer.getId());
 
                         if (serverResource != null) {
+                            requestedResource.setResourceId(serverResource.getId());
                             existingResources.add(serverResource);
                         }
                     }
@@ -403,63 +426,49 @@ public class AuthorizationTokenService {
             }
         }
 
-        String rpt = request.getRpt();
-
-        if (rpt != null) {
-            if (!Tokens.verifySignature(getKeycloakSession(), getRealm(), rpt)) {
-                throw new CorsErrorResponseException(cors, "invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN);
-            }
+        AccessToken rpt = request.getRpt();
 
-            AccessToken requestingPartyToken;
+        if (rpt != null && rpt.isActive()) {
+            AccessToken.Authorization authorizationData = rpt.getAuthorization();
 
-            try {
-                requestingPartyToken = new JWSInput(rpt).readJsonContent(AccessToken.class);
-            } catch (JWSInputException e) {
-                throw new CorsErrorResponseException(cors, "invalid_rpt", "Invalid RPT", Status.FORBIDDEN);
-            }
+            if (authorizationData != null) {
+                List<Permission> permissions = authorizationData.getPermissions();
 
-            if (requestingPartyToken.isActive()) {
-                AccessToken.Authorization authorizationData = requestingPartyToken.getAuthorization();
-
-                if (authorizationData != null) {
-                    List<Permission> permissions = authorizationData.getPermissions();
+                if (permissions != null) {
+                    for (Permission grantedPermission : permissions) {
+                        if (limit != null && limit <= 0) {
+                            break;
+                        }
 
-                    if (permissions != null) {
-                        for (Permission grantedPermission : permissions) {
-                            if (limit != null && limit <= 0) {
-                                break;
-                            }
+                        Resource resource = resourceStore.findById(grantedPermission.getResourceId(), ticket.getAudience()[0]);
 
-                            Resource resourcePermission = resourceStore.findById(grantedPermission.getResourceId(), ticket.getAudience()[0]);
+                        if (resource != null) {
+                            ResourcePermission permission = permissionsToEvaluate.get(resource.getId());
 
-                            if (resourcePermission != null) {
-                                ResourcePermission permission = permissionsToEvaluate.get(resourcePermission.getId());
+                            if (permission == null) {
+                                permission = new ResourcePermission(resource, new ArrayList<>(), resourceServer, grantedPermission.getClaims());
+                                permissionsToEvaluate.put(resource.getId(), permission);
+                                if (limit != null) {
+                                    limit--;
+                                }
+                            } else {
+                                if (grantedPermission.getClaims() != null) {
+                                    for (Entry<String, Set<String>> entry : grantedPermission.getClaims().entrySet()) {
+                                        Set<String> claims = permission.getClaims().get(entry.getKey());
 
-                                if (permission == null) {
-                                    permission = new ResourcePermission(resourcePermission, new ArrayList<>(), resourceServer, grantedPermission.getClaims());
-                                    permissionsToEvaluate.put(resourcePermission.getId(), permission);
-                                    if (limit != null) {
-                                        limit--;
-                                    }
-                                } else {
-                                    if (grantedPermission.getClaims() != null) {
-                                        for (Entry<String, Set<String>> entry : grantedPermission.getClaims().entrySet()) {
-                                            Set<String> claims = permission.getClaims().get(entry.getKey());
-
-                                            if (claims != null) {
-                                                claims.addAll(entry.getValue());
-                                            }
+                                        if (claims != null) {
+                                            claims.addAll(entry.getValue());
                                         }
                                     }
                                 }
+                            }
 
-                                for (String scopeName : grantedPermission.getScopes()) {
-                                    Scope scope = scopeStore.findByName(scopeName, resourceServer.getId());
+                            for (String scopeName : grantedPermission.getScopes()) {
+                                Scope scope = scopeStore.findByName(scopeName, resourceServer.getId());
 
-                                    if (scope != null) {
-                                        if (!permission.getScopes().contains(scope)) {
-                                            permission.getScopes().add(scope);
-                                        }
+                                if (scope != null) {
+                                    if (!permission.getScopes().contains(scope)) {
+                                        permission.getScopes().add(scope);
                                     }
                                 }
                             }
@@ -492,6 +501,17 @@ public class AuthorizationTokenService {
         }
     }
 
+    private boolean isGranted(PermissionTicketToken ticket, AuthorizationRequest request, List<Permission> permissions) {
+        List<Permission> requestedPermissions = ticket.getPermissions();
+
+        // denies in case a rpt was provided along with the authorization request but any requested permission was not granted
+        if (request.getRpt() != null && !requestedPermissions.isEmpty() && requestedPermissions.stream().anyMatch(permission -> !permissions.contains(permission))) {
+            return false;
+        }
+
+        return !permissions.isEmpty();
+    }
+
     private KeycloakSession getKeycloakSession() {
         return this.authorization.getKeycloakSession();
     }
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 e045d31..2386f12 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
@@ -35,6 +35,7 @@ import org.keycloak.authorization.store.ResourceStore;
 import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeyManager;
+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.PermissionTicketToken;
@@ -63,9 +64,9 @@ public class AbstractPermissionService {
         return Response.status(Response.Status.CREATED).entity(new PermissionResponse(createPermissionTicket(request))).build();
     }
 
-    private List<PermissionTicketToken.ResourcePermission> verifyRequestedResource(List<PermissionRequest> request) {
+    private List<Permission> verifyRequestedResource(List<PermissionRequest> request) {
         ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
-        List<PermissionTicketToken.ResourcePermission> requestedResources = new ArrayList<>();
+        List<Permission> requestedResources = new ArrayList<>();
 
         for (PermissionRequest permissionRequest : request) {
             String resourceSetId = permissionRequest.getResourceId();
@@ -102,10 +103,10 @@ public class AbstractPermissionService {
             }
 
             if (resources.isEmpty()) {
-                requestedResources.add(new PermissionTicketToken.ResourcePermission(null, verifyRequestedScopes(permissionRequest, null)));
+                requestedResources.add(new Permission(null, verifyRequestedScopes(permissionRequest, null)));
             } else {
                 for (Resource resource : resources) {
-                    requestedResources.add(new PermissionTicketToken.ResourcePermission(resource.getId(), verifyRequestedScopes(permissionRequest, resource)));
+                    requestedResources.add(new Permission(resource.getId(), verifyRequestedScopes(permissionRequest, resource)));
                 }
             }
         }
@@ -147,7 +148,7 @@ public class AbstractPermissionService {
     }
 
     private String createPermissionTicket(List<PermissionRequest> request) {
-        List<PermissionTicketToken.ResourcePermission> permissions = verifyRequestedResource(request);
+        List<Permission> permissions = verifyRequestedResource(request);
 
         KeyManager.ActiveRsaKey keys = this.authorization.getKeycloakSession().keys().getActiveRsaKey(this.authorization.getRealm());
         ClientModel targetClient = authorization.getRealm().getClientById(resourceServer.getId());
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 ad92195..67cb0dc 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
@@ -1079,7 +1079,20 @@ public class TokenEndpoint {
         authorizationRequest.setClaimToken(claimToken);
         authorizationRequest.setClaimTokenFormat(claimTokenFormat);
         authorizationRequest.setPct(formParams.getFirst("pct"));
-        authorizationRequest.setRpt(formParams.getFirst("rpt"));
+        String rpt = formParams.getFirst("rpt");
+
+        if (rpt != null) {
+            if (!Tokens.verifySignature(session, realm, rpt)) {
+                throw new CorsErrorResponseException(cors, "invalid_rpt", "RPT signature is invalid", Status.FORBIDDEN);
+            }
+
+            try {
+                authorizationRequest.setRpt(new JWSInput(rpt).readJsonContent(AccessToken.class));
+            } catch (JWSInputException e) {
+                throw new CorsErrorResponseException(cors, "invalid_rpt", "Invalid RPT", Status.FORBIDDEN);
+            }
+        }
+
         authorizationRequest.setScope(formParams.getFirst("scope"));
         authorizationRequest.setAudience(formParams.getFirst("audience"));
         authorizationRequest.setSubjectToken(formParams.getFirst("subject_token") != null ? formParams.getFirst("subject_token") : accessTokenString);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionEqualsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionEqualsTest.java
new file mode 100644
index 0000000..e96fda3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PermissionEqualsTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
+import org.junit.Test;
+import org.keycloak.representations.idm.authorization.Permission;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PermissionEqualsTest {
+
+    @Test
+    public void testEquals() {
+        assertTrue(new Permission("1", null, Collections.emptySet(), Collections.emptyMap()).equals(
+                new Permission("1", null, Collections.emptySet(), Collections.emptyMap())
+        ));
+        assertFalse(new Permission("1", null, Collections.emptySet(), Collections.emptyMap()).equals(
+                new Permission("2", null, Collections.emptySet(), Collections.emptyMap())
+        ));
+        assertFalse(new Permission("1", null, new HashSet<>(Arrays.asList("read", "write")), Collections.emptyMap()).equals(
+                new Permission("1", null, Collections.emptySet(), Collections.emptyMap())
+        ));
+        assertTrue(new Permission("1", null, new HashSet<>(Arrays.asList("read", "write")), Collections.emptyMap()).equals(
+                new Permission("1", null, new HashSet<>(Arrays.asList("read", "write")), Collections.emptyMap())
+        ));
+        assertTrue(new Permission("1", null, new HashSet<>(Arrays.asList("read", "write")), Collections.emptyMap()).equals(
+                new Permission("1", null, new HashSet<>(Arrays.asList("write")), Collections.emptyMap())
+        ));
+        assertFalse(new Permission("1", null, new HashSet<>(Arrays.asList("read")), Collections.emptyMap()).equals(
+                new Permission("1", null, new HashSet<>(Arrays.asList("write")), Collections.emptyMap())
+        ));
+        assertFalse(new Permission(null, null, new HashSet<>(Arrays.asList("write")), Collections.emptyMap()).equals(
+                new Permission("1", null, new HashSet<>(Arrays.asList("write")), Collections.emptyMap())
+        ));
+        assertFalse(new Permission("1", null, new HashSet<>(Arrays.asList("write")), Collections.emptyMap()).equals(
+                new Permission(null, null, new HashSet<>(Arrays.asList("write")), Collections.emptyMap())
+        ));
+        assertTrue(new Permission(null, null, new HashSet<>(Arrays.asList("write")), Collections.emptyMap()).equals(
+                new Permission(null, null, new HashSet<>(Arrays.asList("write")), Collections.emptyMap())
+        ));
+        assertTrue(new Permission(null, null, new HashSet<>(Arrays.asList("read", "write")), Collections.emptyMap()).equals(
+                new Permission(null, null, new HashSet<>(Arrays.asList("read")), Collections.emptyMap())
+        ));
+        assertFalse(new Permission(null, null, new HashSet<>(Arrays.asList("read", "write")), Collections.emptyMap()).equals(
+                new Permission(null, null, new HashSet<>(Arrays.asList("update")), Collections.emptyMap())
+        ));
+        assertFalse(new Permission(null, null, Collections.emptySet(), Collections.emptyMap()).equals(
+                new Permission(null, null, new HashSet<>(Arrays.asList("read")), Collections.emptyMap())
+        ));
+    }
+}
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
index aa9ce50..87172f6 100644
--- 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
@@ -36,6 +36,7 @@ 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.Permission;
 import org.keycloak.representations.idm.authorization.PermissionRequest;
 import org.keycloak.representations.idm.authorization.PermissionResponse;
 import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
@@ -303,14 +304,14 @@ public class PermissionManagementTest extends AbstractResourceServerTest {
 
         PermissionTicketToken token = new JWSInput(ticket).readJsonContent(PermissionTicketToken.class);
 
-        List<PermissionTicketToken.ResourcePermission> tokenPermissions = token.getResources();
+        List<Permission> tokenPermissions = token.getPermissions();
         assertNotNull(tokenPermissions);
         assertEquals(expectedPermissions, scopeNames.length > 0 ? scopeNames.length : tokenPermissions.size());
 
-        Iterator<PermissionTicketToken.ResourcePermission> permissionIterator = tokenPermissions.iterator();
+        Iterator<Permission> permissionIterator = tokenPermissions.iterator();
 
         while (permissionIterator.hasNext()) {
-            PermissionTicketToken.ResourcePermission resourcePermission = permissionIterator.next();
+            Permission 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();
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
index 5d4c1a1..a6be35e 100644
--- 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
@@ -19,6 +19,7 @@ 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.junit.Assert.fail;
 import static org.keycloak.testsuite.util.OAuthClient.AUTH_SERVER_ROOT;
 
 import java.net.URI;
@@ -38,17 +39,18 @@ 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.authorization.client.AuthorizationDeniedException;
+import org.keycloak.models.utils.KeycloakModelUtils;
 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.representations.idm.authorization.ScopePermissionRepresentation;
 import org.keycloak.testsuite.util.OAuthClient;
 import org.keycloak.util.BasicAuthHelper;
 
@@ -81,6 +83,14 @@ public class UmaGrantTypeTest extends AbstractResourceServerTest {
 
         response = authorization.permissions().resource().create(permission);
         response.close();
+
+        policy = new JSPolicyRepresentation();
+
+        policy.setName("Deny Policy");
+        policy.setCode("$evaluation.deny();");
+
+        response = authorization.policies().js().create(policy);
+        response.close();
     }
 
     @Test
@@ -118,6 +128,158 @@ public class UmaGrantTypeTest extends AbstractResourceServerTest {
     }
 
     @Test
+    public void testObtainRptWithUpgradeOnlyScopes() throws Exception {
+        AuthorizationResponse response = authorize("marta", "password", null, new String[] {"ScopeA", "ScopeB"});
+        String rpt = response.getToken();
+        AccessToken.Authorization authorization = toAccessToken(rpt).getAuthorization();
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertFalse(response.isUpgraded());
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB");
+        assertTrue(permissions.isEmpty());
+
+        response = authorize("marta", "password", "Resource A", new String[] {"ScopeC"}, rpt);
+
+        authorization = toAccessToken(response.getToken()).getAuthorization();
+        permissions = authorization.getPermissions();
+
+        assertTrue(response.isUpgraded());
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB", "ScopeC");
+        assertTrue(permissions.isEmpty());
+    }
+
+    @Test
+    public void testObtainRptWithUpgradeWithUnauthorizedResource() 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();
+
+        assertFalse(response.isUpgraded());
+        assertNotNull(permissions);
+        assertPermissions(permissions, "Resource A", "ScopeA", "ScopeB");
+        assertTrue(permissions.isEmpty());
+
+        ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
+        ResourceRepresentation resourceB = addResource("Resource B", "ScopeA", "ScopeB", "ScopeC");
+
+        permission.setName(resourceB.getName() + " Permission");
+        permission.addResource(resourceB.getName());
+        permission.addPolicy("Deny Policy");
+
+        getClient(getRealm()).authorization().permissions().resource().create(permission).close();
+
+        try {
+            authorize("marta", "password", "Resource B", new String[]{"ScopeC"}, rpt);
+            fail("Should be denied, resource b not granted");
+        } catch (AuthorizationDeniedException ignore) {
+
+        }
+    }
+
+    @Test
+    public void testObtainRptWithUpgradeWithUnauthorizedResourceFromRpt() throws Exception {
+        ResourcePermissionRepresentation permissionA = new ResourcePermissionRepresentation();
+        ResourceRepresentation resourceA = addResource(KeycloakModelUtils.generateId(), "ScopeA", "ScopeB", "ScopeC");
+
+        permissionA.setName(resourceA.getName() + " Permission");
+        permissionA.addResource(resourceA.getName());
+        permissionA.addPolicy("Default Policy");
+
+        AuthorizationResource authzResource = getClient(getRealm()).authorization();
+
+        authzResource.permissions().resource().create(permissionA).close();
+        AuthorizationResponse response = authorize("marta", "password", resourceA.getId(), new String[] {"ScopeA", "ScopeB"});
+        String rpt = response.getToken();
+        AccessToken.Authorization authorization = toAccessToken(rpt).getAuthorization();
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertFalse(response.isUpgraded());
+        assertNotNull(permissions);
+        assertPermissions(permissions, resourceA.getName(), "ScopeA", "ScopeB");
+        assertTrue(permissions.isEmpty());
+
+        ResourceRepresentation resourceB = addResource(KeycloakModelUtils.generateId(), "ScopeA", "ScopeB", "ScopeC");
+        ResourcePermissionRepresentation permissionB = new ResourcePermissionRepresentation();
+
+        permissionB.setName(resourceB.getName() + " Permission");
+        permissionB.addResource(resourceB.getName());
+        permissionB.addPolicy("Default Policy");
+
+        authzResource.permissions().resource().create(permissionB).close();
+        response = authorize("marta", "password", resourceB.getId(), new String[] {"ScopeC"}, rpt);
+        rpt = response.getToken();
+        authorization = toAccessToken(rpt).getAuthorization();
+        permissions = authorization.getPermissions();
+
+        assertTrue(response.isUpgraded());
+        assertNotNull(permissions);
+        assertPermissions(permissions, resourceA.getName(), "ScopeA", "ScopeB");
+        assertPermissions(permissions, resourceB.getName(), "ScopeC");
+        assertTrue(permissions.isEmpty());
+
+        permissionB = authzResource.permissions().resource().findByName(permissionB.getName());
+        permissionB.removePolicy("Default Policy");
+        permissionB.addPolicy("Deny Policy");
+
+        authzResource.permissions().resource().findById(permissionB.getId()).update(permissionB);
+
+        response = authorize("marta", "password", resourceA.getId(), new String[] {"ScopeC"}, rpt);
+        rpt = response.getToken();
+        authorization = toAccessToken(rpt).getAuthorization();
+        permissions = authorization.getPermissions();
+
+        assertFalse(response.isUpgraded());
+        assertNotNull(permissions);
+        assertPermissions(permissions, resourceA.getName(), "ScopeA", "ScopeB", "ScopeC");
+        assertTrue(permissions.isEmpty());
+    }
+
+    @Test
+    public void testObtainRptOnlyAuthorizedScopes() throws Exception {
+        ResourceRepresentation resourceA = addResource(KeycloakModelUtils.generateId(), "READ", "WRITE");
+        ScopePermissionRepresentation permissionA = new ScopePermissionRepresentation();
+
+        permissionA.setName(KeycloakModelUtils.generateId());
+        permissionA.addScope("READ");
+        permissionA.addPolicy("Default Policy");
+
+        AuthorizationResource authzResource = getClient(getRealm()).authorization();
+
+        authzResource.permissions().scope().create(permissionA).close();
+
+        ScopePermissionRepresentation permissionB = new ScopePermissionRepresentation();
+
+        permissionB.setName(KeycloakModelUtils.generateId());
+        permissionB.addScope("WRITE");
+        permissionB.addPolicy("Deny Policy");
+
+        authzResource.permissions().scope().create(permissionB).close();
+
+        AuthorizationResponse response = authorize("marta", "password", resourceA.getName(), new String[] {"READ"});
+        String rpt = response.getToken();
+        AccessToken.Authorization authorization = toAccessToken(rpt).getAuthorization();
+        List<Permission> permissions = authorization.getPermissions();
+
+        assertFalse(response.isUpgraded());
+        assertNotNull(permissions);
+        assertPermissions(permissions, resourceA.getName(), "READ");
+        assertTrue(permissions.isEmpty());
+
+        response = authorize("marta", "password", resourceA.getName(), new String[] {"READ", "WRITE"});
+        rpt = response.getToken();
+        authorization = toAccessToken(rpt).getAuthorization();
+        permissions = authorization.getPermissions();
+
+        assertFalse(response.isUpgraded());
+        assertNotNull(permissions);
+        assertPermissions(permissions, resourceA.getName(), "READ");
+        assertTrue(permissions.isEmpty());
+    }
+
+    @Test
     public void testObtainRptWithOwnerManagedResource() throws Exception {
         ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
         ResourceRepresentation resourceA = addResource("Resource Marta", "marta", true, "ScopeA", "ScopeB", "ScopeC");