KEYCLOAK-6700 Financial API Read and Write API Security Profile : state hash value (s_hash) to protect state parameter
This commit is contained in:
parent
871ecf83fb
commit
e72756d01a
5 changed files with 51 additions and 2 deletions
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue