Skip to content

Commit

Permalink
More integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
andreaceccanti committed Oct 23, 2021
1 parent cd8ef61 commit 88bb278
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ public class IAMJWTBearerAuthenticationProvider implements AuthenticationProvide
private final ClientDetailsEntityService clientService;
private final ClientKeyCacheService validators;


private final String TOKEN_ENDPOINT;

public IAMJWTBearerAuthenticationProvider(Clock clock, IamProperties iamProperties,
Expand Down Expand Up @@ -170,6 +169,11 @@ private void assertionChecks(ClientDetailsEntity client, SignedJWT jws) throws P
invalidBearerAssertion("invalid audience");
}
}

if (isNull(jwtClaims.getJWTID())) {
invalidBearerAssertion("jti is null");
// no further jti validation is implemented currently
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.time.Instant;
import java.time.ZoneId;
import java.util.Date;
import java.util.UUID;

import org.junit.Before;
import org.junit.Test;
Expand Down Expand Up @@ -416,6 +417,31 @@ public void testInvalidAudience() {
});
}

@Test
public void testJTIRequired() {

when(validators.getValidator(Mockito.any(), Mockito.any())).thenReturn(validator);
when(validator.validateSignature(Mockito.any())).thenReturn(true);

testForAllAlgos(client, a -> {
JWSHeader header = new JWSHeader(a);
JWTClaimsSet claimSet = new JWTClaimsSet.Builder().issuer(JWT_AUTH_NAME)
.subject(JWT_AUTH_NAME)
.expirationTime(Date.from(clock.instant().plusSeconds(1800)))
.audience(singletonList(ISSUER_TOKEN_ENDPOINT))
.build();
SignedJWT jws = new SignedJWT(header, claimSet);
when(authentication.getJwt()).thenReturn(jws);

try {
provider.authenticate(authentication);
} catch (AuthenticationServiceException e) {
assertThat(e.getMessage(), containsString("jti is null"));
}

});
}

