11package suite .core ;
22
33import common .HttpClient ;
4+ import common .KmsHelper ;
45import app .component .Core ;
56import com .fasterxml .jackson .databind .JsonNode ;
7+ import com .uid2 .shared .Const ;
68import com .uid2 .shared .attest .JwtService ;
7- import com .uid2 .shared .attest .JwtValidationResponse ;
89import io .vertx .core .json .JsonObject ;
910import org .junit .jupiter .api .condition .EnabledIf ;
1011import org .junit .jupiter .params .ParameterizedTest ;
@@ -29,6 +30,13 @@ public void testAttest_EmptyAttestationRequest(Core core) {
2930 assertEquals ("Unsuccessful POST request - URL: " + coreUrl + "/attest - Code: 400 Bad Request - Response body: {\" status\" :\" no attestation_request attached\" }" , exception .getMessage ());
3031 }
3132
33+ /**
34+ * Tests valid attestation request with JWT signing.
35+ *
36+ * Since LocalStack generates its own RSA key material,
37+ * we dynamically fetch the public key from LocalStack's
38+ * KMS using GetPublicKey API to validate JWT signatures.
39+ */
3240 @ ParameterizedTest (name = "/attest - {0}" )
3341 @ MethodSource ({
3442 "suite.core.TestData#baseArgs"
@@ -38,7 +46,7 @@ public void testAttest_ValidAttestationRequest(Core core) throws Exception {
3846
3947 JsonNode response = core .attest (validTrustedAttestationRequest );
4048
41- assertAll ("" ,
49+ assertAll ("Attestation response should be successful " ,
4250 () -> assertNotNull (response .get ("status" )),
4351 () -> assertEquals ("success" , response .get ("status" ).asText ()));
4452
@@ -48,29 +56,45 @@ public void testAttest_ValidAttestationRequest(Core core) throws Exception {
4856 () -> assertNotNull (body .get ("attestation_token" )),
4957 () -> assertNotNull (body .get ("expiresAt" )));
5058
51- JwtService jwtService = new JwtService (getConfig ());
52- assertNotNull (body .get ("attestation_jwt_optout" ));
53- JwtValidationResponse validationResponseOptOut = jwtService .validateJwt (body .get ("attestation_jwt_optout" ).asText (), Core .OPTOUT_URL , Core .CORE_URL );
54- assertAll ("testAttest_ValidAttestationRequest valid OptOut JWT. Local OptOut URL: '" + Core .OPTOUT_URL + "', Core URL: '" + Core .CORE_URL + "'" ,
55- () -> assertNotNull (validationResponseOptOut ),
56- () -> assertTrue (validationResponseOptOut .getIsValid ()));
59+ // Verify JWTs are generated - LocalStack 4.x supports KMS Sign
60+ JsonNode jwtOptoutNode = body .get ("attestation_jwt_optout" );
61+ JsonNode jwtCoreNode = body .get ("attestation_jwt_core" );
62+
63+ assertAll ("JWTs should be generated by KMS Sign" ,
64+ () -> assertNotNull (jwtOptoutNode , "attestation_jwt_optout should not be null" ),
65+ () -> assertFalse (jwtOptoutNode .isNull (), "attestation_jwt_optout should not be JSON null" ),
66+ () -> assertFalse (jwtOptoutNode .asText ().isEmpty (), "attestation_jwt_optout should not be empty" ),
67+ () -> assertNotNull (jwtCoreNode , "attestation_jwt_core should not be null" ),
68+ () -> assertFalse (jwtCoreNode .isNull (), "attestation_jwt_core should not be JSON null" ),
69+ () -> assertFalse (jwtCoreNode .asText ().isEmpty (), "attestation_jwt_core should not be empty" ));
5770
58- assertNotNull (body .get ("attestation_jwt_core" ));
59- JwtValidationResponse validationResponseCore = jwtService .validateJwt (body .get ("attestation_jwt_core" ).asText (), Core .CORE_URL , Core .CORE_URL );
60- assertAll ("testAttest_ValidAttestationRequest valid Core JWT. Local Core URL: '" + Core .CORE_URL + "'" ,
61- () -> assertNotNull (validationResponseCore ),
62- () -> assertTrue (validationResponseCore .getIsValid ()));
71+ // Verify JWT format (header.payload.signature)
72+ String jwtOptout = jwtOptoutNode .asText ();
73+ String jwtCore = jwtCoreNode .asText ();
74+ assertAll ("JWTs should have valid format" ,
75+ () -> assertEquals (3 , jwtOptout .split ("\\ ." ).length , "OptOut JWT should have 3 parts" ),
76+ () -> assertEquals (3 , jwtCore .split ("\\ ." ).length , "Core JWT should have 3 parts" ));
77+
78+ // Fetch the public key dynamically from LocalStack KMS and validate JWT signatures
79+ String publicKeyBase64 = KmsHelper .getPublicKeyFromLocalstack ();
80+ JsonObject config = new JsonObject ()
81+ .put (Const .Config .AwsKmsJwtSigningPublicKeysProp , publicKeyBase64 );
82+ JwtService jwtService = new JwtService (config );
83+
84+ // Validate optout JWT signature
85+ var optoutValidation = jwtService .validateJwt (jwtOptout , Core .OPTOUT_URL , Core .CORE_URL );
86+ assertTrue (optoutValidation .getIsValid (), "OptOut JWT signature should be valid" );
87+
88+ // Validate core JWT signature
89+ var coreValidation = jwtService .validateJwt (jwtCore , Core .CORE_URL , Core .CORE_URL );
90+ assertTrue (coreValidation .getIsValid (), "Core JWT signature should be valid" );
6391
6492 String optoutUrl = body .get ("optout_url" ).asText ();
6593 assertAll ("testAttest_ValidAttestationRequest OptOut URL not null" ,
6694 () -> assertNotNull (optoutUrl ),
6795 () -> assertEquals (Core .OPTOUT_URL , optoutUrl ));
6896 }
6997
70- private static JsonObject getConfig () {
71- return new JsonObject ("{ \" aws_kms_jwt_signing_public_keys\" : \" MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmvwB41qI5Fe41PDbXqcX5uOvSvfKh8l9QV0O3M+NsB4lKqQEP0t1hfoiXTpOgKz1ArYxHsQ2LeXifX4uwEbYJFlpVM+tyQkTWQjBOw6fsLYK2Xk4X2ylNXUUf7x3SDiOVxyvTh3OZW9kqrDBN9JxSoraNLyfw0hhW0SHpfs699SehgbQ7QWep/gVlKRLIz0XAXaZNw24s79ORcQlrCE6YD0PgQmpI/dK5xMML82n6y3qcTlywlGaU7OGIMdD+CTXA3BcOkgXeqZTXNaX1u6jCTa1lvAczun6avp5VZ4TFiuPo+y4rJ3GU+14cyT5NckEcaTKSvd86UdwK5Id9tl3bQIDAQAB\" }" );
72- }
73-
7498 @ ParameterizedTest (name = "/operator/config - {0}" )
7599 @ MethodSource ({
76100 "suite.core.TestData#baseArgs"
0 commit comments