KEYCLOAK-6700 Financial API Read and Write API Security Profile : state hash value (s_hash) to protect state parameter

This commit is contained in:
Takashi Norimatsu 2018-02-23 10:02:26 +09:00 committed by Marek Posolda
parent 871ecf83fb
commit e72756d01a
5 changed files with 51 additions and 2 deletions

View file

@ -51,6 +51,10 @@ public class IDToken extends JsonWebToken {
public static final String CLAIMS_LOCALES = "claims_locales"; public static final String CLAIMS_LOCALES = "claims_locales";
public static final String ACR = "acr"; 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 // 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! // anymore. So don't have any @JsonUnwrapped!
@JsonProperty(NONCE) @JsonProperty(NONCE)
@ -131,6 +135,11 @@ public class IDToken extends JsonWebToken {
@JsonProperty(ACR) @JsonProperty(ACR)
protected String 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() { public String getNonce() {
return nonce; return nonce;
} }
@ -338,4 +347,14 @@ public class IDToken extends JsonWebToken {
public void setAcr(String acr) { public void setAcr(String acr) {
this.acr = 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;
}
} }

View file

@ -218,7 +218,11 @@ public class OIDCLoginProtocol implements LoginProtocol {
if (responseType.hasResponseType(OIDCResponseType.CODE)) { if (responseType.hasResponseType(OIDCResponseType.CODE)) {
responseBuilder.generateCodeHash(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(); AccessTokenResponse res = responseBuilder.build();

View file

@ -718,6 +718,10 @@ public class TokenManager {
boolean generateAccessTokenHash = false; boolean generateAccessTokenHash = false;
String codeHash; 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) { public AccessTokenResponseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
this.realm = realm; this.realm = realm;
this.client = client; this.client = client;
@ -819,6 +823,12 @@ public class TokenManager {
return this; 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() { public AccessTokenResponse build() {
KeyManager.ActiveRsaKey activeRsaKey = session.keys().getActiveRsaKey(realm); KeyManager.ActiveRsaKey activeRsaKey = session.keys().getActiveRsaKey(realm);
@ -854,7 +864,11 @@ public class TokenManager {
if (codeHash != null) { if (codeHash != null) {
idToken.setCodeHash(codeHash); 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) { if (idToken != null) {
String encodedToken = new JWSBuilder().type(JWT).kid(activeRsaKey.getKid()).jsonContent(idToken).sign(jwsAlgorithm, activeRsaKey.getPrivateKey()); String encodedToken = new JWSBuilder().type(JWT).kid(activeRsaKey.getKid()).jsonContent(idToken).sign(jwsAlgorithm, activeRsaKey.getPrivateKey());
res.setIdToken(encodedToken); res.setIdToken(encodedToken);

View file

@ -65,6 +65,12 @@ public class OIDCHybridResponseTypeCodeIDTokenTest extends AbstractOIDCResponseT
Assert.assertNotNull(idToken.getCodeHash()); Assert.assertNotNull(idToken.getCodeHash());
Assert.assertEquals(idToken.getCodeHash(), HashProvider.oidcHash(jwsAlgorithm, authzResponse.getCode())); 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 exchanged for the code
IDToken idToken2 = sendTokenRequestAndGetIDToken(loginEvent); IDToken idToken2 = sendTokenRequestAndGetIDToken(loginEvent);

View file

@ -66,6 +66,12 @@ public class OIDCHybridResponseTypeCodeIDTokenTokenTest extends AbstractOIDCResp
Assert.assertNotNull(idToken.getCodeHash()); Assert.assertNotNull(idToken.getCodeHash());
Assert.assertEquals(idToken.getCodeHash(), HashProvider.oidcHash(jwsAlgorithm, authzResponse.getCode())); 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 exchanged for the code
IDToken idToken2 = sendTokenRequestAndGetIDToken(loginEvent); IDToken idToken2 = sendTokenRequestAndGetIDToken(loginEvent);