@Test
public void testValidAssertion() {

Expand All @@ -428,6 +454,7 @@ public void testValidAssertion() {
.subject(JWT_AUTH_NAME)
.expirationTime(Date.from(clock.instant().plusSeconds(1800)))
.audience(singletonList(ISSUER_TOKEN_ENDPOINT))
.jwtID(UUID.randomUUID().toString())
.build();
SignedJWT jws = new SignedJWT(header, claimSet);
when(authentication.getJwt()).thenReturn(jws);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,16 @@

import static java.util.Collections.singletonList;

import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.time.Instant;
import java.util.Date;
import java.util.UUID;

import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
import org.mitre.jwt.signer.service.impl.DefaultJWTSigningAndValidationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
Expand All @@ -29,8 +37,9 @@
import com.nimbusds.jwt.SignedJWT;

import it.infn.mw.iam.test.oauth.EndpointsTestUtils;
import it.infn.mw.iam.util.JWKKeystoreLoader;

public class JWTBearerClientAuthenticationTestSupport extends EndpointsTestUtils {
public class JWTBearerClientAuthenticationIntegrationTestSupport extends EndpointsTestUtils {

public static final String CLIENT_ID_SECRET_JWT = "jwt-auth-client_secret_jwt";
public static final String CLIENT_ID_SECRET_JWT_SECRET = "c8e9eed0-e6e4-4a66-b16e-6f37096356a7";
Expand All @@ -39,14 +48,22 @@ public class JWTBearerClientAuthenticationTestSupport extends EndpointsTestUtils
public static final String JWT_BEARER_ASSERTION_TYPE =
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer";

public SignedJWT createClientAuthToken(String clientId, Instant expirationTime)
public static final String TEST_KEYSTORE_LOCATION = "classpath:/client_auth/client-keys.jwk";

public static final String CLIENT_ID_PRIVATE_KEY_JWT = "jwt-auth-private_key_jwt";

@Autowired
ResourceLoader loader;

public SignedJWT createSymmetricClientAuthToken(String clientId, Instant expirationTime)
throws JOSEException {

JWSSigner signer = new MACSigner(CLIENT_ID_SECRET_JWT_SECRET);
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(clientId)
.issuer(clientId)
.expirationTime(Date.from(expirationTime))
.audience(singletonList(TOKEN_ENDPOINT_AUDIENCE))
.jwtID(UUID.randomUUID().toString())
.build();

SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet);
Expand All @@ -56,4 +73,19 @@ public SignedJWT createClientAuthToken(String clientId, Instant expirationTime)
return signedJWT;
}

public JWTSigningAndValidationService loadSignerService()
throws NoSuchAlgorithmException, InvalidKeySpecException {

JWKKeystoreLoader keystoreLoader = new JWKKeystoreLoader(loader);

DefaultJWTSigningAndValidationService svc = new DefaultJWTSigningAndValidationService(
keystoreLoader.loadKeystoreFromLocation(TEST_KEYSTORE_LOCATION));

svc.setDefaultSignerKeyId("rsa1");
svc.setDefaultSigningAlgorithmName(JWSAlgorithm.RS256.getName());

return svc;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,29 @@
*/
package it.infn.mw.iam.test.oauth.assertion;

import static java.util.Collections.singletonList;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.time.Instant;
import java.util.Date;
import java.util.UUID;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.transaction.annotation.Transactional;

import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;

import it.infn.mw.iam.IamLoginService;
import it.infn.mw.iam.test.core.CoreControllerTestSupport;
Expand All @@ -37,7 +46,7 @@
@SpringApplicationConfiguration(classes = {IamLoginService.class, CoreControllerTestSupport.class})
@WebAppConfiguration
@Transactional
public class JWTBearerClientAuthenticationTests extends JWTBearerClientAuthenticationTestSupport {
public class JWTBearerClientAuthenticationIntegrationTests extends JWTBearerClientAuthenticationIntegrationTestSupport {


@Before
Expand All @@ -46,16 +55,46 @@ public void setup() throws Exception {
}

@Test
public void testJwtAuth() throws Exception {
JWT jwt = createClientAuthToken(CLIENT_ID_SECRET_JWT, Instant.now().plusSeconds(600));
public void testSymmetricJwtAuth() throws Exception {

JWT jwt = createSymmetricClientAuthToken(CLIENT_ID_SECRET_JWT, Instant.now().plusSeconds(600));
String serializedToken = jwt.serialize();

mvc
.perform(post(TOKEN_ENDPOINT).param("client_id", CLIENT_ID_SECRET_JWT)
.param("client_assertion_type", JWT_BEARER_ASSERTION_TYPE)
.param("client_assertion", serializedToken)
.param("grant_type", "client_credentials"))
.andExpect(status().isOk());
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").exists());
}

@Test
public void testAsymmetricJwtAuth() throws Exception {

JWTSigningAndValidationService signer = loadSignerService();
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(CLIENT_ID_PRIVATE_KEY_JWT)
.issuer(CLIENT_ID_PRIVATE_KEY_JWT)
.expirationTime(Date.from(Instant.now().plusSeconds(600)))
.audience(singletonList(TOKEN_ENDPOINT_AUDIENCE))
.jwtID(UUID.randomUUID().toString())
.build();

JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).keyID("rsa1").build();

SignedJWT jwt = new SignedJWT(header, claimsSet);
signer.signJwt(jwt);
String serializedToken = jwt.serialize();

mvc
.perform(post(TOKEN_ENDPOINT).param("client_id", CLIENT_ID_PRIVATE_KEY_JWT)
.param("client_assertion_type", JWT_BEARER_ASSERTION_TYPE)
.param("client_assertion", serializedToken)
.param("grant_type", "client_credentials"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").exists());


}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,8 @@ INSERT INTO client_details (id, client_id, client_secret, client_name, dynamical
(15, 'jwt-auth-client_secret_jwt', 'c8e9eed0-e6e4-4a66-b16e-6f37096356a7', 'JWT Bearer Auth Client (client_secret_jwt)',
false, null, 3600, 600, true, 'SECRET_JWT', false, 'HS256', null),
(16, 'jwt-auth-private_key_jwt', 'secret', 'JWT Bearer Auth Client (private_key_jwt)',
false, null, 3600, 600, true,'SECRET_JWT', false, 'RS256',
'{
"keys": [
{
"p": "7FhhHX--icA3yKaihMZVOJbiB2bQcs_p3HLHolTo31v_IADBrXPdxTtDQwtiM7p17yl290o-MaWS-aboa5GNpuYbNImkLDxKqk2YSQIuU_ZebMaq4CXWXVYvgAmWFAvHf6yCYXxJYSt1RqKbm-loYgaqn5K3iSwnWeEO4I9qDcM",
"kty": "RSA",
"q": "6RI38CT9BSnU6N4jY9gUO5_5F_XLKG3RgEUiExk8-6WnGCiIOAsmkeDLjT_o7xqvtx7vLpAYVgU1dtez1rRBug9q1nhTH9gbZ8YOk2XjubWIyAaYje8qmWCviGvQIK2c2QCoFBr6bAoTUs2o_eJQRRtNpPn_JSeEymY6xqcDKWk",
"d": "OANMD3sVYqk5MFTGnZnIhr2RqWYtxzr_wrcj7J8lBK5tlksvZ1bil8sHHsufXejBGLO2Ulok_pMDx-IrSt9gm1U8nkJSNMLX54XlSdi2Q39QIy2pn3nEtT-sWpkIUWRZjczRHJm7gtySWtKmQVUbLnOyowWWxIM7H9P-7bpHXs88R6MDvNhnq2csdNW9Wph0SDqousmp3uFIuKUsjCYwqxse3sIgdsUdDQ7ULdV_DjsxY-HAQ8yE93NeAeBtqPV8fA6y_VLlkR0iSPd4xqhy6SiukwLCexWhRqIREdbz_FI8ZXk3WOerLdfpe5SGKkVbIEjcync-rTBaArjWOEzFQQ",
"e": "AQAB",
"kid": "rsa1",
"qi": "M1CjStmDrWjcOI2T2P8bPj42c4iav4Dyu5x-Mc3LOTUwx2iVToHg5bZHFEf7d2H91y-EUjDzmRopuNk7dV4Fy5QWTflRCYUGvMfrgL-7rUUDBMDhl96ujmAoZrmpvlGcrsae1R_qXduU00FjCNCqGk96vgnmF77lJPNCEB7xq6k",
"dp": "ZTxWHPSjAQ481s3Jv2XVCzBWESWRFBzK54qiyH3mYgZd-a9ZRpri26DO5uDxZ4bvDUqNks9SZKGvmxBLbggizOKztIVgtTH-KYSjPmKYxY46VA2lE-4hLEnGfumcR2nkQmP6VRePtveOfHsafGY2OAby_vcxdqhbEry8SqQjoVE",
"dq": "JtImBxXiHw4MrIzzkBnZpOTMdLU4FY_VKWxadJvrkG7TGi8GIW-aCQpMXUab4desFPBOHo9Zvlo3wYfEKKr1l6whu39nORKh3fMbUmnSOIiIM-kFV_7SNaHpGuv6SrcgPPTjChZER-KVvWEMGN2tSRV3JVeOq_2dHKlSeOwwlqE",
"n": "1y1CP181zqPNPlV1JDM7Xv0QnGswhSTHe8_XPZHxDTJkykpk_1BmgA3ovP62QRE2ORgsv5oSBI_Z_RaOc4Zx2FonjEJF2oBHtBjsAiF-pxGkM5ZPjFNgFTGp1yUUBjFDcEeIGCwPEyYSt93sQIP_0DRbViMUnpyn3xgM_a1dO5brEWR2n1Uqff1yA5NXfLS03qpl2dpH4HFY5-Zs4bvtJykpAOhoHuIQbz-hmxb9MZ3uTAwsx2HiyEJtz-suyTBHO3BM2o8UcCeyfa34ShPB8i86-sf78fOk2KeRIW1Bju3ANmdV3sxL0j29cesxKCZ06u2ZiGR3Srbft8EdLPzf-w"
}
]
}');
false, null, 3600, 600, true,'PRIVATE_KEY', false, 'RS256',
'{"keys":[{"kty":"RSA","e":"AQAB","kid":"rsa1","n":"1y1CP181zqPNPlV1JDM7Xv0QnGswhSTHe8_XPZHxDTJkykpk_1BmgA3ovP62QRE2ORgsv5oSBI_Z_RaOc4Zx2FonjEJF2oBHtBjsAiF-pxGkM5ZPjFNgFTGp1yUUBjFDcEeIGCwPEyYSt93sQIP_0DRbViMUnpyn3xgM_a1dO5brEWR2n1Uqff1yA5NXfLS03qpl2dpH4HFY5-Zs4bvtJykpAOhoHuIQbz-hmxb9MZ3uTAwsx2HiyEJtz-suyTBHO3BM2o8UcCeyfa34ShPB8i86-sf78fOk2KeRIW1Bju3ANmdV3sxL0j29cesxKCZ06u2ZiGR3Srbft8EdLPzf-w"}]}');

INSERT INTO client_scope (owner_id, scope) VALUES
(1, 'openid'),
Expand Down

0 comments on commit 88bb278

Please sign in to comment.