KEYCLOAK-9756 PS256 algorithm support for token signing and validation

This commit is contained in:
Takashi Norimatsu 2018-12-17 06:29:43 +09:00 committed by Marek Posolda
parent b4973ad7b5
commit 9b3e297cd0
34 changed files with 757 additions and 364 deletions

View file

@ -27,6 +27,9 @@ public interface Algorithm {
String ES256 = "ES256";
String ES384 = "ES384";
String ES512 = "ES512";
String PS256 = "PS256";
String PS384 = "PS384";
String PS512 = "PS512";
String AES = "AES";
}

View file

@ -27,6 +27,9 @@ public class JavaAlgorithm {
public static final String ES256 = "SHA256withECDSA";
public static final String ES384 = "SHA384withECDSA";
public static final String ES512 = "SHA512withECDSA";
public static final String PS256 = "SHA256withRSAandMGF1";
public static final String PS384 = "SHA384withRSAandMGF1";
public static final String PS512 = "SHA512withRSAandMGF1";
public static final String AES = "AES";
public static final String SHA256 = "SHA-256";
@ -53,6 +56,12 @@ public class JavaAlgorithm {
return ES384;
case Algorithm.ES512:
return ES512;
case Algorithm.PS256:
return PS256;
case Algorithm.PS384:
return PS384;
case Algorithm.PS512:
return PS512;
case Algorithm.AES:
return AES;
default:
@ -81,6 +90,12 @@ public class JavaAlgorithm {
return SHA384;
case Algorithm.ES512:
return SHA512;
case Algorithm.PS256:
return SHA256;
case Algorithm.PS384:
return SHA384;
case Algorithm.PS512:
return SHA512;
case Algorithm.AES:
return AES;
default:

View file

@ -34,6 +34,9 @@ public enum Algorithm {
RS256(AlgorithmType.RSA, new RSAProvider()),
RS384(AlgorithmType.RSA, new RSAProvider()),
RS512(AlgorithmType.RSA, new RSAProvider()),
PS256(AlgorithmType.RSA, null),
PS384(AlgorithmType.RSA, null),
PS512(AlgorithmType.RSA, null),
ES256(AlgorithmType.ECDSA, null),
ES384(AlgorithmType.ECDSA, null),
ES512(AlgorithmType.ECDSA, null)

View file

@ -0,0 +1,34 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto;
import org.keycloak.models.KeycloakSession;
public class PS256ClientSignatureVerifierProviderFactory implements ClientSignatureVerifierProviderFactory {
public static final String ID = Algorithm.PS256;
@Override
public String getId() {
return ID;
}
@Override
public ClientSignatureVerifierProvider create(KeycloakSession session) {
return new AsymmetricClientSignatureVerifierProvider(session, Algorithm.PS256);
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto;
import org.keycloak.models.KeycloakSession;
public class PS256SignatureProviderFactory implements SignatureProviderFactory {
public static final String ID = Algorithm.PS256;
@Override
public String getId() {
return ID;
}
@Override
public SignatureProvider create(KeycloakSession session) {
return new AsymmetricSignatureProvider(session, Algorithm.PS256);
}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto;
import org.keycloak.models.KeycloakSession;
public class PS384ClientSignatureVerifierProviderFactory implements ClientSignatureVerifierProviderFactory {
public static final String ID = Algorithm.PS384;
@Override
public String getId() {
return ID;
}
@Override
public ClientSignatureVerifierProvider create(KeycloakSession session) {
return new AsymmetricClientSignatureVerifierProvider(session, Algorithm.PS384);
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto;
import org.keycloak.models.KeycloakSession;
public class PS384SignatureProviderFactory implements SignatureProviderFactory {
public static final String ID = Algorithm.PS384;
@Override
public String getId() {
return ID;
}
@Override
public SignatureProvider create(KeycloakSession session) {
return new AsymmetricSignatureProvider(session, Algorithm.PS384);
}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto;
import org.keycloak.models.KeycloakSession;
public class PS512ClientSignatureVerifierProviderFactory implements ClientSignatureVerifierProviderFactory {
public static final String ID = Algorithm.PS512;
@Override
public String getId() {
return ID;
}
@Override
public ClientSignatureVerifierProvider create(KeycloakSession session) {
return new AsymmetricClientSignatureVerifierProvider(session, Algorithm.PS512);
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright 2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.crypto;
import org.keycloak.models.KeycloakSession;
public class PS512SignatureProviderFactory implements SignatureProviderFactory {
public static final String ID = Algorithm.PS512;
@Override
public String getId() {
return ID;
}
@Override
public SignatureProvider create(KeycloakSession session) {
return new AsymmetricSignatureProvider(session, Algorithm.PS512);
}
}

View file

@ -55,9 +55,10 @@ public interface Attributes {
"16", "24", "32", "64", "128", "256", "512");
String ALGORITHM_KEY = "algorithm";
ProviderConfigProperty RS_ALGORITHM_PROPERTY = new ProviderConfigProperty(ALGORITHM_KEY, "Algorithm", "Intended algorithm for the key", LIST_TYPE,
Algorithm.RS256,
Algorithm.RS256, Algorithm.RS384, Algorithm.RS512);
Algorithm.RS256, Algorithm.RS384, Algorithm.RS512, Algorithm.PS256, Algorithm.PS384, Algorithm.PS512);
ProviderConfigProperty HS_ALGORITHM_PROPERTY = new ProviderConfigProperty(ALGORITHM_KEY, "Algorithm", "Intended algorithm for the key", LIST_TYPE,
Algorithm.HS256,

View file

@ -59,7 +59,7 @@ public class GeneratedRsaKeyProviderFactory extends AbstractRsaKeyProviderFactor
@Override
public boolean createFallbackKeys(KeycloakSession session, KeyUse keyUse, String algorithm) {
if (keyUse.equals(KeyUse.SIG) && (algorithm.equals(Algorithm.RS256) || algorithm.equals(Algorithm.RS384) || algorithm.equals(Algorithm.RS512))) {
if (keyUse.equals(KeyUse.SIG) && isSupportedRsaAlgorithm(algorithm)) {
RealmModel realm = session.getContext().getRealm();
ComponentModel generated = new ComponentModel();
@ -81,6 +81,15 @@ public class GeneratedRsaKeyProviderFactory extends AbstractRsaKeyProviderFactor
}
}
private boolean isSupportedRsaAlgorithm(String algorithm) {
return algorithm.equals(Algorithm.RS256)
|| algorithm.equals(Algorithm.PS256)
|| algorithm.equals(Algorithm.RS384)
|| algorithm.equals(Algorithm.PS384)
|| algorithm.equals(Algorithm.RS512)
|| algorithm.equals(Algorithm.PS512);
}
@Override
public void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel model) throws ComponentValidationException {
super.validateConfiguration(session, realm, model);

View file

@ -4,3 +4,6 @@ org.keycloak.crypto.RS512ClientSignatureVerifierProviderFactory
org.keycloak.crypto.ES256ClientSignatureVerifierProviderFactory
org.keycloak.crypto.ES384ClientSignatureVerifierProviderFactory
org.keycloak.crypto.ES512ClientSignatureVerifierProviderFactory
org.keycloak.crypto.PS256ClientSignatureVerifierProviderFactory
org.keycloak.crypto.PS384ClientSignatureVerifierProviderFactory
org.keycloak.crypto.PS512ClientSignatureVerifierProviderFactory

View file

@ -7,3 +7,6 @@ org.keycloak.crypto.HS512SignatureProviderFactory
org.keycloak.crypto.ES256SignatureProviderFactory
org.keycloak.crypto.ES384SignatureProviderFactory
org.keycloak.crypto.ES512SignatureProviderFactory
org.keycloak.crypto.PS256SignatureProviderFactory
org.keycloak.crypto.PS384SignatureProviderFactory
org.keycloak.crypto.PS512SignatureProviderFactory

View file

@ -22,6 +22,7 @@ import org.jboss.resteasy.spi.BadRequestException;
import org.keycloak.OAuth2Constants;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.PemUtils;
import org.keycloak.crypto.Algorithm;
import org.keycloak.crypto.AsymmetricSignatureSignerContext;
import org.keycloak.crypto.KeyType;
import org.keycloak.crypto.KeyWrapper;
@ -29,7 +30,6 @@ import org.keycloak.crypto.SignatureSignerContext;
import org.keycloak.jose.jwk.JSONWebKeySet;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jwk.JWKBuilder;
import org.keycloak.jose.jws.Algorithm;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.testsuite.rest.TestApplicationResourceProviderFactory;
@ -77,21 +77,24 @@ public class TestingOIDCEndpointsApplicationResource {
String keyType = null;
switch (jwaAlgorithm) {
case org.keycloak.crypto.Algorithm.RS256:
case org.keycloak.crypto.Algorithm.RS384:
case org.keycloak.crypto.Algorithm.RS512:
case Algorithm.RS256:
case Algorithm.RS384:
case Algorithm.RS512:
case Algorithm.PS256:
case Algorithm.PS384:
case Algorithm.PS512:
keyType = KeyType.RSA;
keyPair = KeyUtils.generateRsaKeyPair(2048);
break;
case org.keycloak.crypto.Algorithm.ES256:
case Algorithm.ES256:
keyType = KeyType.EC;
keyPair = generateEcdsaKey("secp256r1");
break;
case org.keycloak.crypto.Algorithm.ES384:
case Algorithm.ES384:
keyType = KeyType.EC;
keyPair = generateEcdsaKey("secp384r1");
break;
case org.keycloak.crypto.Algorithm.ES512:
case Algorithm.ES512:
keyType = KeyType.EC;
keyPair = generateEcdsaKey("secp521r1");
break;
@ -193,12 +196,15 @@ public class TestingOIDCEndpointsApplicationResource {
boolean ret = false;
switch (signingAlgorithm) {
case "none":
case org.keycloak.crypto.Algorithm.RS256:
case org.keycloak.crypto.Algorithm.RS384:
case org.keycloak.crypto.Algorithm.RS512:
case org.keycloak.crypto.Algorithm.ES256:
case org.keycloak.crypto.Algorithm.ES384:
case org.keycloak.crypto.Algorithm.ES512:
case Algorithm.RS256:
case Algorithm.RS384:
case Algorithm.RS512:
case Algorithm.PS256:
case Algorithm.PS384:
case Algorithm.PS512:
case Algorithm.ES256:
case Algorithm.ES384:
case Algorithm.ES512:
ret = true;
}
return ret;

View file

@ -1205,7 +1205,7 @@ public class OAuthClient {
PublicKey publicKey = JWKParser.create(k).toPublicKey();
KeyWrapper key = new KeyWrapper();
key.setKid(key.getKid());
key.setKid(k.getKeyId());
key.setAlgorithm(k.getAlgorithm());
key.setVerifyKey(publicKey);
key.setUse(KeyUse.SIG);

View file

@ -23,7 +23,6 @@ import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;
import javax.ws.rs.core.Response;
@ -32,9 +31,11 @@ import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.common.util.Base64;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.crypto.Algorithm;
import org.keycloak.crypto.JavaAlgorithm;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.keys.GeneratedEcdsaKeyProviderFactory;
import org.keycloak.keys.GeneratedRsaKeyProviderFactory;
import org.keycloak.keys.KeyProvider;
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.representations.idm.ClientRepresentation;
@ -47,9 +48,6 @@ import org.keycloak.testsuite.arquillian.TestContext;
public class TokenSignatureUtil {
private static Logger log = Logger.getLogger(TokenSignatureUtil.class);
private static final String COMPONENT_SIGNATURE_ALGORITHM_KEY = "token.signed.response.alg";
private static final String ECDSA_ELLIPTIC_CURVE_KEY = "ecdsaEllipticCurveKey";
private static final String TEST_REALM_NAME = "test";
public static void changeRealmTokenSignatureProvider(Keycloak adminClient, String toSigAlgName) {
@ -58,10 +56,8 @@ public class TokenSignatureUtil {
public static void changeRealmTokenSignatureProvider(String realm, Keycloak adminClient, String toSigAlgName) {
RealmRepresentation rep = adminClient.realm(realm).toRepresentation();
Map<String, String> attributes = rep.getAttributes();
log.tracef("change realm test signature algorithm from %s to %s", attributes.get(COMPONENT_SIGNATURE_ALGORITHM_KEY), toSigAlgName);
log.tracef("change realm test signature algorithm from %s to %s", rep.getDefaultSignatureAlgorithm(), toSigAlgName);
rep.setDefaultSignatureAlgorithm(toSigAlgName);
rep.setAttributes(attributes);
adminClient.realm(realm).update(rep);
}
@ -88,17 +84,48 @@ public class TokenSignatureUtil {
return verifier.verify(jws.getSignature());
}
public static void registerKeyProvider(String ecNistRep, Keycloak adminClient, TestContext testContext) {
registerKeyProvider(TEST_REALM_NAME, ecNistRep, adminClient, testContext);
public static void registerKeyProvider(String jwaAlgorithmName, Keycloak adminClient, TestContext testContext) {
registerKeyProvider(TEST_REALM_NAME, jwaAlgorithmName, adminClient, testContext);
}
public static void registerKeyProvider(String realm, String ecNistRep, Keycloak adminClient, TestContext testContext) {
public static void registerKeyProvider(String realm, String jwaAlgorithmName, Keycloak adminClient, TestContext testContext) {
switch(jwaAlgorithmName) {
case Algorithm.RS256:
case Algorithm.RS384:
case Algorithm.RS512:
case Algorithm.PS256:
case Algorithm.PS384:
case Algorithm.PS512:
registerKeyProvider(realm, "algorithm", jwaAlgorithmName, GeneratedRsaKeyProviderFactory.ID, adminClient, testContext);
break;
case Algorithm.ES256:
case Algorithm.ES384:
case Algorithm.ES512:
registerKeyProvider(realm, "ecdsaEllipticCurveKey", convertAlgorithmToECDomainParamNistRep(jwaAlgorithmName), GeneratedEcdsaKeyProviderFactory.ID, adminClient, testContext);
break;
}
}
public static String convertAlgorithmToECDomainParamNistRep(String algorithm) {
switch(algorithm) {
case Algorithm.ES256 :
return "P-256";
case Algorithm.ES384 :
return "P-384";
case Algorithm.ES512 :
return "P-521";
default :
return null;
}
}
private static void registerKeyProvider(String realm, String providerSpecificKey, String providerSpecificValue, String providerId, Keycloak adminClient, TestContext testContext) {
long priority = System.currentTimeMillis();
ComponentRepresentation rep = createKeyRep("valid", GeneratedEcdsaKeyProviderFactory.ID);
ComponentRepresentation rep = createKeyRep("valid", providerId);
rep.setConfig(new MultivaluedHashMap<>());
rep.getConfig().putSingle("priority", Long.toString(priority));
rep.getConfig().putSingle(ECDSA_ELLIPTIC_CURVE_KEY, ecNistRep);
rep.getConfig().putSingle(providerSpecificKey, providerSpecificValue);
try (Response response = adminClient.realm(realm).components().add(rep)) {
String id = ApiUtil.getCreatedId(response);

View file

@ -47,7 +47,6 @@ public class AdminSignatureAlgorithmTest extends AbstractKeycloakTest {
@Test
public void changeRealmTokenAlgorithm() throws Exception {
TokenSignatureUtil.registerKeyProvider("master", "P-256", adminClient, testContext);
TokenSignatureUtil.changeRealmTokenSignatureProvider("master", adminClient, Algorithm.ES256);
try (Keycloak adminClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(), suiteContext.getAuthServerInfo().getContextRoot().toString())) {

View file

@ -73,7 +73,6 @@ public class InitialAccessTokenTest extends AbstractClientRegistrationTest {
@Test
public void createWithES256() throws JWSInputException, ClientRegistrationException {
try {
TokenSignatureUtil.registerKeyProvider("P-256", adminClient, testContext);
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.ES256);
ClientInitialAccessPresentation response = resource.create(new ClientInitialAccessCreatePresentation());
@ -82,7 +81,7 @@ public class InitialAccessTokenTest extends AbstractClientRegistrationTest {
String token = response.getToken();
JWSHeader header = new JWSInput(token).getHeader();
assertEquals("HS256", header.getAlgorithm().name());
assertEquals(Algorithm.HS256, header.getAlgorithm().name());
ClientRepresentation rep = new ClientRepresentation();
ClientRepresentation created = reg.create(rep);

View file

@ -206,33 +206,42 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
@Test
public void testSignaturesRequired() throws Exception {
OIDCClientRepresentation clientRep = createRep();
clientRep.setUserinfoSignedResponseAlg(Algorithm.ES256.toString());
clientRep.setRequestObjectSigningAlg(Algorithm.ES256.toString());
OIDCClientRepresentation clientRep = null;
OIDCClientRepresentation response = null;
try {
clientRep = createRep();
clientRep.setUserinfoSignedResponseAlg(Algorithm.ES256.toString());
clientRep.setRequestObjectSigningAlg(Algorithm.ES256.toString());
OIDCClientRepresentation response = reg.oidc().create(clientRep);
Assert.assertEquals(Algorithm.ES256.toString(), response.getUserinfoSignedResponseAlg());
Assert.assertEquals(Algorithm.ES256.toString(), response.getRequestObjectSigningAlg());
Assert.assertNotNull(response.getClientSecret());
response = reg.oidc().create(clientRep);
Assert.assertEquals(Algorithm.ES256.toString(), response.getUserinfoSignedResponseAlg());
Assert.assertEquals(Algorithm.ES256.toString(), response.getRequestObjectSigningAlg());
Assert.assertNotNull(response.getClientSecret());
// Test Keycloak representation
ClientRepresentation kcClient = getClient(response.getClientId());
OIDCAdvancedConfigWrapper config = OIDCAdvancedConfigWrapper.fromClientRepresentation(kcClient);
Assert.assertEquals(config.getUserInfoSignedResponseAlg(), Algorithm.ES256);
Assert.assertEquals(config.getRequestObjectSignatureAlg(), Algorithm.ES256);
// Test Keycloak representation
ClientRepresentation kcClient = getClient(response.getClientId());
OIDCAdvancedConfigWrapper config = OIDCAdvancedConfigWrapper.fromClientRepresentation(kcClient);
Assert.assertEquals(config.getUserInfoSignedResponseAlg(), Algorithm.ES256);
Assert.assertEquals(config.getRequestObjectSignatureAlg(), Algorithm.ES256);
// update (ES256 to RS256)
clientRep.setUserinfoSignedResponseAlg(Algorithm.RS256.toString());
clientRep.setRequestObjectSigningAlg(Algorithm.RS256.toString());
response = reg.oidc().create(clientRep);
Assert.assertEquals(Algorithm.RS256.toString(), response.getUserinfoSignedResponseAlg());
Assert.assertEquals(Algorithm.RS256.toString(), response.getRequestObjectSigningAlg());
// update (ES256 to PS256)
clientRep.setUserinfoSignedResponseAlg(Algorithm.PS256.toString());
clientRep.setRequestObjectSigningAlg(Algorithm.PS256.toString());
response = reg.oidc().create(clientRep);
Assert.assertEquals(Algorithm.PS256.toString(), response.getUserinfoSignedResponseAlg());
Assert.assertEquals(Algorithm.PS256.toString(), response.getRequestObjectSigningAlg());
// keycloak representation
kcClient = getClient(response.getClientId());
config = OIDCAdvancedConfigWrapper.fromClientRepresentation(kcClient);
Assert.assertEquals(config.getUserInfoSignedResponseAlg(), Algorithm.RS256);
Assert.assertEquals(config.getRequestObjectSignatureAlg(), Algorithm.RS256);
// keycloak representation
kcClient = getClient(response.getClientId());
config = OIDCAdvancedConfigWrapper.fromClientRepresentation(kcClient);
Assert.assertEquals(config.getUserInfoSignedResponseAlg(), Algorithm.PS256);
Assert.assertEquals(config.getRequestObjectSignatureAlg(), Algorithm.PS256);
} finally {
// back to RS256 for other tests
clientRep.setUserinfoSignedResponseAlg(Algorithm.RS256.toString());
clientRep.setRequestObjectSigningAlg(Algorithm.RS256.toString());
response = reg.oidc().create(clientRep);
}
}
@Test

View file

@ -379,7 +379,6 @@ public class LoginTest extends AbstractTestRealmKeycloakTest {
assertEquals("HS256", algorithm);
try {
TokenSignatureUtil.registerKeyProvider("P-256", adminClient, testContext);
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.ES256);
oauth.openLoginForm();

View file

@ -96,6 +96,9 @@ public class FallbackKeyProviderTest extends AbstractKeycloakTest {
String[] algorithmsToTest = new String[] {
Algorithm.RS384,
Algorithm.RS512,
Algorithm.PS256,
Algorithm.PS384,
Algorithm.PS512,
Algorithm.ES256,
Algorithm.ES384,
Algorithm.ES512

View file

@ -1057,66 +1057,6 @@ public class AccessTokenTest extends AbstractKeycloakTest {
.post(Entity.form(form));
}
@Test
public void accessTokenRequest_RealmRS256_ClientRS384_EffectiveRS384() throws Exception {
try {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS384);
tokenRequest(Algorithm.HS256, Algorithm.RS384, Algorithm.RS256);
} finally {
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
}
@Test
public void accessTokenRequest_RealmRS512_ClientRS512_EffectiveRS512() throws Exception {
try {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS512);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS512);
tokenRequest(Algorithm.HS256, Algorithm.RS512, Algorithm.RS512);
} finally {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
}
@Test
public void accessTokenRequest_RealmRS256_ClientES256_EffectiveES256() throws Exception {
try {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.ES256);
TokenSignatureUtil.registerKeyProvider("P-256", adminClient, testContext);
tokenRequestSignatureVerifyOnly(Algorithm.HS256, Algorithm.ES256, Algorithm.RS256);
} finally {
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
}
@Test
public void accessTokenRequest_RealmES384_ClientES384_EffectiveES384() throws Exception {
try {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.ES384);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.ES384);
TokenSignatureUtil.registerKeyProvider("P-384", adminClient, testContext);
tokenRequestSignatureVerifyOnly(Algorithm.HS256, Algorithm.ES384, Algorithm.ES384);
} finally {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
}
@Test
public void accessTokenRequest_RealmRS256_ClientES512_EffectiveES512() throws Exception {
try {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.ES512);
TokenSignatureUtil.registerKeyProvider("P-521", adminClient, testContext);
tokenRequestSignatureVerifyOnly(Algorithm.HS256, Algorithm.ES512, Algorithm.RS256);
} finally {
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
}
@Test
public void clientAccessTokenLifespanOverride() {
ClientResource client = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
@ -1161,6 +1101,59 @@ public class AccessTokenTest extends AbstractKeycloakTest {
}
}
@Test
public void accessTokenRequest_ClientPS384_RealmRS256() throws Exception {
conductAccessTokenRequest(Algorithm.HS256, Algorithm.PS384, Algorithm.RS256);
}
@Test
public void accessTokenRequest_ClientPS256_RealmPS256() throws Exception {
conductAccessTokenRequest(Algorithm.HS256, Algorithm.PS256, Algorithm.PS256);
}
@Test
public void accessTokenRequest_ClientPS512_RealmPS256() throws Exception {
conductAccessTokenRequest(Algorithm.HS256, Algorithm.PS512, Algorithm.PS256);
}
@Test
public void accessTokenRequest_ClientRS384_RealmRS256() throws Exception {
conductAccessTokenRequest(Algorithm.HS256, Algorithm.RS384, Algorithm.RS256);
}
@Test
public void accessTokenRequest_ClientRS512_RealmRS512() throws Exception {
conductAccessTokenRequest(Algorithm.HS256, Algorithm.RS512, Algorithm.RS512);
}
@Test
public void accessTokenRequest_ClientES256_RealmPS256() throws Exception {
conductAccessTokenRequest(Algorithm.HS256, Algorithm.ES256, Algorithm.PS256);
}
@Test
public void accessTokenRequest_ClientES384_RealmES384() throws Exception {
conductAccessTokenRequest(Algorithm.HS256, Algorithm.ES384, Algorithm.ES384);
}
@Test
public void accessTokenRequest_ClientES512_RealmRS256() throws Exception {
conductAccessTokenRequest(Algorithm.HS256, Algorithm.ES512, Algorithm.RS256);
}
private void conductAccessTokenRequest(String expectedRefreshAlg, String expectedAccessAlg, String expectedIdTokenAlg) throws Exception {
try {
/// Realm Setting is used for ID Token Signature Algorithm
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, expectedIdTokenAlg);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), expectedAccessAlg);
tokenRequest(expectedRefreshAlg, expectedAccessAlg, expectedIdTokenAlg);
} finally {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
return;
}
private void tokenRequest(String expectedRefreshAlg, String expectedAccessAlg, String expectedIdTokenAlg) throws Exception {
oauth.doLogin("test-user@localhost", "password");
@ -1204,33 +1197,4 @@ public class AccessTokenTest extends AbstractKeycloakTest {
assertEquals(sessionId, token.getSessionState());
}
private void tokenRequestSignatureVerifyOnly(String expectedRefreshAlg, String expectedAccessAlg, String expectedIdTokenAlg) throws Exception {
oauth.doLogin("test-user@localhost", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
assertEquals(200, response.getStatusCode());
assertEquals("bearer", response.getTokenType());
JWSHeader header = new JWSInput(response.getAccessToken()).getHeader();
assertEquals(expectedAccessAlg, header.getAlgorithm().name());
assertEquals("JWT", header.getType());
assertNull(header.getContentType());
header = new JWSInput(response.getIdToken()).getHeader();
assertEquals(expectedIdTokenAlg, header.getAlgorithm().name());
assertEquals("JWT", header.getType());
assertNull(header.getContentType());
header = new JWSInput(response.getRefreshToken()).getHeader();
assertEquals(expectedRefreshAlg, header.getAlgorithm().name());
assertEquals("JWT", header.getType());
assertNull(header.getContentType());
assertEquals(TokenSignatureUtil.verifySignature(expectedAccessAlg, response.getAccessToken(), adminClient), true);
assertEquals(TokenSignatureUtil.verifySignature(expectedIdTokenAlg, response.getIdToken(), adminClient), true);
}
}

View file

@ -30,6 +30,7 @@ import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RoleResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.crypto.Algorithm;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.jose.jws.JWSHeader;
@ -695,6 +696,28 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
testOfflineSessionExpiration(IDLE_LIFESPAN, MAX_LIFESPAN, IDLE_LIFESPAN + SessionTimeoutHelper.IDLE_TIMEOUT_WINDOW_SECONDS + 60);
}
@Test
public void offlineTokenRequest_ClientES256_RealmPS256() throws Exception {
conductOfflineTokenRequest(Algorithm.HS256, Algorithm.ES256, Algorithm.PS256);
}
@Test
public void offlineTokenRequest_ClientPS256_RealmES256() throws Exception {
conductOfflineTokenRequest(Algorithm.HS256, Algorithm.PS256, Algorithm.ES256);
}
private void conductOfflineTokenRequest(String expectedRefreshAlg, String expectedAccessAlg, String expectedIdTokenAlg) throws Exception {
try {
/// Realm Setting is used for ID Token Signature Algorithm
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, expectedIdTokenAlg);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "offline-client"), expectedAccessAlg);
offlineTokenRequest(expectedRefreshAlg, expectedAccessAlg, expectedIdTokenAlg);
} finally {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "offline-client"), Algorithm.RS256);
}
}
private void testOfflineSessionExpiration(int idleTime, int maxLifespan, int offset) {
int prev[] = null;
try {
@ -821,15 +844,4 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
}
@Test
public void offlineTokenRequest_RealmRS512_ClientRS384() throws Exception {
try {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, "RS512");
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "offline-client"), "RS384");
offlineTokenRequest("HS256","RS384", "RS512");
} finally {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, "RS256");
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "offline-client"), "RS256");
}
}
}

View file

@ -871,6 +871,46 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
assertNotNull(response.getRefreshToken());
}
@Test
public void tokenRefreshRequest_ClientRS384_RealmRS384() throws Exception {
conductTokenRefreshRequest(Algorithm.HS256, Algorithm.RS384, Algorithm.RS384);
}
@Test
public void tokenRefreshRequest_ClientRS512_RealmRS256() throws Exception {
conductTokenRefreshRequest(Algorithm.HS256, Algorithm.RS512, Algorithm.RS256);
}
@Test
public void tokenRefreshRequest_ClientES256_RealmRS256() throws Exception {
conductTokenRefreshRequest(Algorithm.HS256, Algorithm.ES256, Algorithm.RS256);
}
@Test
public void tokenRefreshRequest_ClientES384_RealmES384() throws Exception {
conductTokenRefreshRequest(Algorithm.HS256, Algorithm.ES384, Algorithm.ES384);
}
@Test
public void tokenRefreshRequest_ClientES512_RealmRS256() throws Exception {
conductTokenRefreshRequest(Algorithm.HS256, Algorithm.ES512, Algorithm.RS256);
}
@Test
public void tokenRefreshRequest_ClientPS256_RealmRS256() throws Exception {
conductTokenRefreshRequest(Algorithm.HS256, Algorithm.PS256, Algorithm.RS256);
}
@Test
public void tokenRefreshRequest_ClientPS384_RealmES384() throws Exception {
conductTokenRefreshRequest(Algorithm.HS256, Algorithm.PS384, Algorithm.ES384);
}
@Test
public void tokenRefreshRequest_ClientPS512_RealmPS256() throws Exception {
conductTokenRefreshRequest(Algorithm.HS256, Algorithm.PS512, Algorithm.PS256);
}
protected Response executeRefreshToken(WebTarget refreshTarget, String refreshToken) {
String header = BasicAuthHelper.createHeader("test-app", "password");
Form form = new Form();
@ -892,6 +932,18 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
.post(Entity.form(form));
}
private void conductTokenRefreshRequest(String expectedRefreshAlg, String expectedAccessAlg, String expectedIdTokenAlg) throws Exception {
try {
// Realm setting is used for ID Token signature algorithm
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, expectedIdTokenAlg);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), expectedAccessAlg);
refreshToken(expectedRefreshAlg, expectedAccessAlg, expectedIdTokenAlg);
} finally {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
}
private void refreshToken(String expectedRefreshAlg, String expectedAccessAlg, String expectedIdTokenAlg) throws Exception {
oauth.doLogin("test-user@localhost", "password");
@ -957,118 +1009,4 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
setTimeOffset(0);
}
@Test
public void tokenRefreshRequest_RealmRS384_ClientRS384_EffectiveRS384() throws Exception {
try {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS384);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS384);
refreshToken(Algorithm.HS256, Algorithm.RS384, Algorithm.RS384);
} finally {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
}
@Test
public void tokenRefreshRequest_RealmRS256_ClientRS512_EffectiveRS256() throws Exception {
try {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS512);
refreshToken(Algorithm.HS256, Algorithm.RS512, Algorithm.RS256);
} finally {
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
}
@Test
public void tokenRefreshRequest_RealmRS256_ClientES256_EffectiveRS256() throws Exception {
try {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.ES256);
TokenSignatureUtil.registerKeyProvider("P-256", adminClient, testContext);
refreshTokenSignatureVerifyOnly(Algorithm.HS256, Algorithm.ES256, Algorithm.RS256);
} finally {
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
}
@Test
public void tokenRefreshRequest_RealmES384_ClientES384_EffectiveES384() throws Exception {
try {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.ES384);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.ES384);
TokenSignatureUtil.registerKeyProvider("P-384", adminClient, testContext);
refreshTokenSignatureVerifyOnly(Algorithm.HS256, Algorithm.ES384, Algorithm.ES384);
} finally {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
}
@Test
public void tokenRefreshRequest_RealmRS256_ClientES512_EffectiveRS256() throws Exception {
try {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.ES512);
TokenSignatureUtil.registerKeyProvider("P-521", adminClient, testContext);
refreshTokenSignatureVerifyOnly(Algorithm.HS256, Algorithm.ES512, Algorithm.RS256);
} finally {
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
}
private void refreshTokenSignatureVerifyOnly(String expectedRefreshAlg, String expectedAccessAlg, String expectedIdTokenAlg) throws Exception {
oauth.doLogin("test-user@localhost", "password");
EventRepresentation loginEvent = events.expectLogin().assertEvent();
String sessionId = loginEvent.getSessionId();
String codeId = loginEvent.getDetails().get(Details.CODE_ID);
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
JWSHeader header = new JWSInput(tokenResponse.getAccessToken()).getHeader();
assertEquals(expectedAccessAlg, header.getAlgorithm().name());
assertEquals("JWT", header.getType());
assertNull(header.getContentType());
header = new JWSInput(tokenResponse.getIdToken()).getHeader();
assertEquals(expectedIdTokenAlg, header.getAlgorithm().name());
assertEquals("JWT", header.getType());
assertNull(header.getContentType());
header = new JWSInput(tokenResponse.getRefreshToken()).getHeader();
assertEquals(expectedRefreshAlg, header.getAlgorithm().name());
assertEquals("JWT", header.getType());
assertNull(header.getContentType());
String refreshTokenString = tokenResponse.getRefreshToken();
EventRepresentation tokenEvent = events.expectCodeToToken(codeId, sessionId).assertEvent();
assertNotNull(refreshTokenString);
assertEquals("bearer", tokenResponse.getTokenType());
setTimeOffset(2);
OAuthClient.AccessTokenResponse response = oauth.doRefreshTokenRequest(refreshTokenString, "password");
assertEquals(200, response.getStatusCode());
assertEquals("bearer", response.getTokenType());
// decode JWS for refreshed access token and refresh token
assertEquals(TokenSignatureUtil.verifySignature(expectedAccessAlg, response.getAccessToken(), adminClient), true);
assertEquals(TokenSignatureUtil.verifySignature(expectedIdTokenAlg, response.getIdToken(), adminClient), true);
EventRepresentation refreshEvent = events.expectRefresh(tokenEvent.getDetails().get(Details.REFRESH_TOKEN_ID), sessionId).assertEvent();
Assert.assertNotEquals(tokenEvent.getDetails().get(Details.TOKEN_ID), refreshEvent.getDetails().get(Details.TOKEN_ID));
Assert.assertNotEquals(tokenEvent.getDetails().get(Details.REFRESH_TOKEN_ID), refreshEvent.getDetails().get(Details.UPDATED_REFRESH_TOKEN_ID));
setTimeOffset(0);
}
}

View file

@ -26,8 +26,11 @@ import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
import org.keycloak.crypto.Algorithm;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.jose.jws.JWSHeader;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.TimeBasedOTP;
@ -39,17 +42,20 @@ import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.RealmManager;
import org.keycloak.testsuite.util.TokenSignatureUtil;
import org.keycloak.testsuite.util.UserBuilder;
import org.keycloak.testsuite.util.UserManager;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -221,6 +227,79 @@ public class ResourceOwnerPasswordCredentialsGrantTest extends AbstractKeycloakT
events.expectRefresh(refreshToken.getId(), refreshToken.getSessionState()).user(userId).client(clientId).assertEvent();
}
@Test
public void grantRequest_ClientES256_RealmPS256() throws Exception {
conductGrantRequest(Algorithm.HS256, Algorithm.ES256, Algorithm.PS256);
}
@Test
public void grantRequest_ClientPS256_RealmES256() throws Exception {
conductGrantRequest(Algorithm.HS256, Algorithm.PS256, Algorithm.ES256);
}
private void conductGrantRequest(String expectedRefreshAlg, String expectedAccessAlg, String realmTokenAlg) throws Exception {
try {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, realmTokenAlg);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "resource-owner"), expectedAccessAlg);
grantRequest(expectedRefreshAlg, expectedAccessAlg);
} finally {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "resource-owner"), Algorithm.RS256);
}
return;
}
private void grantRequest(String expectedRefreshAlg, String expectedAccessAlg) throws Exception {
String clientId = "resource-owner";
String login = "direct-login";
oauth.clientId(clientId);
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("secret", login, "password", null);
assertEquals(200, response.getStatusCode());
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
RefreshToken refreshToken = oauth.parseRefreshToken(response.getRefreshToken());
JWSHeader header = new JWSInput(response.getAccessToken()).getHeader();
assertEquals(expectedAccessAlg, header.getAlgorithm().name());
assertEquals("JWT", header.getType());
assertNull(header.getContentType());
header = new JWSInput(response.getRefreshToken()).getHeader();
assertEquals(expectedRefreshAlg, header.getAlgorithm().name());
assertEquals("JWT", header.getType());
assertNull(header.getContentType());
events.expectLogin()
.client(clientId)
.user(userId)
.session(accessToken.getSessionState())
.detail(Details.GRANT_TYPE, OAuth2Constants.PASSWORD)
.detail(Details.TOKEN_ID, accessToken.getId())
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
.detail(Details.USERNAME, login)
.removeDetail(Details.CODE_ID)
.removeDetail(Details.REDIRECT_URI)
.removeDetail(Details.CONSENT)
.assertEvent();
Assert.assertTrue(login.equals(accessToken.getPreferredUsername()) || login.equals(accessToken.getEmail()));
assertEquals(accessToken.getSessionState(), refreshToken.getSessionState());
OAuthClient.AccessTokenResponse refreshedResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "secret");
AccessToken refreshedAccessToken = oauth.verifyToken(refreshedResponse.getAccessToken());
RefreshToken refreshedRefreshToken = oauth.parseRefreshToken(refreshedResponse.getRefreshToken());
assertEquals(accessToken.getSessionState(), refreshedAccessToken.getSessionState());
assertEquals(accessToken.getSessionState(), refreshedRefreshToken.getSessionState());
events.expectRefresh(refreshToken.getId(), refreshToken.getSessionState()).user(userId).client(clientId).assertEvent();
}
@Test
public void grantAccessTokenLogout() throws Exception {
oauth.clientId("resource-owner");

View file

@ -23,8 +23,11 @@ import org.junit.Rule;
import org.junit.Test;
import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.crypto.Algorithm;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.jose.jws.JWSHeader;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken;
@ -33,15 +36,18 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.TokenSignatureUtil;
import org.keycloak.testsuite.util.UserBuilder;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -269,4 +275,68 @@ public class ServiceAccountTest extends AbstractKeycloakTest {
userName = user.getUsername();
}
}
@Test
public void clientCredentialsAuthRequest_ClientES256_RealmPS256() throws Exception {
conductClientCredentialsAuthRequest(Algorithm.HS256, Algorithm.ES256, Algorithm.PS256);
}
private void conductClientCredentialsAuthRequest(String expectedRefreshAlg, String expectedAccessAlg, String realmTokenAlg) throws Exception {
try {
/// Realm Setting is used for ID Token Signature Algorithm
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, realmTokenAlg);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "service-account-cl"), expectedAccessAlg);
clientCredentialsAuthSuccess(expectedRefreshAlg, expectedAccessAlg);
} finally {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "service-account-cl"), Algorithm.RS256);
}
return;
}
private void clientCredentialsAuthSuccess(String expectedRefreshAlg, String expectedAccessAlg) throws Exception {
oauth.clientId("service-account-cl");
OAuthClient.AccessTokenResponse response = oauth.doClientCredentialsGrantAccessTokenRequest("secret1");
assertEquals(200, response.getStatusCode());
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
RefreshToken refreshToken = oauth.parseRefreshToken(response.getRefreshToken());
JWSHeader header = new JWSInput(response.getAccessToken()).getHeader();
assertEquals(expectedAccessAlg, header.getAlgorithm().name());
assertEquals("JWT", header.getType());
assertNull(header.getContentType());
header = new JWSInput(response.getRefreshToken()).getHeader();
assertEquals(expectedRefreshAlg, header.getAlgorithm().name());
assertEquals("JWT", header.getType());
assertNull(header.getContentType());
events.expectClientLogin()
.client("service-account-cl")
.user(userId)
.session(accessToken.getSessionState())
.detail(Details.TOKEN_ID, accessToken.getId())
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
.detail(Details.USERNAME, userName)
.assertEvent();
assertEquals(accessToken.getSessionState(), refreshToken.getSessionState());
System.out.println("Access token other claims: " + accessToken.getOtherClaims());
Assert.assertEquals("service-account-cl", accessToken.getOtherClaims().get(ServiceAccountConstants.CLIENT_ID));
Assert.assertTrue(accessToken.getOtherClaims().containsKey(ServiceAccountConstants.CLIENT_ADDRESS));
Assert.assertTrue(accessToken.getOtherClaims().containsKey(ServiceAccountConstants.CLIENT_HOST));
OAuthClient.AccessTokenResponse refreshedResponse = oauth.doRefreshTokenRequest(response.getRefreshToken(), "secret1");
AccessToken refreshedAccessToken = oauth.verifyToken(refreshedResponse.getAccessToken());
RefreshToken refreshedRefreshToken = oauth.parseRefreshToken(refreshedResponse.getRefreshToken());
assertEquals(accessToken.getSessionState(), refreshedAccessToken.getSessionState());
assertEquals(accessToken.getSessionState(), refreshedRefreshToken.getSessionState());
events.expectRefresh(refreshToken.getId(), refreshToken.getSessionState()).user(userId).client("service-account-cl").assertEvent();
}
}

