Skip to content

Commit 963fd0b

Browse files
authored
UID2-5673 Edit audit logging fields (#479)
* Remove operator_key_contact * Remove operator_ prefix * Change key_site_id to participant_id * Add key_id * Add Jti for token_id * Handle cases where jti is not specified * Fix typo
1 parent 520b6cd commit 963fd0b

File tree

7 files changed

+49
-13
lines changed

7 files changed

+49
-13
lines changed

src/main/java/com/uid2/shared/attest/JwtService.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,19 @@ public JwtValidationResponse validateJwt(String jwt, String audience, String iss
7373
// verify checks that the token has not expired
7474
JsonWebSignature signature = tokenVerifier.verify(jwt);
7575
JsonWebToken.Payload webToken = signature.getPayload();
76+
String jti = webToken.get("jti").toString();
77+
if (jti == null) {
78+
jti = "unknown";
79+
}
7680
response = new JwtValidationResponse(true)
7781
.withSubject(webToken.get("sub").toString())
7882
.withRoles(webToken.get("roles").toString())
7983
.withEnclaveId(webToken.get("enclaveId").toString())
8084
.withEnclaveType(webToken.get("enclaveType").toString())
8185
.withSiteId(Integer.valueOf(webToken.get("siteId").toString()))
8286
.withOperatorVersion(webToken.get("operatorVersion").toString())
83-
.withAudience(webToken.get("aud").toString());
87+
.withAudience(webToken.get("aud").toString())
88+
.withJti(jti);
8489

8590
// return the first verified response
8691
return response;

src/main/java/com/uid2/shared/attest/JwtValidationResponse.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class JwtValidationResponse {
1818

1919
private String audience;
2020
private String subject;
21+
private String jti;
2122

2223
public JwtValidationResponse(boolean isValid) {
2324
this.isValid = isValid;
@@ -66,6 +67,11 @@ public JwtValidationResponse withSubject(String subject) {
6667
return this;
6768
}
6869

70+
public JwtValidationResponse withJti(String jti) {
71+
this.jti = jti;
72+
return this;
73+
}
74+
6975
public Set<Role> getRoles() {
7076
return this.roles;
7177
}
@@ -103,4 +109,6 @@ public String getAudience() {
103109
public String getSubject() {
104110
return subject;
105111
}
112+
113+
public String getJti() { return jti; }
106114
}

src/main/java/com/uid2/shared/middleware/AttestationMiddleware.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ public void handle(RoutingContext rc) {
9999
if (CollectionUtils.isNotEmpty(response.getRoles())) {
100100
auditLogUserDetails.put("jwt_roles", new ArrayList<>(response.getRoles()));
101101
}
102+
auditLogUserDetails.put("token_id", response.getJti());
102103

103104
String subject = calculateSubject(operatorKey);
104105
auditLogUserDetails.put("jwt_subject", subject);

src/main/java/com/uid2/shared/middleware/AuthMiddleware.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ public static void setAuthClient(RoutingContext rc, IAuthorizable profile) {
5959
rc.data().put(API_CONTACT_PROP, profile.getContact());
6060
if (profile instanceof OperatorKey operatorKey) {
6161
JsonObject auditLogUserDetails = new JsonObject();
62-
auditLogUserDetails.put("operator_key_name", operatorKey.getName());
63-
auditLogUserDetails.put("operator_key_contact", operatorKey.getContact());
64-
auditLogUserDetails.put("operator_key_site_id", operatorKey.getSiteId());
62+
auditLogUserDetails.put("key_id", operatorKey.getKeyId());
63+
auditLogUserDetails.put("key_name", operatorKey.getName());
64+
auditLogUserDetails.put("participant_id", operatorKey.getSiteId());
6565
rc.put(Audit.USER_DETAILS, auditLogUserDetails);
6666
}
6767
}

src/test/java/com/uid2/shared/attest/JwtServiceTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@
2727
import static org.mockito.Mockito.*;
2828

2929
public class JwtServiceTest {
30-
private final static String VALID_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjQ4NDgzNjAxNjgsImlhdCI6MTY5MjY4NjU3NCwic3ViIjoiS0FPUnhzUGRycDRqVnBQcWIwS2dwWWFsRjVSekJwZXAwZGR2UStsakYzdnltaFJYdjNDNkNJWERmaE5KV2RvYmN6bW9pZWU5R0kwMFZybHRMYm1xVUE9PSIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA5MCIsIm9wZXJhdG9yVmVyc2lvbiI6IlNwZWNpYWx8dWlkMi1vcGVyYXRvcnwyLjcuMTYtU05BUFNIT1QiLCJlbmNsYXZlVHlwZSI6InRydXN0ZWQiLCJyb2xlcyI6Ik9QVE9VVCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4OCIsIm5hbWUiOiJTcGVjaWFsIiwic2l0ZUlkIjoiOTk5IiwiZW5jbGF2ZUlkIjoiVHJ1c3RlZCJ9.ICbHPZnm25hIa69A06aNfduyh1xSdXDgxI0_ORGqdNdrop_6CCqk5HtM5uS2hJ_1opjNAjQdfE_CMaAfda13_bnOaZSPe-aQLQBx6vCEjYCRUaAvD7p4NgHnEz9u0nf-ijFBZh0MZR8MV2s3pcEcSbZ7rkSFYW73n2KuACDQxCRdl39wQ-Bs222pm0Q-BkCZlb6OFp6pd-SUT_VWHeSeWbSyXQpfald1WAmH1rHyUdWzfz005Jv8JCucgyorhInQZY0O7wQ-UPk5nuVqz0QkT7w2S4pa4gopLpsi0BM6gu5jDfQredy49v3wuuZRAiefw4ueGBevMA6RxAqtjGEAiA";
31-
private final static String EXPIRED_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjE2OTIzODE2MzMsImlhdCI6MTY5MjY3OTg3NSwic3ViIjoiZ1k1b1hUNmZaeG5xcVZhdFNHVnE4dU9xOW1tcVEvc1A4cU9SRzZ5UStraUZyNHJCQWR6SFBORXpQTXFxK3JlNFEvb1ovaXFqa0IvL3dBLzAxT2w3RkE9PSIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA5MCIsIm9wZXJhdG9yVmVyc2lvbiI6IlNwZWNpYWwgKE9sZCwgUHJpdmF0ZSl8dWlkMi1vcGVyYXRvcnwyLjcuMTYtU05BUFNIT1QiLCJlbmNsYXZlVHlwZSI6InRydXN0ZWQiLCJyb2xlcyI6Ik9QVE9VVCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4OCIsIm5hbWUiOiJTcGVjaWFsIChPbGQsIFByaXZhdGUpIiwic2l0ZUlkIjoiOTk5IiwiZW5jbGF2ZUlkIjoiVHJ1c3RlZCJ9.gRdwQqNc7IHuPBSVjtjuBoEpr4n6vwZIFh0LbW1cqeYZd7uORYyizLyyPtG_Mh_A73LoiYQ4Rn0q1VnMdBqIqsDFYILP182XGNvsToTj_HlpP95DbKs7RfdZ7XTxKflG00M156xdJmieY_v1OQiOjw3vYt82F4QUOaIm5M_k7TAs6GgYPmhinILIJWWgljk7reGmEuWl_abU0wU4cQilwL9F9Byto5Ba8CHLAmv62PcsjbuU8LN-RrkCTYzgIqUUdRceIyrfnxP4UgKqijrjIMbeVvYyTY43anQHicoRYZCQXMaUQWk57xipFTCc-J6QKNPvh198PqtuBUBbg073Mw";
30+
// The token was generated using the private key: https://github.com/IABTechLab/uid2-admin/blob/main/src/main/resources/localstack/kms/seed.yaml#L9
31+
private final static String VALID_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ4NDgzNjAxNjgsImlhdCI6MTY5MjY4NjU3NCwic3ViIjoiS0FPUnhzUGRycDRqVnBQcWIwS2dwWWFsRjVSekJwZXAwZGR2UStsakYzdnltaFJYdjNDNkNJWERmaE5KV2RvYmN6bW9pZWU5R0kwMFZybHRMYm1xVUE9PSIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA5MCIsIm9wZXJhdG9yVmVyc2lvbiI6IlNwZWNpYWx8dWlkMi1vcGVyYXRvcnwyLjcuMTYtU05BUFNIT1QiLCJlbmNsYXZlVHlwZSI6InRydXN0ZWQiLCJyb2xlcyI6Ik9QVE9VVCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4OCIsIm5hbWUiOiJTcGVjaWFsIiwic2l0ZUlkIjoiOTk5IiwiZW5jbGF2ZUlkIjoiVHJ1c3RlZCIsImp0aSI6ImR1bW15SnRpIn0.MLfb17MtIay-RzDEmrzC4gUHyNAXpBIFJai1-aiFIOmrejInqKDbYZdgnSAbPwHWPgExvr68dyCf362SZqjw3YyUDx6snQ47o3gAqrc0_reizkmgQ_xaa5useayg-8b8Csu9C7XaCoCo0c_Lq9pid34Kmx_LtC6l8rDmyY78AbiecdN_H90BmfCgm1-4QM4gGfjfKxyvkdPioYziAEw4QQ6X2SFiPMl36PCrHkc8UE0jgaI1xCYFghZTBJ0BonIOVoGYEZld-s9OIWtMWx58HYFXygIRLMS0y5_bDq2JCiQx7KNJtckUh8E3PGkaQAHoWjHf9URSusrM_R06tJRxYQ";
32+
private final static String EXPIRED_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTIzODE2MzMsImlhdCI6MTY5MjY3OTg3NSwic3ViIjoiZ1k1b1hUNmZaeG5xcVZhdFNHVnE4dU9xOW1tcVEvc1A4cU9SRzZ5UStraUZyNHJCQWR6SFBORXpQTXFxK3JlNFEvb1ovaXFqa0IvL3dBLzAxT2w3RkE9PSIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA5MCIsIm9wZXJhdG9yVmVyc2lvbiI6IlNwZWNpYWwgKE9sZCwgUHJpdmF0ZSl8dWlkMi1vcGVyYXRvcnwyLjcuMTYtU05BUFNIT1QiLCJlbmNsYXZlVHlwZSI6InRydXN0ZWQiLCJyb2xlcyI6Ik9QVE9VVCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4OCIsIm5hbWUiOiJTcGVjaWFsIChPbGQsIFByaXZhdGUpIiwic2l0ZUlkIjoiOTk5IiwiZW5jbGF2ZUlkIjoiVHJ1c3RlZCIsImp0aSI6ImR1bW15SnRpIn0.NMJPbN_hCGkxWK9ZEBhsHKHxdXvElCDsgph4NBiMNtGVe-Yw7nZxYDpyw9cvEws9EPckMgDXfOGo9SqOhHbsapB0-JDLVebHk2yXpzft1DRF7zoY7MLXdoV5-apZDeRmJyuCAtGVpKpP03CoJ-8Dd9FETmk8x4GE0JktkKi1O7WnyNv7g8bqI8IR-VCi83sjTeQrQSBj1sV0VCYPIcg5dFcgApiYt_b2u7ugM0B090IsKK-ChigC6jKHqKLlRT5OlvnR5y7q9D37MIgsDQQbqRNaAsWzFjk1HA1iHTodNO4FlWyYJsrpC_XBnZzqqu2YLQ00cSMO2i--Cycjgmvt5A";
3233
private final static String INVALID_TOKEN = "invalid_token";
3334
private final static String PUBLIC_KEY_STRING = "-----BEGIN PUBLIC KEY-----\n" +
3435
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmvwB41qI5Fe41PDbXqcX\n" +
@@ -73,6 +74,7 @@ void validateTokenSucceeds() {
7374
assertEquals("trusted", validationResponse.getEnclaveType());
7475
assertEquals(999, validationResponse.getSiteId());
7576
assertEquals("Special|uid2-operator|2.7.16-SNAPSHOT", validationResponse.getOperatorVersion());
77+
assertEquals("dummyJti", validationResponse.getJti());
7678
}
7779

7880
@Test

src/test/java/com/uid2/shared/middleware/AttestationMiddlewareTest.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,15 @@ private void verifyAuditLogFilledWithJwt(JwtValidationResponse response) {
7878
}
7979
Assertions.assertEquals(expectedJwtRoles, auditLogUserDetailsActual.getJsonArray("jwt_roles"));
8080
Assertions.assertEquals(response.getSubject(), auditLogUserDetailsActual.getString("jwt_subject"));
81+
Assertions.assertEquals(response.getJti(), auditLogUserDetailsActual.getString("token_id"));
8182

8283
}
8384

8485
@Test
85-
void trustedValidJwtNoRolesReturnsSuccess() throws JwtService.ValidationException {
86+
void trustedValidJwtNoJtiReturnsSuccess() throws JwtService.ValidationException {
8687
var attestationMiddleware = getAttestationMiddleware(true);
8788
JwtValidationResponse response = new JwtValidationResponse(true)
89+
.withRoles(Role.OPERATOR, Role.SUPER_USER, Role.OPTOUT)
8890
.withSubject(EXPECTED_OPERATOR_KEY_HASH_DIGEST);
8991
when(this.jwtService.validateJwt("dummy jwt", JWT_AUDIENCE, JWT_ISSUER)).thenReturn(response);
9092

@@ -95,12 +97,28 @@ void trustedValidJwtNoRolesReturnsSuccess() throws JwtService.ValidationExceptio
9597
verifyAuditLogFilledWithJwt(response);
9698
}
9799

100+
@Test
101+
void trustedValidJwtNoRolesReturnsSuccess() throws JwtService.ValidationException {
102+
var attestationMiddleware = getAttestationMiddleware(true);
103+
JwtValidationResponse response = new JwtValidationResponse(true)
104+
.withSubject(EXPECTED_OPERATOR_KEY_HASH_DIGEST)
105+
.withJti("dummyJti");
106+
when(this.jwtService.validateJwt("dummy jwt", JWT_AUDIENCE, JWT_ISSUER)).thenReturn(response);
107+
108+
var handler = attestationMiddleware.handle(nextHandler);
109+
handler.handle(this.routingContext);
110+
111+
verify(nextHandler).handle(routingContext);
112+
verifyAuditLogFilledWithJwt(response);
113+
}
114+
98115
@Test
99116
void trustedValidJwtHasRequiredRoleReturnsSuccess() throws JwtService.ValidationException {
100117
var attestationMiddleware = getAttestationMiddleware(true);
101118
JwtValidationResponse response = new JwtValidationResponse(true)
102119
.withRoles(Role.OPERATOR, Role.SUPER_USER, Role.OPTOUT)
103-
.withSubject(EXPECTED_OPERATOR_KEY_HASH_DIGEST);
120+
.withSubject(EXPECTED_OPERATOR_KEY_HASH_DIGEST)
121+
.withJti("dummyJti");
104122
when(this.jwtService.validateJwt("dummy jwt", JWT_AUDIENCE, JWT_ISSUER)).thenReturn(response);
105123

106124
var handler = attestationMiddleware.handle(nextHandler, Role.OPERATOR);
@@ -115,7 +133,8 @@ void trustedValidJwtHasMultipleRolesReturnsSuccess() throws JwtService.Validatio
115133
var attestationMiddleware = getAttestationMiddleware(true);
116134
JwtValidationResponse response = new JwtValidationResponse(true)
117135
.withRoles(Role.OPERATOR, Role.SUPER_USER, Role.OPTOUT)
118-
.withSubject(EXPECTED_OPERATOR_KEY_HASH_DIGEST);
136+
.withSubject(EXPECTED_OPERATOR_KEY_HASH_DIGEST)
137+
.withJti("dummyJti");
119138
when(this.jwtService.validateJwt("dummy jwt", JWT_AUDIENCE, JWT_ISSUER)).thenReturn(response);
120139

121140
var handler = attestationMiddleware.handle(nextHandler, Role.OPERATOR, Role.SUPER_USER);
@@ -130,7 +149,8 @@ void trustedValidJwtMissingRequiredRoleReturns401() throws JwtService.Validation
130149
var attestationMiddleware = getAttestationMiddleware(true);
131150
JwtValidationResponse response = new JwtValidationResponse(true)
132151
.withRoles(Role.OPTOUT)
133-
.withSubject(EXPECTED_OPERATOR_KEY_HASH_DIGEST);
152+
.withSubject(EXPECTED_OPERATOR_KEY_HASH_DIGEST)
153+
.withJti("dummyJti");
134154
when(this.jwtService.validateJwt("dummy jwt", JWT_AUDIENCE, JWT_ISSUER)).thenReturn(response);
135155

136156
var handler = attestationMiddleware.handle(nextHandler, Role.OPERATOR);

src/test/java/com/uid2/shared/middleware/AuthMiddlewareTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,9 @@ private void verifyAuditLogFilled() {
115115
verify(routingContext).put(keyArgumentCaptor.capture(), jsonObjectArgumentCaptor.capture());
116116
JsonObject auditLogUserDetailsActual = jsonObjectArgumentCaptor.getValue();
117117
Assertions.assertEquals(Audit.USER_DETAILS, keyArgumentCaptor.getValue());
118-
Assertions.assertEquals(operatorKey.getName(), auditLogUserDetailsActual.getString("operator_key_name"));
119-
Assertions.assertEquals(operatorKey.getContact(), auditLogUserDetailsActual.getString("operator_key_contact"));
120-
Assertions.assertEquals(operatorKey.getSiteId().toString(), auditLogUserDetailsActual.getString("operator_key_site_id"));
118+
Assertions.assertEquals(operatorKey.getKeyId(), auditLogUserDetailsActual.getString("key_id"));
119+
Assertions.assertEquals(operatorKey.getName(), auditLogUserDetailsActual.getString("key_name"));
120+
Assertions.assertEquals(operatorKey.getSiteId().toString(), auditLogUserDetailsActual.getString("participant_id"));
121121
}
122122

123123
@Test

0 commit comments

Comments
 (0)