Skip to content

Commit 670656b

Browse files
authored
Merge pull request #63 from IABTechLab/ccm-UID2-2878-dsp-refactor-and-check-token-lifetimes
Add BidstreamClient and SharingClient, deprecate UID2Client
2 parents 7ed7891 + 7429ead commit 670656b

32 files changed

+1691
-161
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@
6262
<artifactId>okhttp</artifactId>
6363
<version>4.10.0</version>
6464
</dependency>
65+
<dependency>
66+
<groupId>org.junit.jupiter</groupId>
67+
<artifactId>junit-jupiter-params</artifactId>
68+
<version>5.8.0</version>
69+
<scope>test</scope>
70+
</dependency>
6571
</dependencies>
6672

6773
<build>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.uid2.client;
2+
3+
import java.time.Instant;
4+
5+
public class BidstreamClient {
6+
private final TokenHelper tokenHelper;
7+
8+
public BidstreamClient(String baseUrl, String clientApiKey, String base64SecretKey) {
9+
tokenHelper = new TokenHelper(baseUrl, clientApiKey, base64SecretKey);
10+
}
11+
12+
public DecryptionResponse decryptTokenIntoRawUid(String token, String domainNameFromBidRequest) {
13+
return tokenHelper.decrypt(token, Instant.now(), domainNameFromBidRequest, ClientType.BIDSTREAM);
14+
}
15+
16+
DecryptionResponse decryptTokenIntoRawUid(String token, String domainNameFromBidRequest, Instant now) {
17+
return tokenHelper.decrypt(token, now, domainNameFromBidRequest, ClientType.BIDSTREAM);
18+
}
19+
20+
public RefreshResponse refresh() {
21+
return tokenHelper.refresh("/v2/key/bidstream");
22+
}
23+
24+
RefreshResponse refreshJson(String json) {
25+
return tokenHelper.refreshJson(json);
26+
}
27+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.uid2.client;
2+
3+
enum ClientType {
4+
SHARING,
5+
BIDSTREAM,
6+
LEGACY
7+
}

src/main/java/com/uid2/client/DecryptionResponse.java

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,22 @@ public class DecryptionResponse {
88
private final Instant established;
99
private final Integer siteId;
1010
private final Integer siteKeySiteId;
11+
private final IdentityType identityType;
12+
private final Integer advertisingTokenVersion;
13+
private final boolean isClientSideGenerated;
14+
private final Instant expiry;
1115

12-
DecryptionResponse(DecryptionStatus status, String uid, Instant established, Integer siteId, Integer siteKeySiteId) {
16+
17+
DecryptionResponse(DecryptionStatus status, String uid, Instant established, Integer siteId, Integer siteKeySiteId, IdentityType identityType, Integer advertisingTokenVersion, boolean isClientSideGenerated, Instant expiry) {
1318
this.status = status;
1419
this.uid = uid;
1520
this.established = established;
1621
this.siteId = siteId;
1722
this.siteKeySiteId = siteKeySiteId;
23+
this.identityType = identityType;
24+
this.advertisingTokenVersion = advertisingTokenVersion;
25+
this.isClientSideGenerated = isClientSideGenerated;
26+
this.expiry = expiry;
1827
}
1928

2029
/**
@@ -46,11 +55,27 @@ public Instant getEstablished() {
4655

4756
public Integer getSiteKeySiteId() { return siteKeySiteId; }
4857

58+
public Integer getAdvertisingTokenVersion() {
59+
return advertisingTokenVersion;
60+
}
61+
62+
public IdentityType getIdentityType() {
63+
return identityType;
64+
}
65+
66+
public boolean getIsClientSideGenerated() {
67+
return isClientSideGenerated;
68+
}
69+
70+
public Instant getExpiry() {
71+
return expiry;
72+
}
73+
4974
static DecryptionResponse makeError(DecryptionStatus status) {
50-
return new DecryptionResponse(status, null, Instant.MIN, null, null);
75+
return new DecryptionResponse(status, null, Instant.MIN, null, null, null, null, false, Instant.MIN);
5176
}
5277

53-
static DecryptionResponse makeError(DecryptionStatus status, Instant established, Integer siteId, Integer siteKeySiteId) {
54-
return new DecryptionResponse(status, null, established, siteId, siteKeySiteId);
78+
static DecryptionResponse makeError(DecryptionStatus status, Instant established, Integer siteId, Integer siteKeySiteId, IdentityType identityType, Integer advertisingTokenVersion, boolean isClientSideGenerated, Instant expiry) {
79+
return new DecryptionResponse(status, null, established, siteId, siteKeySiteId, identityType, advertisingTokenVersion, isClientSideGenerated, expiry);
5580
}
5681
}

src/main/java/com/uid2/client/DecryptionStatus.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,8 @@ public enum DecryptionStatus {
4444
* to decrypt a UID2 token. Ensure the factory class matches the token type you are decrypting.
4545
*/
4646
INVALID_IDENTITY_SCOPE,
47+
/**
48+
* INVALID_TOKEN_LIFETIME: The token has invalid timestamps.
49+
*/
50+
INVALID_TOKEN_LIFETIME
4751
}

src/main/java/com/uid2/client/IUID2Client.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
import java.time.Instant;
44

5+
/**
6+
* @deprecated Please use BidstreamClient or SharingClient instead.
7+
*/
8+
@Deprecated
59
public interface IUID2Client {
610

711
/**

src/main/java/com/uid2/client/KeyContainer.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ class KeyContainer {
1313
private int masterKeysetId;
1414
private int defaultKeysetId;
1515
private long tokenExpirySeconds;
16+
private IdentityScope identityScope;
17+
private long maxBidstreamLifetimeSeconds;
18+
private long maxSharingLifetimeSeconds;
19+
private long allowClockSkewSeconds;
1620

1721

1822
KeyContainer(List<Key> keyList)
@@ -34,11 +38,15 @@ class KeyContainer {
3438
}
3539
}
3640

37-
KeyContainer(int callerSiteId, int masterKeysetId, int defaultKeysetId, long tokenExpirySeconds, List<Key> keyList) {
41+
KeyContainer(int callerSiteId, int masterKeysetId, int defaultKeysetId, long tokenExpirySeconds, List<Key> keyList, IdentityScope identityScope, long maxBidstreamLifetimeSeconds, long maxSharingLifetimeSeconds, long allowClockSkewSeconds) {
3842
this.callerSiteId = callerSiteId;
3943
this.masterKeysetId = masterKeysetId;
4044
this.defaultKeysetId = defaultKeysetId;
4145
this.tokenExpirySeconds = tokenExpirySeconds;
46+
this.identityScope = identityScope;
47+
this.maxBidstreamLifetimeSeconds = maxBidstreamLifetimeSeconds;
48+
this.maxSharingLifetimeSeconds = maxSharingLifetimeSeconds;
49+
this.allowClockSkewSeconds = allowClockSkewSeconds;
4250

4351
for (Key key : keyList) {
4452
this.keys.put(key.getId(), key);
@@ -82,12 +90,12 @@ private Key getKeysetActiveKey(int keysetId, Instant now)
8290

8391
private Key getLatestKey(List<Key> keys, Instant now)
8492
{
85-
if(keys == null || keys.isEmpty())
93+
if (keys == null || keys.isEmpty())
8694
return null;
8795
int it = ListHelpers.upperBound(keys, now, (ts, k) -> ts.isBefore(k.getActivates()));
8896
while(it > 0) {
8997
Key key = keys.get(it-1);
90-
if(key.isActive(now)) {
98+
if (key.isActive(now)) {
9199
return key;
92100
}
93101
--it;
@@ -107,4 +115,20 @@ public int getCallerSiteId() {
107115
public long getTokenExpirySeconds() {
108116
return tokenExpirySeconds;
109117
}
118+
119+
long getMaxBidstreamLifetimeSeconds() {
120+
return maxBidstreamLifetimeSeconds;
121+
}
122+
123+
long getMaxSharingLifetimeSeconds() {
124+
return maxSharingLifetimeSeconds;
125+
}
126+
127+
long getAllowClockSkewSeconds() {
128+
return allowClockSkewSeconds;
129+
}
130+
131+
IdentityScope getIdentityScope() {
132+
return identityScope;
133+
}
110134
}

src/main/java/com/uid2/client/KeyParser.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class KeyParser {
1313
static KeyContainer parse(InputStream stream) {
1414
JsonObject json = JsonParser.parseReader(new InputStreamReader(stream)).getAsJsonObject();
1515
JsonElement bodyElement = json.get("body");
16-
if (bodyElement.isJsonArray()) { // key/latest response, which will become legacy
16+
if (bodyElement.isJsonArray()) { // key/latest response, which is now become legacy. We can remove this block once all tests use key/sharing JSON instead
1717
List<Key> keys = new ArrayList<>();
1818
JsonArray body = json.getAsJsonArray("body");
1919
for (JsonElement item : body) {
@@ -34,7 +34,12 @@ static KeyContainer parse(InputStream stream) {
3434
int callerSiteId = getAsInt(body,"caller_site_id");
3535
int masterKeysetId = getAsInt(body,"master_keyset_id");
3636
int defaultKeysetId = getAsInt(body,"default_keyset_id");
37-
long tokenExpirySeconds = getAsLong(body,"token_expiry_seconds");
37+
long maxBidstreamLifetimeSeconds = getAsLongOrDefault(body, "max_bidstream_lifetime_seconds", Long.MAX_VALUE);
38+
long maxSharingLifetimeSeconds = getAsLongOrDefault(body, "max_sharing_lifetime_seconds", Long.MAX_VALUE);
39+
long allowClockSkewSeconds = getAsLongOrDefault(body, "allow_clock_skew_seconds", 1800);
40+
IdentityScope identityScope = getAsIdentityScopeOrDefault(body, "identity_scope", IdentityScope.UID2);
41+
42+
long tokenExpirySeconds = getAsLongOrDefault(body,"token_expiry_seconds", 0);
3843
if (tokenExpirySeconds == 0) {
3944
final short defaultTokenExpiryDays = 30;
4045
tokenExpirySeconds = defaultTokenExpiryDays * 24 * 60 * 60;
@@ -56,7 +61,7 @@ static KeyContainer parse(InputStream stream) {
5661
keys.add(key);
5762
}
5863

59-
return new KeyContainer(callerSiteId, masterKeysetId, defaultKeysetId, tokenExpirySeconds, keys);
64+
return new KeyContainer(callerSiteId, masterKeysetId, defaultKeysetId, tokenExpirySeconds, keys, identityScope, maxBidstreamLifetimeSeconds, maxSharingLifetimeSeconds, allowClockSkewSeconds);
6065
}
6166
}
6267

@@ -65,9 +70,14 @@ static private int getAsInt(JsonObject body, String memberName) {
6570
return isNull(element) ? 0 : element.getAsInt();
6671
}
6772

68-
static private long getAsLong(JsonObject body, String memberName) {
73+
static private long getAsLongOrDefault(JsonObject body, String memberName, long defaultVal) {
74+
JsonElement element = body.get(memberName);
75+
return isNull(element) ? defaultVal : element.getAsLong();
76+
}
77+
78+
static private IdentityScope getAsIdentityScopeOrDefault(JsonObject body, String memberName, IdentityScope defaultVal) {
6979
JsonElement element = body.get(memberName);
70-
return isNull(element) ? 0 : element.getAsLong();
80+
return isNull(element) ? defaultVal : body.get("identity_scope").getAsString().equals("EUID") ? IdentityScope.EUID : IdentityScope.UID2;
7181
}
7282

7383
static private boolean isNull(JsonElement jo) {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.uid2.client;
2+
3+
import java.util.BitSet;
4+
5+
class PrivacyBits {
6+
// Bit 0 is legacy and is no longer in use
7+
private final int bitClientSideGenerated = 1;
8+
9+
private final BitSet bits;
10+
11+
PrivacyBits(int bitsAsInt)
12+
{
13+
bits = new BitSet();
14+
int index = 0;
15+
while (bitsAsInt != 0) {
16+
if ((bitsAsInt & 1) == 1) {
17+
bits.set(index); // Set the corresponding bit to 1
18+
}
19+
bitsAsInt >>= 1; // Right shift to get next bit
20+
index++;
21+
}
22+
}
23+
24+
boolean isClientSideGenerated() {
25+
return bits.get(bitClientSideGenerated);
26+
}
27+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.uid2.client;
2+
3+
public class RefreshResponse {
4+
private RefreshResponse(boolean success, String reason)
5+
{
6+
this.success = success;
7+
this.reason = reason;
8+
}
9+
10+
static RefreshResponse makeSuccess()
11+
{
12+
return new RefreshResponse(true, "");
13+
}
14+
15+
static RefreshResponse makeError(String reason)
16+
{
17+
return new RefreshResponse(false, reason);
18+
}
19+
20+
public boolean isSuccess()
21+
{
22+
return success;
23+
}
24+
25+
public String getReason()
26+
{
27+
return reason;
28+
}
29+
30+
private final boolean success;
31+
private final String reason;
32+
}

0 commit comments

Comments
 (0)