View file

@ -18,7 +18,6 @@ package org.keycloak.testsuite.oauth;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.TextNode;
import org.junit.Rule;
import org.junit.Test;
@ -249,16 +248,24 @@ public class TokenIntrospectionTest extends AbstractTestRealmKeycloakTest {
@Test
public void testIntrospectAccessTokenES256() throws Exception {
testIntrospectAccessToken(Algorithm.ES256);
}
@Test
public void testIntrospectAccessTokenPS256() throws Exception {
testIntrospectAccessToken(Algorithm.PS256);
}
private void testIntrospectAccessToken(String jwaAlgorithm) throws Exception {
try {
TokenSignatureUtil.registerKeyProvider("P-256", adminClient, testContext);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.ES256);
TokenSignatureUtil.changeClientAccessTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), jwaAlgorithm);
oauth.doLogin("test-user@localhost", "password");
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
EventRepresentation loginEvent = events.expectLogin().assertEvent();
AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
assertEquals("ES256", new JWSInput(accessTokenResponse.getAccessToken()).getHeader().getAlgorithm().name());
assertEquals(jwaAlgorithm, new JWSInput(accessTokenResponse.getAccessToken()).getHeader().getAlgorithm().name());
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());

View file

@ -930,6 +930,24 @@ public class OIDCAdvancedRequestParamsTest extends AbstractTestRealmKeycloakTest
requestUriParamSignedIn(Algorithm.RS512, Algorithm.RS512);
}
@Test
public void requestUriParamSignedExpectedPS256ActualPS256() throws Exception {
// will success
requestUriParamSignedIn(Algorithm.PS256, Algorithm.PS256);
}
@Test
public void requestUriParamSignedExpectedPS384ActualPS384() throws Exception {
// will success
requestUriParamSignedIn(Algorithm.PS384, Algorithm.PS384);
}
@Test
public void requestUriParamSignedExpectedPS512ActualPS512() throws Exception {
// will success
requestUriParamSignedIn(Algorithm.PS512, Algorithm.PS512);
}
@Test
public void requestUriParamSignedExpectedAnyActualES256() throws Exception {
// Algorithm is null if 'any'

View file

@ -124,9 +124,11 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
assertContains(oidcConfig.getResponseModesSupported(), "query", "fragment");
Assert.assertNames(oidcConfig.getSubjectTypesSupported(), "pairwise", "public");
Assert.assertNames(oidcConfig.getIdTokenSigningAlgValuesSupported(), Algorithm.RS256, Algorithm.RS384, Algorithm.RS512, Algorithm.ES256, Algorithm.ES384, Algorithm.ES512, Algorithm.HS256, Algorithm.HS384, Algorithm.HS512);
Assert.assertNames(oidcConfig.getUserInfoSigningAlgValuesSupported(), "none", Algorithm.RS256, Algorithm.RS384, Algorithm.RS512, Algorithm.ES256, Algorithm.ES384, Algorithm.ES512, Algorithm.HS256, Algorithm.HS384, Algorithm.HS512);
Assert.assertNames(oidcConfig.getRequestObjectSigningAlgValuesSupported(), "none", Algorithm.RS256, Algorithm.RS384, Algorithm.RS512, Algorithm.ES256, Algorithm.ES384, Algorithm.ES512);
// Signature algorithms
Assert.assertNames(oidcConfig.getIdTokenSigningAlgValuesSupported(), Algorithm.RS256, Algorithm.RS384, Algorithm.RS512, Algorithm.ES256, Algorithm.ES384, Algorithm.ES512, Algorithm.HS256, Algorithm.HS384, Algorithm.HS512, Algorithm.PS256, Algorithm.PS384, Algorithm.PS512);
Assert.assertNames(oidcConfig.getUserInfoSigningAlgValuesSupported(), "none", Algorithm.RS256, Algorithm.RS384, Algorithm.RS512, Algorithm.ES256, Algorithm.ES384, Algorithm.ES512, Algorithm.HS256, Algorithm.HS384, Algorithm.HS512, Algorithm.PS256, Algorithm.PS384, Algorithm.PS512);
Assert.assertNames(oidcConfig.getRequestObjectSigningAlgValuesSupported(), "none", Algorithm.RS256, Algorithm.RS384, Algorithm.RS512, Algorithm.ES256, Algorithm.ES384, Algorithm.ES512, Algorithm.PS256, Algorithm.PS384, Algorithm.PS512);
// Client authentication
Assert.assertNames(oidcConfig.getTokenEndpointAuthMethodsSupported(), "client_secret_basic", "client_secret_post", "private_key_jwt", "client_secret_jwt");
@ -194,7 +196,7 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
@Test
public void certs() throws IOException {
TokenSignatureUtil.registerKeyProvider("P-256", adminClient, testContext);
TokenSignatureUtil.registerKeyProvider(Algorithm.ES256, adminClient, testContext);
OIDCConfigurationRepresentation representation = SimpleHttp.doGet(getAuthServerRoot().toString() + "realms/test/.well-known/openid-configuration", client).asJson(OIDCConfigurationRepresentation.class);
String jwksUri = representation.getJwksUri();

View file

@ -280,62 +280,12 @@ public class UserInfoTest extends AbstractKeycloakTest {
@Test
public void testSuccessSignedResponseES256() throws Exception {
testSuccessSignedResponse(Algorithm.ES256);
}
try {
TokenSignatureUtil.registerKeyProvider("P-256", adminClient, testContext);
// Require signed userInfo request
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
ClientRepresentation clientRep = clientResource.toRepresentation();
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setUserInfoSignedResponseAlg(Algorithm.ES256);
clientResource.update(clientRep);
// test signed response
Client client = ClientBuilder.newClient();
try {
AccessTokenResponse accessTokenResponse = executeGrantAccessTokenRequest(client);
Response response = UserInfoClientUtil.executeUserInfoRequest_getMethod(client, accessTokenResponse.getToken());
events.expect(EventType.USER_INFO_REQUEST)
.session(Matchers.notNullValue(String.class))
.detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN)
.detail(Details.USERNAME, "test-user@localhost")
.detail(Details.SIGNATURE_REQUIRED, "true")
.detail(Details.SIGNATURE_ALGORITHM, Algorithm.ES256.toString())
.assertEvent();
Assert.assertEquals(200, response.getStatus());
Assert.assertEquals(response.getHeaderString(HttpHeaders.CONTENT_TYPE), MediaType.APPLICATION_JWT);
String signedResponse = response.readEntity(String.class);
response.close();
JWSInput jwsInput = new JWSInput(signedResponse);
assertEquals("ES256", jwsInput.getHeader().getAlgorithm().name());
UserInfo userInfo = JsonSerialization.readValue(jwsInput.getContent(), UserInfo.class);
Assert.assertNotNull(userInfo);
Assert.assertNotNull(userInfo.getSubject());
Assert.assertEquals("test-user@localhost", userInfo.getEmail());
Assert.assertEquals("test-user@localhost", userInfo.getPreferredUsername());
Assert.assertTrue(userInfo.hasAudience("test-app"));
String expectedIssuer = Urls.realmIssuer(new URI(AUTH_SERVER_ROOT), "test");
Assert.assertEquals(expectedIssuer, userInfo.getIssuer());
} finally {
client.close();
}
// Revert signed userInfo request
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setUserInfoSignedResponseAlg(null);
clientResource.update(clientRep);
} finally {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, org.keycloak.crypto.Algorithm.RS256);
}
@Test
public void testSuccessSignedResponsePS256() throws Exception {
testSuccessSignedResponse(Algorithm.PS256);
}
@Test
@ -510,4 +460,62 @@ public class UserInfoTest extends AbstractKeycloakTest {
.assertEvent();
UserInfoClientUtil.testSuccessfulUserInfoResponse(response, "test-user@localhost", "test-user@localhost");
}
private void testSuccessSignedResponse(Algorithm sigAlg) throws Exception {
try {
// Require signed userInfo request
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
ClientRepresentation clientRep = clientResource.toRepresentation();
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setUserInfoSignedResponseAlg(sigAlg);
clientResource.update(clientRep);
// test signed response
Client client = ClientBuilder.newClient();
try {
AccessTokenResponse accessTokenResponse = executeGrantAccessTokenRequest(client);
Response response = UserInfoClientUtil.executeUserInfoRequest_getMethod(client, accessTokenResponse.getToken());
events.expect(EventType.USER_INFO_REQUEST)
.session(Matchers.notNullValue(String.class))
.detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN)
.detail(Details.USERNAME, "test-user@localhost")
.detail(Details.SIGNATURE_REQUIRED, "true")
.detail(Details.SIGNATURE_ALGORITHM, sigAlg.toString())
.assertEvent();
Assert.assertEquals(200, response.getStatus());
Assert.assertEquals(response.getHeaderString(HttpHeaders.CONTENT_TYPE), MediaType.APPLICATION_JWT);
String signedResponse = response.readEntity(String.class);
response.close();
JWSInput jwsInput = new JWSInput(signedResponse);
assertEquals(sigAlg.toString(), jwsInput.getHeader().getAlgorithm().name());
UserInfo userInfo = JsonSerialization.readValue(jwsInput.getContent(), UserInfo.class);
Assert.assertNotNull(userInfo);
Assert.assertNotNull(userInfo.getSubject());
Assert.assertEquals("test-user@localhost", userInfo.getEmail());
Assert.assertEquals("test-user@localhost", userInfo.getPreferredUsername());
Assert.assertTrue(userInfo.hasAudience("test-app"));
String expectedIssuer = Urls.realmIssuer(new URI(AUTH_SERVER_ROOT), "test");
Assert.assertEquals(expectedIssuer, userInfo.getIssuer());
} finally {
client.close();
}
// Revert signed userInfo request
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setUserInfoSignedResponseAlg(null);
clientResource.update(clientRep);
} finally {
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, org.keycloak.crypto.Algorithm.RS256);
}
}
}

