keycloak-memoizeit

Merge pull request #5050 from TeliaSoneraNorge/KEYCLOAK-6659 Add

3/2/2018 2:44:28 PM

Details

diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
index 654193d..58962b5 100644
--- a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
+++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
@@ -212,7 +212,7 @@ public class KeycloakIdentity implements Identity {
             return client==null ? null : client.getId();
         }
 
-        return this.accessToken.getSubject();
+        return this.getUserFromSessionState().getId();
     }
 
     @Override
@@ -237,7 +237,7 @@ public class KeycloakIdentity implements Identity {
             return false;
         }
 
-        return this.accessToken.getSubject().equals(clientUser.getId());
+        return this.getUserFromSessionState().getId().equals(clientUser.getId());
     }
 
     private ClientModel getTargetClient() {
@@ -252,4 +252,9 @@ public class KeycloakIdentity implements Identity {
 
         return null;
     }
+
+    private UserModel getUserFromSessionState() {
+        UserSessionModel userSession = keycloakSession.sessions().getUserSession(realm, accessToken.getSessionState());
+        return userSession.getUser();
+    }
 }
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index e18b444..803e778 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -1082,24 +1082,28 @@ public class AuthenticationManager {
                 }
             }
 
-            UserModel user = session.users().getUserById(token.getSubject(), realm);
-            if (user == null || !user.isEnabled() ) {
-                logger.debug("Unknown user in identity token");
-                return null;
-            }
+            UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionState());
+            UserModel user = null;
+            if (userSession != null) {
+                user = userSession.getUser();
+                if (user == null || !user.isEnabled()) {
+                    logger.debug("Unknown user in identity token");
+                    return null;
+                }
 
-            int userNotBefore = session.users().getNotBeforeOfUser(realm, user);
-            if (token.getIssuedAt() < userNotBefore) {
-                logger.debug("User notBefore newer than token");
-                return null;
+                int userNotBefore = session.users().getNotBeforeOfUser(realm, user);
+                if (token.getIssuedAt() < userNotBefore) {
+                    logger.debug("User notBefore newer than token");
+                    return null;
+                }
             }
 
-            UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionState());
             if (!isSessionValid(realm, userSession)) {
                 // Check if accessToken was for the offline session.
                 if (!isCookie) {
                     UserSessionModel offlineUserSession = session.sessions().getOfflineUserSession(realm, token.getSessionState());
                     if (isOfflineSessionValid(realm, offlineUserSession)) {
+                        user = offlineUserSession.getUser();
                         return new AuthResult(user, offlineUserSession, token);
                     }
                 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java
index 61d2773..37dd1ff 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java
@@ -53,29 +53,53 @@ import org.keycloak.util.JsonSerialization;
  */
 public class AuthorizationAPITest extends AbstractAuthzTest {
 
+    private static final String RESOURCE_SERVER_TEST = "resource-server-test";
+    private static final String TEST_CLIENT = "test-client";
+    private static final String AUTHZ_CLIENT_CONFIG = "default-keycloak.json";
+    private static final String PAIRWISE_RESOURCE_SERVER_TEST = "pairwise-resource-server-test";
+    private static final String PAIRWISE_TEST_CLIENT = "test-client-pairwise";
+    private static final String PAIRWISE_AUTHZ_CLIENT_CONFIG = "default-keycloak-pairwise.json";
+
     @Override
     public void addTestRealms(List<RealmRepresentation> testRealms) {
         testRealms.add(RealmBuilder.create().name("authz-test")
                 .roles(RolesBuilder.create().realmRole(RoleBuilder.create().name("uma_authorization").build()))
                 .user(UserBuilder.create().username("marta").password("password").addRoles("uma_authorization"))
                 .user(UserBuilder.create().username("kolo").password("password"))
-                .client(ClientBuilder.create().clientId("resource-server-test")
+                .client(ClientBuilder.create().clientId(RESOURCE_SERVER_TEST)
                     .secret("secret")
                     .authorizationServicesEnabled(true)
                     .redirectUris("http://localhost/resource-server-test")
                     .defaultRoles("uma_protection")
                     .directAccessGrants())
-                .client(ClientBuilder.create().clientId("test-client")
+                .client(ClientBuilder.create().clientId(PAIRWISE_RESOURCE_SERVER_TEST)
+                    .secret("secret")
+                    .authorizationServicesEnabled(true)
+                    .redirectUris("http://localhost/resource-server-test")
+                    .defaultRoles("uma_protection")
+                    .directAccessGrants()
+                    .pairwise("http://pairwise.com"))
+                .client(ClientBuilder.create().clientId(TEST_CLIENT)
                     .secret("secret")
                     .authorizationServicesEnabled(true)
                     .redirectUris("http://localhost/test-client")
                     .directAccessGrants())
+                .client(ClientBuilder.create().clientId(PAIRWISE_TEST_CLIENT)
+                        .secret("secret")
+                        .authorizationServicesEnabled(true)
+                        .redirectUris("http://localhost/test-client")
+                        .directAccessGrants())
                 .build());
     }
 
     @Before
     public void configureAuthorization() throws Exception {
-        ClientResource client = getClient(getRealm());
+        configureAuthorization(RESOURCE_SERVER_TEST);
+        configureAuthorization(PAIRWISE_RESOURCE_SERVER_TEST);
+    }
+
+    private void configureAuthorization(String clientId) throws Exception {
+        ClientResource client = getClient(getRealm(), clientId);
         AuthorizationResource authorization = client.authorization();
         ResourceRepresentation resource = new ResourceRepresentation("Resource A");
 
@@ -102,7 +126,16 @@ public class AuthorizationAPITest extends AbstractAuthzTest {
 
     @Test
     public void testAccessTokenWithUmaAuthorization() {
-        AuthzClient authzClient = getAuthzClient();
+        testAccessTokenWithUmaAuthorization(AUTHZ_CLIENT_CONFIG);
+    }
+
+    @Test
+    public void testAccessTokenWithUmaAuthorizationPairwise() {
+        testAccessTokenWithUmaAuthorization(PAIRWISE_AUTHZ_CLIENT_CONFIG);
+    }
+
+    public void testAccessTokenWithUmaAuthorization(String authzConfigFile) {
+        AuthzClient authzClient = getAuthzClient(authzConfigFile);
         PermissionRequest request = new PermissionRequest("Resource A");
 
         String ticket = authzClient.protection().permission().create(request).getTicket();
@@ -113,32 +146,63 @@ public class AuthorizationAPITest extends AbstractAuthzTest {
 
     @Test
     public void testResourceServerAsAudience() throws Exception {
-        AuthzClient authzClient = getAuthzClient();
+        testResourceServerAsAudience(
+                TEST_CLIENT,
+                RESOURCE_SERVER_TEST,
+                AUTHZ_CLIENT_CONFIG);
+    }
+
+    @Test
+    public void testResourceServerAsAudienceWithPairwiseClient() throws Exception {
+        testResourceServerAsAudience(
+                PAIRWISE_TEST_CLIENT,
+                RESOURCE_SERVER_TEST,
+                AUTHZ_CLIENT_CONFIG);
+    }
+
+    @Test
+    public void testPairwiseResourceServerAsAudience() throws Exception {
+        testResourceServerAsAudience(
+                TEST_CLIENT,
+                PAIRWISE_RESOURCE_SERVER_TEST,
+                PAIRWISE_AUTHZ_CLIENT_CONFIG);
+    }
+
+    @Test
+    public void testPairwiseResourceServerAsAudienceWithPairwiseClient() throws Exception {
+        testResourceServerAsAudience(
+                PAIRWISE_TEST_CLIENT,
+                PAIRWISE_RESOURCE_SERVER_TEST,
+                PAIRWISE_AUTHZ_CLIENT_CONFIG);
+    }
+
+    public void testResourceServerAsAudience(String clientId, String resourceServerClientId, String authzConfigFile) throws Exception {
+        AuthzClient authzClient = getAuthzClient(authzConfigFile);
         PermissionRequest request = new PermissionRequest();
 
         request.setResourceId("Resource A");
 
-        String accessToken = new OAuthClient().realm("authz-test").clientId("test-client").doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
+        String accessToken = new OAuthClient().realm("authz-test").clientId(clientId).doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
         String ticket = authzClient.protection().permission().create(request).getTicket();
         AuthorizationResponse response = authzClient.authorization(accessToken).authorize(new AuthorizationRequest(ticket));
 
         assertNotNull(response.getToken());
         AccessToken rpt = toAccessToken(response.getToken());
-        assertEquals("resource-server-test", rpt.getAudience()[0]);
+        assertEquals(resourceServerClientId, rpt.getAudience()[0]);
     }
 
     private RealmResource getRealm() throws Exception {
         return adminClient.realm("authz-test");
     }
 
-    private ClientResource getClient(RealmResource realm) {
+    private ClientResource getClient(RealmResource realm, String clientId) {
         ClientsResource clients = realm.clients();
-        return clients.findByClientId("resource-server-test").stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]"));
+        return clients.findByClientId(clientId).stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]"));
     }
 
-    private AuthzClient getAuthzClient() {
+    private AuthzClient getAuthzClient(String configFile) {
         try {
-            return AuthzClient.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/default-keycloak.json"), Configuration.class));
+            return AuthzClient.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/" + configFile), Configuration.class));
         } catch (IOException cause) {
             throw new RuntimeException("Failed to create authz client", cause);
         }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java
index 54bcd2e..a7fab87 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java
@@ -55,6 +55,13 @@ import org.keycloak.util.JsonSerialization;
  */
 public class EntitlementAPITest extends AbstractAuthzTest {
 
+    private static final String RESOURCE_SERVER_TEST = "resource-server-test";
+    private static final String TEST_CLIENT = "test-client";
+    private static final String AUTHZ_CLIENT_CONFIG = "default-keycloak.json";
+    private static final String PAIRWISE_RESOURCE_SERVER_TEST = "pairwise-resource-server-test";
+    private static final String PAIRWISE_TEST_CLIENT = "test-client-pairwise";
+    private static final String PAIRWISE_AUTHZ_CLIENT_CONFIG = "default-keycloak-pairwise.json";
+
     private AuthzClient authzClient;
 
     @Override
@@ -63,23 +70,41 @@ public class EntitlementAPITest extends AbstractAuthzTest {
                 .roles(RolesBuilder.create().realmRole(RoleBuilder.create().name("uma_authorization").build()))
                 .user(UserBuilder.create().username("marta").password("password").addRoles("uma_authorization"))
                 .user(UserBuilder.create().username("kolo").password("password"))
-                .client(ClientBuilder.create().clientId("resource-server-test")
-                    .secret("secret")
-                    .authorizationServicesEnabled(true)
-                    .redirectUris("http://localhost/resource-server-test")
-                    .defaultRoles("uma_protection")
-                    .directAccessGrants())
-                .client(ClientBuilder.create().clientId("test-client")
-                    .secret("secret")
-                    .authorizationServicesEnabled(true)
-                    .redirectUris("http://localhost/test-client")
-                    .directAccessGrants())
+                .client(ClientBuilder.create().clientId(RESOURCE_SERVER_TEST)
+                        .secret("secret")
+                        .authorizationServicesEnabled(true)
+                        .redirectUris("http://localhost/resource-server-test")
+                        .defaultRoles("uma_protection")
+                        .directAccessGrants())
+                .client(ClientBuilder.create().clientId(PAIRWISE_RESOURCE_SERVER_TEST)
+                        .secret("secret")
+                        .authorizationServicesEnabled(true)
+                        .redirectUris("http://localhost/resource-server-test")
+                        .defaultRoles("uma_protection")
+                        .pairwise("http://pairwise.com")
+                        .directAccessGrants())
+                .client(ClientBuilder.create().clientId(TEST_CLIENT)
+                        .secret("secret")
+                        .authorizationServicesEnabled(true)
+                        .redirectUris("http://localhost/test-client")
+                        .directAccessGrants())
+                .client(ClientBuilder.create().clientId(PAIRWISE_TEST_CLIENT)
+                        .secret("secret")
+                        .authorizationServicesEnabled(true)
+                        .redirectUris("http://localhost/test-client")
+                        .pairwise("http://pairwise.com")
+                        .directAccessGrants())
                 .build());
     }
 
     @Before
     public void configureAuthorization() throws Exception {
-        ClientResource client = getClient(getRealm());
+        configureAuthorization(RESOURCE_SERVER_TEST);
+        configureAuthorization(PAIRWISE_RESOURCE_SERVER_TEST);
+    }
+
+    public void configureAuthorization(String clientId) throws Exception {
+        ClientResource client = getClient(getRealm(), clientId);
         AuthorizationResource authorization = client.authorization();
 
         JSPolicyRepresentation policy = new JSPolicyRepresentation();
@@ -106,6 +131,15 @@ public class EntitlementAPITest extends AbstractAuthzTest {
 
     @Test
     public void testRptRequestWithoutResourceName() {
+        testRptRequestWithoutResourceName(AUTHZ_CLIENT_CONFIG);
+    }
+
+    @Test
+    public void testRptRequestWithoutResourceNamePairwise() {
+        testRptRequestWithoutResourceName(PAIRWISE_AUTHZ_CLIENT_CONFIG);
+    }
+
+    public void testRptRequestWithoutResourceName(String configFile) {
         Metadata metadata = new Metadata();
 
         metadata.setIncludeResourceName(false);
@@ -116,32 +150,51 @@ public class EntitlementAPITest extends AbstractAuthzTest {
             request.setMetadata(metadata);
             request.addPermission("Resource 1");
 
-            return getAuthzClient().authorization("marta", "password").authorize(request);
+            return getAuthzClient(configFile).authorization("marta", "password").authorize(request);
         });
     }
 
     @Test
     public void testRptRequestWithResourceName() {
+        testRptRequestWithResourceName(AUTHZ_CLIENT_CONFIG);
+    }
+
+    @Test
+    public void testRptRequestWithResourceNamePairwise() {
+        testRptRequestWithResourceName(PAIRWISE_AUTHZ_CLIENT_CONFIG);
+    }
+
+
+    public void testRptRequestWithResourceName(String configFile) {
         Metadata metadata = new Metadata();
 
         metadata.setIncludeResourceName(true);
 
-        assertResponse(metadata, () -> getAuthzClient().authorization("marta", "password").authorize());
+        assertResponse(metadata, () -> getAuthzClient(configFile).authorization("marta", "password").authorize());
 
         AuthorizationRequest request = new AuthorizationRequest();
 
         request.setMetadata(metadata);
         request.addPermission("Resource 13");
 
-        assertResponse(metadata, () -> getAuthzClient().authorization("marta", "password").authorize(request));
+        assertResponse(metadata, () -> getAuthzClient(configFile).authorization("marta", "password").authorize(request));
 
         request.setMetadata(null);
 
-        assertResponse(metadata, () -> getAuthzClient().authorization("marta", "password").authorize(request));
+        assertResponse(metadata, () -> getAuthzClient(configFile).authorization("marta", "password").authorize(request));
     }
 
     @Test
     public void testPermissionLimit() {
+        testPermissionLimit(AUTHZ_CLIENT_CONFIG);
+    }
+
+    @Test
+    public void testPermissionLimitPairwise() {
+        testPermissionLimit(PAIRWISE_AUTHZ_CLIENT_CONFIG);
+    }
+
+    public void testPermissionLimit(String configFile) {
         AuthorizationRequest request = new AuthorizationRequest();
 
         for (int i = 1; i <= 10; i++) {
@@ -154,7 +207,7 @@ public class EntitlementAPITest extends AbstractAuthzTest {
 
         request.setMetadata(metadata);
 
-        AuthorizationResponse response = getAuthzClient().authorization("marta", "password").authorize(request);
+        AuthorizationResponse response = getAuthzClient(configFile).authorization("marta", "password").authorize(request);
         AccessToken rpt = toAccessToken(response.getToken());
 
         List<Permission> permissions = rpt.getAuthorization().getPermissions();
@@ -174,7 +227,7 @@ public class EntitlementAPITest extends AbstractAuthzTest {
         request.setMetadata(metadata);
         request.setRpt(response.getToken());
 
-        response = getAuthzClient().authorization("marta", "password").authorize(request);
+        response = getAuthzClient(configFile).authorization("marta", "password").authorize(request);
         rpt = toAccessToken(response.getToken());
 
         permissions = rpt.getAuthorization().getPermissions();
@@ -198,7 +251,7 @@ public class EntitlementAPITest extends AbstractAuthzTest {
         request.setMetadata(metadata);
         request.setRpt(response.getToken());
 
-        response = getAuthzClient().authorization("marta", "password").authorize(request);
+        response = getAuthzClient(configFile).authorization("marta", "password").authorize(request);
         rpt = toAccessToken(response.getToken());
 
         permissions = rpt.getAuthorization().getPermissions();
@@ -221,7 +274,7 @@ public class EntitlementAPITest extends AbstractAuthzTest {
         request.setMetadata(metadata);
         request.setRpt(response.getToken());
 
-        response = getAuthzClient().authorization("marta", "password").authorize(request);
+        response = getAuthzClient(configFile).authorization("marta", "password").authorize(request);
         rpt = toAccessToken(response.getToken());
 
         permissions = rpt.getAuthorization().getPermissions();
@@ -236,15 +289,46 @@ public class EntitlementAPITest extends AbstractAuthzTest {
 
     @Test
     public void testResourceServerAsAudience() throws Exception {
+        testResourceServerAsAudience(
+                TEST_CLIENT,
+                RESOURCE_SERVER_TEST,
+                AUTHZ_CLIENT_CONFIG);
+    }
+
+    @Test
+    public void testResourceServerAsAudienceWithPairwiseClient() throws Exception {
+        testResourceServerAsAudience(
+                PAIRWISE_TEST_CLIENT,
+                RESOURCE_SERVER_TEST,
+                AUTHZ_CLIENT_CONFIG);
+    }
+
+    @Test
+    public void testPairwiseResourceServerAsAudience() throws Exception {
+        testResourceServerAsAudience(
+                TEST_CLIENT,
+                PAIRWISE_RESOURCE_SERVER_TEST,
+                PAIRWISE_AUTHZ_CLIENT_CONFIG);
+    }
+
+    @Test
+    public void testPairwiseResourceServerAsAudienceWithPairwiseClient() throws Exception {
+        testResourceServerAsAudience(
+                PAIRWISE_TEST_CLIENT,
+                PAIRWISE_RESOURCE_SERVER_TEST,
+                PAIRWISE_AUTHZ_CLIENT_CONFIG);
+    }
+
+    public void testResourceServerAsAudience(String testClientId, String resourceServerClientId, String configFile) throws Exception {
         AuthorizationRequest request = new AuthorizationRequest();
 
         request.addPermission("Resource 1");
 
-        String accessToken = new OAuthClient().realm("authz-test").clientId("test-client").doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
-        AuthorizationResponse response = getAuthzClient().authorization(accessToken).authorize(request);
+        String accessToken = new OAuthClient().realm("authz-test").clientId(testClientId).doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
+        AuthorizationResponse response = getAuthzClient(configFile).authorization(accessToken).authorize(request);
         AccessToken rpt = toAccessToken(response.getToken());
 
-        assertEquals("resource-server-test", rpt.getAudience()[0]);
+        assertEquals(resourceServerClientId, rpt.getAudience()[0]);
     }
 
     private void assertResponse(Metadata metadata, Supplier<AuthorizationResponse> responseSupplier) {
@@ -268,15 +352,15 @@ public class EntitlementAPITest extends AbstractAuthzTest {
         return adminClient.realm("authz-test");
     }
 
-    private ClientResource getClient(RealmResource realm) {
+    private ClientResource getClient(RealmResource realm, String clientId) {
         ClientsResource clients = realm.clients();
-        return clients.findByClientId("resource-server-test").stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]"));
+        return clients.findByClientId(clientId).stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]"));
     }
 
-    private AuthzClient getAuthzClient() {
+    private AuthzClient getAuthzClient(String configFile) {
         if (authzClient == null) {
             try {
-                authzClient = AuthzClient.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/default-keycloak.json"), Configuration.class));
+                authzClient = AuthzClient.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/" + configFile), Configuration.class));
             } catch (IOException cause) {
                 throw new RuntimeException("Failed to create authz client", cause);
             }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
index 870d059..3fef22a 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
@@ -198,4 +198,12 @@ public class ClientBuilder {
         rep.getProtocolMappers().addAll(Arrays.asList(mappers));
         return this;
     }
+
+    public ClientBuilder pairwise(String sectorIdentifierUri, String salt) {
+        return protocolMapper(ProtocolMapperUtil.createPairwiseMapper(sectorIdentifierUri, salt));
+    }
+
+    public ClientBuilder pairwise(String sectorIdentifierUri) {
+        return protocolMapper(ProtocolMapperUtil.createPairwiseMapper(sectorIdentifierUri, null));
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java
index 17e0eb3..18b7872 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java
@@ -6,6 +6,7 @@ import org.keycloak.protocol.oidc.mappers.AddressMapper;
 import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
 import org.keycloak.protocol.oidc.mappers.HardcodedRole;
 import org.keycloak.protocol.oidc.mappers.RoleNameMapper;
+import org.keycloak.protocol.oidc.mappers.SHA256PairwiseSubMapper;
 import org.keycloak.protocol.oidc.mappers.ScriptBasedOIDCProtocolMapper;
 import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
 import org.keycloak.protocol.oidc.mappers.UserClientRoleMappingMapper;
@@ -164,4 +165,7 @@ public class ProtocolMapperUtil {
         );
     }
 
+    public static ProtocolMapperRepresentation createPairwiseMapper(String sectorIdentifierUri, String salt) {
+        return SHA256PairwiseSubMapper.createPairwiseMapper(sectorIdentifierUri, salt);
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak-pairwise.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak-pairwise.json
new file mode 100644
index 0000000..fbd7c14
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak-pairwise.json
@@ -0,0 +1,8 @@
+{
+    "realm": "authz-test",
+    "auth-server-url" : "http://localhost:8180/auth",
+    "resource" : "pairwise-resource-server-test",
+    "credentials": {
+        "secret": "secret"
+    }
+}
\ No newline at end of file