keycloak-aplcache

Details

diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractPairwiseSubMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractPairwiseSubMapper.java
index 09b39c4..374581d 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractPairwiseSubMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractPairwiseSubMapper.java
@@ -64,24 +64,32 @@ public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapp
     }
 
     @Override
-    public final IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
-        setSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSession.getClient(), mappingModel), userSession.getUser().getId()));
+    public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
+        setIDTokenSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSession.getClient(), mappingModel), userSession.getUser().getId()));
         return token;
     }
 
     @Override
-    public final AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
-        setSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSession.getClient(), mappingModel), userSession.getUser().getId()));
+    public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
+        setAccessTokenSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSession.getClient(), mappingModel), userSession.getUser().getId()));
         return token;
     }
 
     @Override
-    public final AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
-        setSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSession.getClient(), mappingModel), userSession.getUser().getId()));
+    public AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
+        setUserInfoTokenSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSession.getClient(), mappingModel), userSession.getUser().getId()));
         return token;
     }
 
-    private void setSubject(IDToken token, String pairwiseSub) {
+    protected void setIDTokenSubject(IDToken token, String pairwiseSub) {
+        token.setSubject(pairwiseSub);
+    }
+
+    protected void setAccessTokenSubject(IDToken token, String pairwiseSub) {
+        token.setSubject(pairwiseSub);
+    }
+
+    protected void setUserInfoTokenSubject(IDToken token, String pairwiseSub) {
         token.getOtherClaims().put("sub", pairwiseSub);
     }
 
@@ -115,6 +123,4 @@ public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapp
     public final String getId() {
         return "oidc-" + getIdPrefix() + PROVIDER_ID_SUFFIX;
     }
-}
-
-
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA256PairwiseSubMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA256PairwiseSubMapper.java
index 28a0b87..f69f2b1 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA256PairwiseSubMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA256PairwiseSubMapper.java
@@ -79,7 +79,7 @@ public class SHA256PairwiseSubMapper extends AbstractPairwiseSubMapper {
         Charset charset = Charset.forName("UTF-8");
         byte[] salt = saltStr.getBytes(charset);
         String pairwiseSub = generateSub(sectorIdentifier, localSub, salt);
-        logger.infof("local sub = '%s', pairwise sub = '%s'", localSub, pairwiseSub);
+        logger.tracef("local sub = '%s', pairwise sub = '%s'", localSub, pairwiseSub);
         return pairwiseSub;
     }
 
@@ -109,4 +109,4 @@ public class SHA256PairwiseSubMapper extends AbstractPairwiseSubMapper {
     public String getIdPrefix() {
         return PROVIDER_ID;
     }
-}
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java
index c5bb785..8666e04 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCPairwiseClientRegistrationTest.java
@@ -18,6 +18,7 @@
 package org.keycloak.testsuite.client;
 
 
+import org.apache.commons.lang.StringUtils;
 import org.junit.Before;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.ClientResource;
@@ -44,6 +45,7 @@ import org.keycloak.testsuite.util.UserInfoClientUtil;
 import javax.ws.rs.client.Client;
 import javax.ws.rs.core.Response;
 import java.util.ArrayList;
+import java.util.Base64;
 import java.util.Collections;
 import java.util.List;
 
@@ -319,11 +321,20 @@ public class OIDCPairwiseClientRegistrationTest extends AbstractClientRegistrati
         oauth.openLoginForm();
         loginResponse = new OAuthClient.AuthorizationEndpointResponse(oauth);
         accessTokenResponse = oauth.doAccessTokenRequest(loginResponse.getCode(), pairwiseClient.getClientSecret());
+
+        // Assert token payloads don't contain more than one "sub"
+        String accessTokenPayload = getPayload(accessTokenResponse.getAccessToken());
+        Assert.assertEquals(1, StringUtils.countMatches(accessTokenPayload, "\"sub\""));
+        String idTokenPayload = getPayload(accessTokenResponse.getIdToken());
+        Assert.assertEquals(1, StringUtils.countMatches(idTokenPayload, "\"sub\""));
+        String refreshTokenPayload = getPayload(accessTokenResponse.getRefreshToken());
+        Assert.assertEquals(1, StringUtils.countMatches(refreshTokenPayload, "\"sub\""));
+
         accessToken = oauth.verifyToken(accessTokenResponse.getAccessToken());
         Assert.assertEquals("test-user", accessToken.getPreferredUsername());
         Assert.assertEquals("test-user@localhost", accessToken.getEmail());
 
-        // Assert pairwise client has different subject like userId
+        // Assert pairwise client has different subject than userId
         String pairwiseUserId = accessToken.getSubject();
         Assert.assertNotEquals(pairwiseUserId, user.getId());
 
@@ -339,4 +350,9 @@ public class OIDCPairwiseClientRegistrationTest extends AbstractClientRegistrati
             jaxrsClient.close();
         }
     }
-}
+
+    private String getPayload(String token) {
+        String payloadBase64 = token.split("\\.")[1];
+        return new String(Base64.getDecoder().decode(payloadBase64));
+    }
+}
\ No newline at end of file