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);