keycloak-uncached

Merge pull request #4077 from pedroigor/KEYCLOAK-4792 [KEYCLOAK-4792]

4/25/2017 6:03:32 PM

Changes

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 1ba9cc5..61f46f1 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
@@ -29,6 +29,7 @@ import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.adapters.OIDCHttpFacade;
 import org.keycloak.adapters.spi.HttpFacade.Request;
 import org.keycloak.authorization.client.AuthzClient;
+import org.keycloak.authorization.client.ClientAuthorizationContext;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
 import org.keycloak.representations.adapters.config.PolicyEnforcerConfig.EnforcementMode;
@@ -203,7 +204,7 @@ public abstract class AbstractPolicyEnforcer {
     }
 
     private AuthorizationContext createEmptyAuthorizationContext(final boolean granted) {
-        return new AuthorizationContext() {
+        return new ClientAuthorizationContext(authzClient) {
             @Override
             public boolean hasPermission(String resourceName, String scopeName) {
                 return granted;
@@ -252,7 +253,7 @@ public abstract class AbstractPolicyEnforcer {
     }
 
     private AuthorizationContext createAuthorizationContext(AccessToken accessToken) {
-        return new AuthorizationContext(accessToken, this.paths);
+        return new ClientAuthorizationContext(accessToken, this.paths, authzClient);
     }
 
     private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
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 679a33c..7f21eef 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
@@ -21,7 +21,9 @@ import org.jboss.logging.Logger;
 import org.keycloak.AuthorizationContext;
 import org.keycloak.adapters.KeycloakDeployment;
 import org.keycloak.adapters.OIDCHttpFacade;
+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;
@@ -56,7 +58,12 @@ 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()));
+        this.authzClient = AuthzClient.create(new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient()), new ClientAuthenticator() {
+            @Override
+            public void configureClientCredentials(HashMap<String, String> requestParams, HashMap<String, String> requestHeaders) {
+                ClientCredentialsProviderUtils.setClientCredentials(PolicyEnforcer.this.deployment, requestHeaders, requestParams);
+            }
+        });
         this.pathMatcher = new PathMatcher(this.authzClient);
         this.paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
 
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 0221af6..5ed1306 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
@@ -53,13 +53,17 @@ public class AuthzClient {
     }
 
     public static AuthzClient create(Configuration configuration) {
-        return new AuthzClient(configuration);
+        return new AuthzClient(configuration, configuration.getClientAuthenticator());
+    }
+
+    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) {
+    private AuthzClient(Configuration configuration, ClientAuthenticator authenticator) {
         if (configuration == null) {
             throw new IllegalArgumentException("Client configuration can not be null.");
         }
@@ -72,7 +76,9 @@ public class AuthzClient {
 
         configurationUrl += "/realms/" + configuration.getRealm() + "/.well-known/uma-configuration";
 
-        this.http = new Http(configuration);
+        this.deployment = configuration;
+
+        this.http = new Http(configuration, authenticator != null ? authenticator : configuration.getClientAuthenticator());
 
         try {
             this.serverConfiguration = this.http.<ServerConfiguration>get(URI.create(configurationUrl))
@@ -83,8 +89,10 @@ public class AuthzClient {
         }
 
         this.http.setServerConfiguration(this.serverConfiguration);
+    }
 
-        this.deployment = configuration;
+    private AuthzClient(Configuration configuration) {
+        this(configuration, null);
     }
 
     public ProtectionResource protection() {
@@ -106,7 +114,7 @@ public class AuthzClient {
     public AccessTokenResponse obtainAccessToken() {
         return this.http.<AccessTokenResponse>post(this.serverConfiguration.getTokenEndpoint())
                 .authentication()
-                    .oauth2ClientCredentials()
+                    .client()
                 .response()
                     .json(AccessTokenResponse.class)
                 .execute();
diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthorizationContext.java b/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthorizationContext.java
new file mode 100644
index 0000000..73bcd9f
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/ClientAuthorizationContext.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.authorization.client;
+
+import java.util.Map;
+
+import org.keycloak.AuthorizationContext;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
+
+/**
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ClientAuthorizationContext extends AuthorizationContext {
+
+    private final AuthzClient client;
+
+    public ClientAuthorizationContext(AccessToken authzToken, Map<String, PolicyEnforcerConfig.PathConfig> paths, AuthzClient client) {
+        super(authzToken, paths);
+        this.client = client;
+    }
+
+    public ClientAuthorizationContext(AuthzClient client) {
+        this.client = client;
+    }
+
+    public AuthzClient getClient() {
+        return client;
+    }
+}
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 536b188..fc24f0e 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
@@ -48,7 +48,7 @@ public class ProtectionResource {
     public TokenIntrospectionResponse introspectRequestingPartyToken(String rpt) {
         return this.http.<TokenIntrospectionResponse>post("/protocol/openid-connect/token/introspect")
                 .authentication()
-                    .oauth2ClientCredentials()
+                    .client()
                 .form()
                     .param("token_type_hint", "requesting_party_token")
                     .param("token", rpt)
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 8bd9c07..f72e6b7 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
@@ -18,6 +18,7 @@
 package org.keycloak.authorization.client.util;
 
 import org.apache.http.client.methods.RequestBuilder;
+import org.keycloak.authorization.client.ClientAuthenticator;
 import org.keycloak.authorization.client.Configuration;
 import org.keycloak.authorization.client.representation.ServerConfiguration;
 
@@ -29,10 +30,12 @@ import java.net.URI;
 public class Http {
 
     private final Configuration configuration;
+    private final ClientAuthenticator authenticator;
     private ServerConfiguration serverConfiguration;
 
-    public Http(Configuration configuration) {
+    public Http(Configuration configuration, ClientAuthenticator authenticator) {
         this.configuration = configuration;
+        this.authenticator = authenticator;
     }
 
     public <R> HttpMethod<R> get(String path) {
@@ -60,7 +63,7 @@ public class Http {
     }
 
     private <R> HttpMethod<R> method(RequestBuilder builder) {
-        return new HttpMethod(this.configuration, builder);
+        return new HttpMethod(this.configuration, authenticator, builder);
     }
 
     public void setServerConfiguration(ServerConfiguration serverConfiguration) {
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 10d450e..9a7e51a 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
@@ -17,6 +17,12 @@
  */
 package org.keycloak.authorization.client.util;
 
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.NameValuePair;
@@ -27,33 +33,30 @@ import org.apache.http.client.methods.RequestBuilder;
 import org.apache.http.entity.ByteArrayEntity;
 import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.util.EntityUtils;
+import org.keycloak.authorization.client.ClientAuthenticator;
 import org.keycloak.authorization.client.Configuration;
 
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
 public class HttpMethod<R> {
 
     private final HttpClient httpClient;
+    private final ClientAuthenticator authenticator;
     private final RequestBuilder builder;
     protected final Configuration configuration;
     protected final HashMap<String, String> headers;
     protected final HashMap<String, String> params;
     private HttpMethodResponse<R> response;
 
-    public HttpMethod(Configuration configuration, RequestBuilder builder) {
-        this(configuration, builder, new HashMap<String, String>(), new HashMap<String, String>());
+    public HttpMethod(Configuration configuration, ClientAuthenticator authenticator, RequestBuilder builder) {
+        this(configuration, authenticator, builder, new HashMap<String, String>(), new HashMap<String, String>());
     }
 
-    public HttpMethod(Configuration configuration, RequestBuilder builder, HashMap<String, String> params, HashMap<String, String> headers) {
+    public HttpMethod(Configuration configuration, ClientAuthenticator authenticator, RequestBuilder builder, HashMap<String, String> params, HashMap<String, String> headers) {
         this.configuration = configuration;
         this.httpClient = configuration.getHttpClient();
+        this.authenticator = authenticator;
         this.builder = builder;
         this.params = params;
         this.headers = headers;
@@ -121,7 +124,7 @@ public class HttpMethod<R> {
     }
 
     public HttpMethodAuthenticator<R> authentication() {
-        return new HttpMethodAuthenticator<R>(this);
+        return new HttpMethodAuthenticator<R>(this, authenticator);
     }
 
     public HttpMethod<R> param(String name, String value) {
@@ -136,7 +139,7 @@ public class HttpMethod<R> {
     }
 
     public HttpMethod<R> form() {
-        return new HttpMethod<R>(this.configuration, this.builder, this.params, this.headers) {
+        return new HttpMethod<R>(this.configuration, authenticator, this.builder, this.params, this.headers) {
             @Override
             protected void preExecute(RequestBuilder builder) {
                 if (params != null) {
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 d67090a..8807d39 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
@@ -18,6 +18,7 @@
 package org.keycloak.authorization.client.util;
 
 import org.keycloak.OAuth2Constants;
+import org.keycloak.authorization.client.ClientAuthenticator;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -25,26 +26,24 @@ import org.keycloak.OAuth2Constants;
 public class HttpMethodAuthenticator<R> {
 
     private final HttpMethod<R> method;
+    private final ClientAuthenticator authenticator;
 
-    public HttpMethodAuthenticator(HttpMethod<R> method) {
+    public HttpMethodAuthenticator(HttpMethod<R> method, ClientAuthenticator authenticator) {
         this.method = method;
+        this.authenticator = authenticator;
     }
 
-    public HttpMethod<R> oauth2ClientCredentials() {
+    public HttpMethod<R> client() {
         this.method.params.put(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS);
-        configureClientCredentials();
+        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);
-        configureClientCredentials();
         return this.method;
     }
-
-    private void configureClientCredentials() {
-        this.method.configuration.getClientAuthenticator().configureClientCredentials(this.method.params, this.method.headers);
-    }
 }
diff --git a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
index 428ba07..a9a53f4 100644
--- a/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
+++ b/examples/authz/photoz/photoz-restful-api/src/main/java/org/keycloak/example/photoz/admin/AdminAlbumService.java
@@ -17,19 +17,18 @@
  */
 package org.keycloak.example.photoz.admin;
 
-import org.keycloak.example.photoz.entity.Album;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 
 import javax.inject.Inject;
 import javax.persistence.EntityManager;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
+
+import org.keycloak.example.photoz.entity.Album;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -37,14 +36,9 @@ 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";
-
     @Inject
     private EntityManager entityManager;
 
-    @Context
-    private HttpHeaders headers;
-
     @GET
     @Produces("application/json")
     public Response findAll() {
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 7dd6b24..129a11a 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,15 +1,14 @@
 package org.keycloak.example.photoz.album;
 
+import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.authorization.client.AuthzClient;
-import org.keycloak.authorization.client.Configuration;
+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.adapters.config.AdapterConfig;
-import org.keycloak.util.JsonSerialization;
 
 import javax.inject.Inject;
 import javax.persistence.EntityManager;
@@ -35,7 +34,6 @@ import java.util.Set;
 public class AlbumService {
 
     public static final String SCOPE_ALBUM_VIEW = "urn:photoz.com:scopes:album:view";
-    public static final String SCOPE_ALBUM_CREATE = "urn:photoz.com:scopes:album:create";
     public static final String SCOPE_ALBUM_DELETE = "urn:photoz.com:scopes:album:delete";
 
     @Inject
@@ -44,12 +42,6 @@ public class AlbumService {
     @Context
     private HttpServletRequest request;
 
-    private AuthzClient authzClient;
-
-    public AlbumService() {
-
-    }
-
     @POST
     @Consumes("application/json")
     public Response create(Album newAlbum) {
@@ -142,17 +134,14 @@ public class AlbumService {
     }
 
     private AuthzClient getAuthzClient() {
-        if (this.authzClient == null) {
-            try {
-                AdapterConfig adapterConfig = JsonSerialization.readValue(this.request.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json"), AdapterConfig.class);
-                Configuration configuration = new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), null);
-
-                this.authzClient = AuthzClient.create(configuration);
-            } catch (Exception e) {
-                throw new RuntimeException("Could not create authorization client.", e);
-            }
-        }
+        return getAuthorizationContext().getClient();
+    }
+
+    private ClientAuthorizationContext getAuthorizationContext() {
+        return ClientAuthorizationContext.class.cast(getKeycloakSecurityContext().getAuthorizationContext());
+    }
 
-        return this.authzClient;
+    private KeycloakSecurityContext getKeycloakSecurityContext() {
+        return KeycloakSecurityContext.class.cast(request.getAttribute(KeycloakSecurityContext.class.getName()));
     }
 }
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 80fb84a..665fe8f 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
@@ -65,9 +65,9 @@ public class AbstractPermissionService {
         return request.stream().map(request1 -> {
             String resourceSetId = request1.getResourceSetId();
             String resourceSetName = request1.getResourceSetName();
-            boolean resourceNotProvider = resourceSetId == null && resourceSetName == null;
+            boolean resourceNotProvided = resourceSetId == null && resourceSetName == null;
 
-            if (resourceNotProvider) {
+            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);
                 }
@@ -75,7 +75,7 @@ public class AbstractPermissionService {
 
             Resource resource = null;
 
-            if (!resourceNotProvider) {
+            if (!resourceNotProvided) {
                 if (resourceSetId != null) {
                     resource = storeFactory.getResourceStore().findById(resourceSetId, resourceServer.getId());
                 } else {
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json b/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json
index b0aeb5d..bb21e50 100644
--- a/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json
+++ b/testsuite/integration-arquillian/test-apps/photoz/photoz-realm.json
@@ -112,14 +112,17 @@
     },
     {
       "clientId": "photoz-restful-api",
-      "secret": "secret",
       "enabled": true,
       "baseUrl": "/photoz-restful-api",
       "authorizationServicesEnabled" : true,
       "redirectUris": [
         "/photoz-restful-api/*"
       ],
-      "webOrigins" : ["*"]
+      "webOrigins" : ["*"],
+      "clientAuthenticatorType": "client-jwt",
+      "attributes" : {
+        "jwt.credential.certificate" : "MIICqTCCAZECBgFT0Ngs/DANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1zZWN1cmUtcG9ydGFsMB4XDTE2MDQwMTA4MDA0MVoXDTI2MDQwMTA4MDIyMVowGDEWMBQGA1UEAwwNc2VjdXJlLXBvcnRhbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJa4GixpmzP511AmI0eLPLORyJwXS8908MUvdG3hmh8jMOIhe28XjIFeZSY09vFxh22F2SUMjxU/B2Hw4PDJUkebuNR7rXhOIYCJAo6eEZzjSBY/wngFtfm74zJ/eLCobBtDvIld7jobdHTfE1Oz9+GzvtG0k7cm7ubrLT0J4I1UsFZj3b//3wa+O0vNaTwHC1Jz/m59VbtXqyO4xEzIdl416cnGCmEmk5qd5h1de2UoLi/CTad8HftIJhzN1qhlySzW/9Ha70aYlDH2hiibDsXDTrNaMdaaLik7I8Rv/nIbggysG863PKZo8wknDe62QctH5VYSSktiy4gjSJkGh7ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAZnnx+AHQ8txugGcFK8gWjildDgk+v31fBHBDvmLQaSzsUaIOJaK4wnlwUI+VfR46HmBXhjlDCobFLUptd+kz0G7xapcIn3b5jLrySUUD7L+LAp1vNOQU4mKhTGS3IEvNB73D3GH9rQ+M3KEcoN3f99fNKqKsUdxbmZqGf4VOQ57PUfLBw4PJJGlROPosBc7ivPRyeYnKekhoCTynq30BAD1FA1BA8ppcY4ZVGADPTAgMJxpglpFY9LiqCwdLAGW1ttnsyIJ7DpT+kybhhk7c+MU7gyQdv8xPnMR0bSCB9hndowgBn5oZ393aMscwMNCzwJ0aWBs1sUyn3X0RIsu9Jg=="
+      }
     }
   ]
 }
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 cd4fdba..a1230d8 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
@@ -1,6 +1,8 @@
 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.Configuration;
 import org.keycloak.authorization.client.representation.ResourceRepresentation;
 import org.keycloak.authorization.client.representation.ScopeRepresentation;
@@ -38,7 +40,6 @@ 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_CREATE = "urn:photoz.com:scopes:album:create";
     public static final String SCOPE_ALBUM_DELETE = "urn:photoz.com:scopes:album:delete";
 
     @Inject
@@ -47,12 +48,6 @@ public class AlbumService {
     @Context
     private HttpServletRequest request;
 
-    private AuthzClient authzClient;
-
-    public AlbumService() {
-
-    }
-
     @POST
     @Consumes("application/json")
     public Response create(Album newAlbum, @QueryParam("user") String username) {
@@ -148,17 +143,14 @@ public class AlbumService {
     }
 
     private AuthzClient getAuthzClient() {
-        if (this.authzClient == null) {
-            try {
-                AdapterConfig adapterConfig = JsonSerialization.readValue(this.request.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json"), AdapterConfig.class);
-                Configuration configuration = new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), null);
-
-                this.authzClient = AuthzClient.create(configuration);
-            } catch (Exception e) {
-                throw new RuntimeException("Could not create authorization client.", e);
-            }
-        }
+        return getAuthorizationContext().getClient();
+    }
+
+    private ClientAuthorizationContext getAuthorizationContext() {
+        return ClientAuthorizationContext.class.cast(getKeycloakSecurityContext().getAuthorizationContext());
+    }
 
-        return this.authzClient;
+    private KeycloakSecurityContext getKeycloakSecurityContext() {
+        return KeycloakSecurityContext.class.cast(request.getAttribute(KeycloakSecurityContext.class.getName()));
     }
-}
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/keystore.jks b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/keystore.jks
new file mode 100644
index 0000000..399be7a
Binary files /dev/null and b/testsuite/integration-arquillian/test-apps/photoz/photoz-restful-api/src/main/resources/keystore.jks differ
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 a3ac697..5b41d26 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
@@ -6,7 +6,14 @@
   "resource": "photoz-restful-api",
   "bearer-only" : true,
   "credentials": {
-    "secret": "secret"
+    "jwt": {
+      "client-key-password": "password",
+      "client-keystore-file": "classpath:keystore.jks",
+      "client-keystore-password": "password",
+      "client-key-alias": "secure-portal",
+      "token-timeout": 10,
+      "client-keystore-type": "jks"
+    }
   },
   "policy-enforcer": {
     "user-managed-access" : {},
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
new file mode 100644
index 0000000..2e9086a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzClientCredentialsTest.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.authz;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.KeycloakDeploymentBuilder;
+import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
+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;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.authorization.Permission;
+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;
+import org.keycloak.testsuite.util.UserBuilder;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AuthzClientCredentialsTest extends AbstractKeycloakTest {
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        testRealms.add(configureRealm(RealmBuilder.create().name("authz-client-jwt-test"), ClientBuilder.create()
+                .attribute(JWTClientAuthenticator.CERTIFICATE_ATTR, "MIICnTCCAYUCBgFPPLDaTzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE3MjI0N1oXDTI1MDgxNzE3MjQyN1owEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIUjjgv+V3s96O+Za9002Lp/trtGuHBeaeVL9dFKMKzO2MPqdRmHB4PqNlDdd28Rwf5Xn6iWdFpyUKOnI/yXDLhdcuFpR0sMNK/C9Lt+hSpPFLuzDqgtPgDotlMxiHIWDOZ7g9/gPYNXbNvjv8nSiyqoguoCQiiafW90bPHsiVLdP7ZIUwCcfi1qQm7FhxRJ1NiW5dvUkuCnnWEf0XR+Wzc5eC9EgB0taLFiPsSEIlWMm5xlahYyXkPdNOqZjiRnrTWm5Y4uk8ZcsD/KbPTf/7t7cQXipVaswgjdYi1kK2/zRwOhg1QwWFX/qmvdd+fLxV0R6VqRDhn7Qep2cxwMxLsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAKE6OA46sf20bz8LZPoiNsqRwBUDkaMGXfnob7s/hJZIIwDEx0IAQ3uKsG7q9wb+aA6s+v7S340zb2k3IxuhFaHaZpAd4CyR5cn1FHylbzoZ7rI/3ASqHDqpljdJaFqPH+m7nZWtyDvtZf+gkZ8OjsndwsSBK1d/jMZPp29qYbl1+XfO7RCp/jDqro/R3saYFaIFiEZPeKn1hUJn6BO48vxH1xspSu9FmlvDOEAOz4AuM58z4zRMP49GcFdCWr1wkonJUHaSptJaQwmBwLFUkCbE5I1ixGMb7mjEud6Y5jhfzJiZMo2U8RfcjNbrN0diZl3jB6LQIwESnhYSghaTjNQ==")
+                .authenticatorType(JWTClientAuthenticator.PROVIDER_ID))
+                .build());
+        testRealms.add(configureRealm(RealmBuilder.create().name("authz-test"), ClientBuilder.create().secret("secret")).build());
+    }
+
+    @Before
+    public void beforeAbstractKeycloakTest() throws Exception {
+        super.beforeAbstractKeycloakTest();
+        testContext.getTestRealmReps().forEach(realmRepresentation -> {
+            Keycloak adminClient = getAdminClient();
+            ClientsResource clients = adminClient.realm(realmRepresentation.getRealm()).clients();
+            ClientRepresentation client = clients.findByClientId("resource-server-test").get(0);
+
+            client.setAuthorizationServicesEnabled(false);
+
+            clients.get(client.getId()).update(client);
+
+            client.setAuthorizationServicesEnabled(true);
+
+            clients.get(client.getId()).update(client);
+
+            AuthorizationResource authorization = clients.get(client.getId()).authorization();
+            ResourceServerRepresentation settings = authorization.getSettings();
+
+            settings.setAllowRemoteResourceManagement(true);
+
+            authorization.update(settings);
+        });
+    }
+
+    @Test
+    public void testSuccessfulJWTAuthentication() {
+        assertAccessProtectionAPI(getAuthzClient("keycloak-with-jwt-authentication.json").protection());
+    }
+
+    @Test
+    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);
+        String ticket = ticketResponse.getTicket();
+
+        AuthorizationResponse authorizationResponse = authzClient.authorization("marta", "password").authorize(new AuthorizationRequest(ticket));
+        String rpt = authorizationResponse.getRpt();
+
+        assertNotNull(rpt);
+
+        AccessToken accessToken = new JWSInput(rpt).readJsonContent(AccessToken.class);
+
+        AccessToken.Authorization authorization = accessToken.getAuthorization();
+
+        assertNotNull(authorization);
+
+        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());
+        }
+    }
+
+    @Test
+    public void failJWTAuthentication() {
+        try {
+            getAuthzClient("keycloak-with-invalid-keys-jwt-authentication.json").protection();
+            fail("Should fail due to invalid signature");
+        } catch (HttpResponseException cause) {
+            assertEquals(400, cause.getStatusCode());
+        }
+    }
+
+    @Test
+    public void testSuccessfulClientSecret() {
+        ProtectionResource protection = getAuthzClient("default-keycloak.json").protection();
+        assertAccessProtectionAPI(protection);
+    }
+
+    private RealmBuilder configureRealm(RealmBuilder builder, ClientBuilder clientBuilder) {
+        return builder
+                .roles(RolesBuilder.create().realmRole(new RoleRepresentation("uma_authorization", "", false)))
+                .user(UserBuilder.create().username("marta").password("password").addRoles("uma_authorization"))
+                .user(UserBuilder.create().username("kolo").password("password"))
+                .client(clientBuilder.clientId("resource-server-test")
+                        .authorizationServicesEnabled(true)
+                        .redirectUris("http://localhost/resource-server-test")
+                        .defaultRoles("uma_protection")
+                        .directAccessGrants());
+    }
+
+    private void assertAccessProtectionAPI(ProtectionResource protection) {
+        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();
+
+        assertNotNull(actual);
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(id, actual.getId());
+    }
+
+    private AuthzClient getAuthzClient(String adapterConfig) {
+        KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getConfigurationStream(adapterConfig));
+
+        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);
+            }
+        });
+    }
+
+    private InputStream getConfigurationStream(String adapterConfig) {
+        return getClass().getResourceAsStream("/authorization-test/" + adapterConfig);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/keycloak-with-invalid-keys-jwt-authentication.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/keycloak-with-invalid-keys-jwt-authentication.json
new file mode 100644
index 0000000..482741f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/keycloak-with-invalid-keys-jwt-authentication.json
@@ -0,0 +1,15 @@
+{
+    "realm": "authz-client-jwt-test",
+    "auth-server-url" : "http://localhost:8180/auth",
+    "resource" : "resource-server-test",
+    "credentials": {
+        "jwt": {
+            "client-keystore-file": "classpath:client-auth-test/keystore-client2.jks",
+            "client-keystore-type": "JKS",
+            "client-keystore-password": "storepass",
+            "client-key-alias": "clientkey",
+            "client-key-password": "keypass",
+            "token-expiration": 10
+        }
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/keycloak-with-jwt-authentication.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/keycloak-with-jwt-authentication.json
new file mode 100644
index 0000000..0fad5ef
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/keycloak-with-jwt-authentication.json
@@ -0,0 +1,15 @@
+{
+    "realm": "authz-client-jwt-test",
+    "auth-server-url" : "http://localhost:8180/auth",
+    "resource" : "resource-server-test",
+    "credentials": {
+        "jwt": {
+            "client-keystore-file": "classpath:client-auth-test/keystore-client1.jks",
+            "client-keystore-type": "JKS",
+            "client-keystore-password": "storepass",
+            "client-key-alias": "clientkey",
+            "client-key-password": "keypass",
+            "token-expiration": 10
+        }
+    }
+}
\ No newline at end of file