From 6e5241e3273e6fb7d6dac0be04030a981d1c0812 Mon Sep 17 00:00:00 2001 From: samin rahman Date: Wed, 2 Sep 2026 15:31:19 +1000 Subject: [PATCH 1/5] UID2-7641: Refresh returns 400 on error paths so attacker can't glean extra info --- .../java/com/uid2/operator/vertx/UIDOperatorVerticle.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java b/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java index 2481b1019..520b580ae 100644 --- a/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java +++ b/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java @@ -1525,7 +1525,15 @@ private RefreshResponse refreshIdentity(RoutingContext rc, String tokenStr) { } catch (ClientInputValidationException cie) { LOGGER.warn("Failed to decode refresh token for site ID: " + rc.data().get(Const.RoutingContextData.SiteId), cie); return RefreshResponse.Invalid; + } catch (Exception e) { + if (e.getCause() instanceof BadPaddingException || e.getCause() instanceof IllegalBlockSizeException) { + LOGGER.warn("Failed to decode refresh token for site ID: " + rc.data().get(Const.RoutingContextData.SiteId), e); + } else { + LOGGER.error("Unexpected error decoding refresh token for site ID: " + rc.data().get(Const.RoutingContextData.SiteId), e); + } + return RefreshResponse.Invalid; } + if (refreshToken == null) { return RefreshResponse.Invalid; } From 7cfb5b1a951744c61fb0382449e4eb6a0c917a6f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 06:28:19 +0000 Subject: [PATCH 2/5] [CI Pipeline] Released Snapshot version: 5.70.281-alpha-332-SNAPSHOT [no-jira - reason: automated release] Automated release (PR #2719). --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bed0f1fbe..8c747ac06 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.uid2 uid2-operator - 5.70.280 + 5.70.281-alpha-332-SNAPSHOT UTF-8 From a87a4320f96dd6e1d7163fde805bbcae6dde1024 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 23:05:39 +0000 Subject: [PATCH 3/5] [CI Pipeline] Released Snapshot version: 5.70.282-alpha-333-SNAPSHOT [no-jira - reason: automated release] Automated release (PR #2721). --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8c747ac06..85d2e6097 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.uid2 uid2-operator - 5.70.281-alpha-332-SNAPSHOT + 5.70.282-alpha-333-SNAPSHOT UTF-8 From 2139affa618868791aed61528787b9447e142148 Mon Sep 17 00:00:00 2001 From: samin rahman Date: Thu, 3 Sep 2026 13:40:00 +1000 Subject: [PATCH 4/5] UID2-7641: Removed support for V2 refresh tokens --- .../service/EncryptedTokenEncoder.java | 53 ------------------- 1 file changed, 53 deletions(-) diff --git a/src/main/java/com/uid2/operator/service/EncryptedTokenEncoder.java b/src/main/java/com/uid2/operator/service/EncryptedTokenEncoder.java index 630918e19..95f3fdbce 100644 --- a/src/main/java/com/uid2/operator/service/EncryptedTokenEncoder.java +++ b/src/main/java/com/uid2/operator/service/EncryptedTokenEncoder.java @@ -88,49 +88,12 @@ public RefreshToken decodeRefreshToken(String s) { final Buffer b = Buffer.buffer(bytes); if (b.getByte(1) == TokenVersion.V3.rawVersion) { return decodeRefreshTokenV3(b, bytes); - } else if (b.getByte(0) == TokenVersion.V2.rawVersion) { - return decodeRefreshTokenV2(b); } } throw new ClientInputValidationException("Invalid refresh token version"); } - private RefreshToken decodeRefreshTokenV2(Buffer b) { - final Instant createdAt = Instant.ofEpochMilli(b.getLong(1)); - //final Instant expiresAt = Instant.ofEpochMilli(b.getLong(9)); - final Instant validTill = Instant.ofEpochMilli(b.getLong(17)); - final int keyId = b.getInt(25); - - final KeysetKey key = this.keyManager.getKey(keyId); - - if (key == null) { - throw new ClientInputValidationException("Failed to fetch key with id: " + keyId); - } - - final byte[] decryptedPayload = AesCbc.decrypt(b.slice(29, b.length()).getBytes(), key); - - final Buffer b2 = Buffer.buffer(decryptedPayload); - - final int siteId = b2.getInt(0); - final int length = b2.getInt(4); - final byte[] identity; - try { - identity = EncodingUtils.fromBase64(b2.slice(8, 8 + length).getBytes()); - } catch (Exception e) { - throw new ClientInputValidationException("Failed to decode refreshTokenV2: Identity segment is not valid base64.", e); - } - - final int privacyBits = b2.getInt(8 + length); - final long establishedMillis = b2.getLong(8 + length + 4); - - return new RefreshToken( - TokenVersion.V2, createdAt, validTill, - new OperatorIdentity(0, OperatorType.Service, 0, 0), - new PublisherIdentity(siteId, 0, 0), - new UserIdentity(IdentityScope.UID2, IdentityType.Email, identity, privacyBits, Instant.ofEpochMilli(establishedMillis), null)); - } - private RefreshToken decodeRefreshTokenV3(Buffer b, byte[] bytes) { final int keyId = b.getInt(2); final KeysetKey key = this.keyManager.getKey(keyId); @@ -285,9 +248,6 @@ public byte[] encode(RefreshToken t, Instant asOf) { final KeysetKey serviceKey = this.keyManager.getRefreshKey(asOf); switch (t.version) { - case V2: - recordRefreshTokenVersionCount(String.valueOf(t.publisherIdentity.siteId), TokenVersion.V2); - return encodeV2(t, serviceKey); case V3: recordRefreshTokenVersionCount(String.valueOf(t.publisherIdentity.siteId), TokenVersion.V3); return encodeV3(t, serviceKey); @@ -296,19 +256,6 @@ public byte[] encode(RefreshToken t, Instant asOf) { } } - public byte[] encodeV2(RefreshToken t, KeysetKey serviceKey) { - final Buffer b = Buffer.buffer(); - b.appendByte((byte) t.version.rawVersion); - b.appendLong(t.createdAt.toEpochMilli()); - b.appendLong(t.expiresAt.toEpochMilli()); // should not be used - // give an extra minute for clients which are trying to refresh tokens close to or at the refresh expiry timestamp - b.appendLong(t.expiresAt.plusSeconds(60).toEpochMilli()); - b.appendInt(serviceKey.getId()); - final byte[] encryptedIdentity = encryptIdentityV2(t.publisherIdentity, t.userIdentity, serviceKey); - b.appendBytes(encryptedIdentity); - return b.getBytes(); - } - public byte[] encodeV3(RefreshToken t, KeysetKey serviceKey) { final Buffer refreshPayload = Buffer.buffer(90); refreshPayload.appendLong(t.expiresAt.toEpochMilli()); From 24c0c92d72a8866afeb258d26f7b1c31e2d56afa Mon Sep 17 00:00:00 2001 From: samin rahman Date: Thu, 3 Sep 2026 16:44:43 +1000 Subject: [PATCH 5/5] UID2-7641: Removed catch all in refreshIdentity --- .../java/com/uid2/operator/vertx/UIDOperatorVerticle.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java b/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java index 520b580ae..2481b1019 100644 --- a/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java +++ b/src/main/java/com/uid2/operator/vertx/UIDOperatorVerticle.java @@ -1525,15 +1525,7 @@ private RefreshResponse refreshIdentity(RoutingContext rc, String tokenStr) { } catch (ClientInputValidationException cie) { LOGGER.warn("Failed to decode refresh token for site ID: " + rc.data().get(Const.RoutingContextData.SiteId), cie); return RefreshResponse.Invalid; - } catch (Exception e) { - if (e.getCause() instanceof BadPaddingException || e.getCause() instanceof IllegalBlockSizeException) { - LOGGER.warn("Failed to decode refresh token for site ID: " + rc.data().get(Const.RoutingContextData.SiteId), e); - } else { - LOGGER.error("Unexpected error decoding refresh token for site ID: " + rc.data().get(Const.RoutingContextData.SiteId), e); - } - return RefreshResponse.Invalid; } - if (refreshToken == null) { return RefreshResponse.Invalid; }