View file

@ -21,6 +21,7 @@ import org.jboss.arquillian.graphene.page.Page;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuthErrorException;
import org.keycloak.crypto.Algorithm;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.jose.jws.JWSHeader;
@ -248,36 +249,44 @@ public abstract class AbstractOIDCResponseTypeTest extends AbstractTestRealmKeyc
}
@Test
public void oidcFlow_RealmRS256_ClientRS384_EffectiveRS384() throws Exception {
try {
setSignatureAlgorithm("RS384");
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, "RS256");
TokenSignatureUtil.changeClientIdTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), "RS384");
oidcFlow("RS256", "RS384");
} finally {
setSignatureAlgorithm("RS256");
TokenSignatureUtil.changeClientIdTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), "RS256");
}
public void oidcFlow_RealmRS256_ClientRS384() throws Exception {
oidcFlowRequest(Algorithm.RS256, Algorithm.RS384);
}
@Test
public void oidcFlow_RealmES256_ClientES384_EffectiveES384() throws Exception {
public void oidcFlow_RealmES256_ClientES384() throws Exception {
oidcFlowRequest(Algorithm.ES256, Algorithm.ES384);
}
@Test
public void oidcFlow_RealmRS256_ClientPS256() throws Exception {
oidcFlowRequest(Algorithm.RS256, Algorithm.PS256);
}
@Test
public void oidcFlow_RealmPS256_ClientES256() throws Exception {
oidcFlowRequest(Algorithm.PS256, Algorithm.ES256);
}
private void oidcFlowRequest(String expectedAccessAlg, String expectedIdTokenAlg) throws Exception {
try {
setSignatureAlgorithm("ES384");
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, "ES256");
TokenSignatureUtil.changeClientIdTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), "ES384");
oidcFlow("ES256", "ES384");
setIdTokenSignatureAlgorithm(expectedIdTokenAlg);
// Realm setting is used for access token signature algorithm
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, expectedAccessAlg);
TokenSignatureUtil.changeClientIdTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), expectedIdTokenAlg);
oidcFlow(expectedAccessAlg, expectedIdTokenAlg);
} finally {
setSignatureAlgorithm("RS256");
TokenSignatureUtil.changeClientIdTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), "RS256");
setIdTokenSignatureAlgorithm(Algorithm.RS256);
TokenSignatureUtil.changeRealmTokenSignatureProvider(adminClient, Algorithm.RS256);
TokenSignatureUtil.changeClientIdTokenSignatureProvider(ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app"), Algorithm.RS256);
}
}
private String sigAlgName = "RS256";
private void setSignatureAlgorithm(String sigAlgName) {
this.sigAlgName = sigAlgName;
private String idTokenSigAlgName = Algorithm.RS256;
private void setIdTokenSignatureAlgorithm(String idTokenSigAlgName) {
this.idTokenSigAlgName = idTokenSigAlgName;
}
protected String getSignatureAlgorithm() {
return this.sigAlgName;
protected String getIdTokenSignatureAlgorithm() {
return this.idTokenSigAlgName;
}
}

View file

@ -64,14 +64,14 @@ public class OIDCHybridResponseTypeCodeIDTokenTest extends AbstractOIDCResponseT
Assert.assertNull(idToken.getAccessTokenHash());
Assert.assertNotNull(idToken.getCodeHash());
Assert.assertEquals(idToken.getCodeHash(), HashUtils.oidcHash(getSignatureAlgorithm(), authzResponse.getCode()));
Assert.assertEquals(idToken.getCodeHash(), HashUtils.oidcHash(getIdTokenSignatureAlgorithm(), 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(), HashUtils.oidcHash(getSignatureAlgorithm(), authzResponse.getState()));
Assert.assertEquals(idToken.getStateHash(), HashUtils.oidcHash(getIdTokenSignatureAlgorithm(), authzResponse.getState()));
// IDToken exchanged for the code
IDToken idToken2 = sendTokenRequestAndGetIDToken(loginEvent);

View file

@ -63,17 +63,17 @@ public class OIDCHybridResponseTypeCodeIDTokenTokenTest extends AbstractOIDCResp
// Validate "at_hash" and "c_hash"
Assert.assertNotNull(idToken.getAccessTokenHash());
Assert.assertEquals(idToken.getAccessTokenHash(), HashUtils.oidcHash(getSignatureAlgorithm(), authzResponse.getAccessToken()));
Assert.assertEquals(idToken.getAccessTokenHash(), HashUtils.oidcHash(getIdTokenSignatureAlgorithm(), authzResponse.getAccessToken()));
Assert.assertNotNull(idToken.getCodeHash());
Assert.assertEquals(idToken.getCodeHash(), HashUtils.oidcHash(getSignatureAlgorithm(), authzResponse.getCode()));
Assert.assertEquals(idToken.getCodeHash(), HashUtils.oidcHash(getIdTokenSignatureAlgorithm(), 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(), HashUtils.oidcHash(getSignatureAlgorithm(), authzResponse.getState()));
Assert.assertEquals(idToken.getStateHash(), HashUtils.oidcHash(getIdTokenSignatureAlgorithm(), authzResponse.getState()));
// IDToken exchanged for the code
IDToken idToken2 = sendTokenRequestAndGetIDToken(loginEvent);

View file

@ -62,7 +62,7 @@ public class OIDCImplicitResponseTypeIDTokenTokenTest extends AbstractOIDCRespon
// Validate "at_hash"
Assert.assertNotNull(idToken.getAccessTokenHash());
Assert.assertEquals(idToken.getAccessTokenHash(), HashUtils.oidcHash(getSignatureAlgorithm(), authzResponse.getAccessToken()));
Assert.assertEquals(idToken.getAccessTokenHash(), HashUtils.oidcHash(getIdTokenSignatureAlgorithm(), authzResponse.getAccessToken()));
Assert.assertNull(idToken.getCodeHash());
return Collections.singletonList(idToken);