keycloak-memoizeit

KEYCLOAK-6700 Financial API Read and Write API Security Profile

2/22/2018 10:02:26 PM

Details

diff --git a/core/src/main/java/org/keycloak/representations/IDToken.java b/core/src/main/java/org/keycloak/representations/IDToken.java
index 51776f0..76842bf 100755
--- a/core/src/main/java/org/keycloak/representations/IDToken.java
+++ b/core/src/main/java/org/keycloak/representations/IDToken.java
@@ -51,6 +51,10 @@ public class IDToken extends JsonWebToken {
     public static final String CLAIMS_LOCALES = "claims_locales";
     public static final String ACR = "acr";
 
+    // Financial API - Part 2: Read and Write API Security Profile
+    // http://openid.net/specs/openid-financial-api-part-2.html#authorization-server
+    public static final String S_HASH = "s_hash";
+
     // NOTE!!!  WE used to use @JsonUnwrapped on a UserClaimSet object.  This screws up otherClaims and the won't work
     // anymore.  So don't have any @JsonUnwrapped!
     @JsonProperty(NONCE)
@@ -131,6 +135,11 @@ public class IDToken extends JsonWebToken {
     @JsonProperty(ACR)
     protected String acr;
 
+    // Financial API - Part 2: Read and Write API Security Profile
+    // http://openid.net/specs/openid-financial-api-part-2.html#authorization-server
+    @JsonProperty(S_HASH)
+    protected String stateHash; 
+
     public String getNonce() {
         return nonce;
     }
@@ -338,4 +347,14 @@ public class IDToken extends JsonWebToken {
     public void setAcr(String acr) {
         this.acr = acr;
     }
+
+    // Financial API - Part 2: Read and Write API Security Profile
+    // http://openid.net/specs/openid-financial-api-part-2.html#authorization-server
+    public String getStateHash() {
+        return stateHash;
+    }
+
+    public void setStateHash(String stateHash) {
+        this.stateHash = stateHash;
+    }
 }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
index 36d93fa..7a2f6c6 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
@@ -218,7 +218,11 @@ public class OIDCLoginProtocol implements LoginProtocol {
                 if (responseType.hasResponseType(OIDCResponseType.CODE)) {
                     responseBuilder.generateCodeHash(code);
                 }
-
+                
+                // Financial API - Part 2: Read and Write API Security Profile
+                // http://openid.net/specs/openid-financial-api-part-2.html#authorization-server
+                if (state != null)
+                    responseBuilder.generateStateHash(state);
             }
 
             AccessTokenResponse res = responseBuilder.build();
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
index e21f3ad..41b71a7 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
@@ -718,6 +718,10 @@ public class TokenManager {
         boolean generateAccessTokenHash = false;
         String codeHash;
 
+        // Financial API - Part 2: Read and Write API Security Profile
+        // http://openid.net/specs/openid-financial-api-part-2.html#authorization-server
+        String stateHash;
+        
         public AccessTokenResponseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
             this.realm = realm;
             this.client = client;
@@ -819,6 +823,12 @@ public class TokenManager {
             return this;
         }
 
+        // Financial API - Part 2: Read and Write API Security Profile
+        // http://openid.net/specs/openid-financial-api-part-2.html#authorization-server
+        public AccessTokenResponseBuilder generateStateHash(String state) {
+            stateHash = HashProvider.oidcHash(jwsAlgorithm, state);
+            return this;
+        }
 
         public AccessTokenResponse build() {
             KeyManager.ActiveRsaKey activeRsaKey = session.keys().getActiveRsaKey(realm);
@@ -854,7 +864,11 @@ public class TokenManager {
             if (codeHash != null) {
                 idToken.setCodeHash(codeHash);
             }
-
+            // Financial API - Part 2: Read and Write API Security Profile
+            // http://openid.net/specs/openid-financial-api-part-2.html#authorization-server
+            if (stateHash != null) {
+                idToken.setStateHash(stateHash);
+            }
             if (idToken != null) {
                 String encodedToken = new JWSBuilder().type(JWT).kid(activeRsaKey.getKid()).jsonContent(idToken).sign(jwsAlgorithm, activeRsaKey.getPrivateKey());
                 res.setIdToken(encodedToken);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/flows/OIDCHybridResponseTypeCodeIDTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/flows/OIDCHybridResponseTypeCodeIDTokenTest.java
index 3dfa580..154d659 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/flows/OIDCHybridResponseTypeCodeIDTokenTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/flows/OIDCHybridResponseTypeCodeIDTokenTest.java
@@ -65,6 +65,12 @@ public class OIDCHybridResponseTypeCodeIDTokenTest extends AbstractOIDCResponseT
         Assert.assertNotNull(idToken.getCodeHash());
         Assert.assertEquals(idToken.getCodeHash(), HashProvider.oidcHash(jwsAlgorithm, authzResponse.getCode()));
 
+        // Financial API - Part 2: Read and Write API Security Profile
+        // http://openid.net/specs/openid-financial-api-part-2.html#authorization-server
+        // Validate "s_hash"
+        Assert.assertNotNull(idToken.getStateHash());
+        Assert.assertEquals(idToken.getStateHash(), HashProvider.oidcHash(jwsAlgorithm, authzResponse.getState())); 
+
         // IDToken exchanged for the code
         IDToken idToken2 = sendTokenRequestAndGetIDToken(loginEvent);
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/flows/OIDCHybridResponseTypeCodeIDTokenTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/flows/OIDCHybridResponseTypeCodeIDTokenTokenTest.java
index 132195d..4ceb049 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/flows/OIDCHybridResponseTypeCodeIDTokenTokenTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/flows/OIDCHybridResponseTypeCodeIDTokenTokenTest.java
@@ -66,6 +66,12 @@ public class OIDCHybridResponseTypeCodeIDTokenTokenTest extends AbstractOIDCResp
         Assert.assertNotNull(idToken.getCodeHash());
         Assert.assertEquals(idToken.getCodeHash(), HashProvider.oidcHash(jwsAlgorithm, authzResponse.getCode()));
 
+        // Financial API - Part 2: Read and Write API Security Profile
+        // http://openid.net/specs/openid-financial-api-part-2.html#authorization-server
+        // Validate "s_hash"
+        Assert.assertNotNull(idToken.getStateHash());
+        Assert.assertEquals(idToken.getStateHash(), HashProvider.oidcHash(jwsAlgorithm, authzResponse.getState())); 
+        
         // IDToken exchanged for the code
         IDToken idToken2 = sendTokenRequestAndGetIDToken(loginEvent);