Skip to content

Commit 9441e75

Browse files
authored
Merge pull request #21 from IABTechLab/gdm-UID2-1526-add-cstg-to-decryptionresponse
Added v3/v4 identity type and advertising token version fields in DecryptionResponse
2 parents 4e68f23 + 30530ed commit 9441e75

File tree

12 files changed

+132
-55
lines changed

12 files changed

+132
-55
lines changed

UID2.Client.nuspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<package>
33
<metadata>
44
<id>UID2.Client</id>
5-
<version>5.3.2</version>
5+
<version>5.4.0</version>
66
<title>UID2 Client C# SDK</title>
77
<authors>UID2 team</authors>
88
<owners>UID2 team</owners>

src/SampleApp/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ static void ExampleBasicRefresh()
3737
Console.WriteLine($"UID={result.Uid}");
3838
Console.WriteLine($"EstablishedAt={result.Established}");
3939
Console.WriteLine($"SiteId={result.SiteId}");
40+
Console.WriteLine($"IdentityType={result.IdentityType}");
41+
Console.WriteLine($"AdvertisingTokenVersion={result.AdvertisingTokenVersion}");
4042
Console.WriteLine($"IsClientSideGenerated={result.IsClientSideGenerated}");
4143
}
4244

src/UID2.Client/DecryptionResponse.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,22 @@ namespace UID2.Client
44
{
55
public readonly struct DecryptionResponse
66
{
7-
public DecryptionResponse(DecryptionStatus status, string uid, DateTime? established, int? siteId, int? siteKeySiteId, bool? isClientSideGenerated = false)
7+
public DecryptionResponse(DecryptionStatus status, string uid, DateTime? established, int? siteId, int? siteKeySiteId, IdentityType? identityType, int? advertisingTokenVersion,
8+
bool? isClientSideGenerated = false)
89
{
910
Status = status;
1011
Uid = uid;
1112
Established = established;
1213
SiteId = siteId;
1314
SiteKeySiteId = siteKeySiteId;
15+
IdentityType = identityType;
16+
AdvertisingTokenVersion = advertisingTokenVersion;
1417
IsClientSideGenerated = isClientSideGenerated;
1518
}
1619

1720
public static DecryptionResponse MakeError(DecryptionStatus status)
1821
{
19-
return new DecryptionResponse(status, null, null, null, null, null);
22+
return new DecryptionResponse(status, null, null, null, null, null, null, null);
2023
}
2124

2225
public bool Success => Status == DecryptionStatus.Success;
@@ -25,6 +28,8 @@ public static DecryptionResponse MakeError(DecryptionStatus status)
2528
public DateTime? Established { get; }
2629
public int? SiteId { get; }
2730
public int? SiteKeySiteId { get; }
31+
public IdentityType? IdentityType { get; }
32+
public int? AdvertisingTokenVersion { get; }
2833
public bool? IsClientSideGenerated { get; }
2934
}
3035
}

src/UID2.Client/IdentityType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
namespace UID2.Client
22
{
3-
internal enum IdentityType
3+
public enum IdentityType
44
{
55
Email = 0,
66
Phone = 1,

src/UID2.Client/UID2Client.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public async Task<RefreshResponse> RefreshAsync(CancellationToken token)
134134

135135
private string GetAssemblyNameAndVersion()
136136
{
137-
var version = "5.3.2";
137+
var version = "5.4.0";
138138
return "uid-client-net-" + version;
139139
}
140140

src/UID2.Client/UID2Encryption.cs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ internal static DecryptionResponse Decrypt(string token, KeyContainer keys, Date
3333

3434
if (data[1] == (int)AdvertisingTokenVersion.V3)
3535
{
36-
return DecryptV3(Convert.FromBase64String(token), keys, now, domainName, identityScope, enableDomainNameCheck);
36+
return DecryptV3(Convert.FromBase64String(token), keys, now, identityScope, 3, domainName, enableDomainNameCheck);
3737
}
3838

3939
if (data[1] == (int)AdvertisingTokenVersion.V4)
4040
{
4141
//same as V3 but use Base64URL encoding
42-
return DecryptV3(UID2Base64UrlCoder.Decode(token), keys, now, domainName, identityScope, enableDomainNameCheck);
42+
return DecryptV3(UID2Base64UrlCoder.Decode(token), keys, now, identityScope, 4, domainName, enableDomainNameCheck);
4343
}
4444

4545
return DecryptionResponse.MakeError(DecryptionStatus.VersionNotSupported);
@@ -93,26 +93,26 @@ private static DecryptionResponse DecryptV2(byte[] encryptedId, KeyContainer key
9393
var expiry = DateTimeUtils.FromEpochMilliseconds(expiresMilliseconds);
9494
if (expiry < now)
9595
{
96-
return new DecryptionResponse(DecryptionStatus.ExpiredToken, null, established, siteId, siteKey.SiteId, privacyBits.IsClientSideGenerated);
96+
return new DecryptionResponse(DecryptionStatus.ExpiredToken, null, established, siteId, siteKey.SiteId, null, 2, privacyBits.IsClientSideGenerated);
9797
}
9898

9999
if (privacyBits.IsOptedOut)
100100
{
101-
return new DecryptionResponse(DecryptionStatus.UserOptedOut, null, established, siteId, siteKey.SiteId, privacyBits.IsClientSideGenerated);
101+
return new DecryptionResponse(DecryptionStatus.UserOptedOut, null, established, siteId, siteKey.SiteId, null, 2, privacyBits.IsClientSideGenerated);
102102
}
103103

104104
if (enableDomainNameCheck && !IsDomainNameAllowedForSite(privacyBits, siteId, domainName, keys))
105105
{
106-
return new DecryptionResponse(DecryptionStatus.DomainNameCheckFailed, null, established, siteId, siteKey.SiteId, privacyBits.IsClientSideGenerated);
106+
return new DecryptionResponse(DecryptionStatus.DomainNameCheckFailed, null, established, siteId, siteKey.SiteId, null, 2, privacyBits.IsClientSideGenerated);
107107
}
108108

109-
return new DecryptionResponse(DecryptionStatus.Success, idString, established, siteId, siteKey.SiteId, privacyBits.IsClientSideGenerated);
109+
return new DecryptionResponse(DecryptionStatus.Success, idString, established, siteId, siteKey.SiteId, null, 2, privacyBits.IsClientSideGenerated);
110110
}
111111

112-
private static DecryptionResponse DecryptV3(byte[] encryptedId, KeyContainer keys, DateTime now,
113-
string domainName,
114-
IdentityScope identityScope, bool enableDomainNameCheck)
112+
private static DecryptionResponse DecryptV3(byte[] encryptedId, KeyContainer keys, DateTime now, IdentityScope identityScope, int advertisingTokenVersion, string domainName, bool enableDomainNameCheck)
115113
{
114+
IdentityType identityType = GetIdentityType(encryptedId);
115+
116116
var reader = new BigEndianByteReader(new MemoryStream(encryptedId));
117117

118118
var prefix = reader.ReadByte();
@@ -170,20 +170,21 @@ private static DecryptionResponse DecryptV3(byte[] encryptedId, KeyContainer key
170170
var expiry = DateTimeUtils.FromEpochMilliseconds(expiresMilliseconds);
171171
if (expiry < now)
172172
{
173-
return new DecryptionResponse(DecryptionStatus.ExpiredToken, null, established, siteId, siteKey.SiteId, privacyBits.IsClientSideGenerated);
173+
return new DecryptionResponse(DecryptionStatus.ExpiredToken, null, established, siteId, siteKey.SiteId, identityType, advertisingTokenVersion, privacyBits.IsClientSideGenerated);
174174
}
175175

176176
if (privacyBits.IsOptedOut)
177177
{
178-
return new DecryptionResponse(DecryptionStatus.UserOptedOut, null, established, siteId, siteKey.SiteId, privacyBits.IsClientSideGenerated);
178+
return new DecryptionResponse(DecryptionStatus.UserOptedOut, null, established, siteId, siteKey.SiteId, identityType, advertisingTokenVersion, privacyBits.IsClientSideGenerated);
179179
}
180180

181181
if (enableDomainNameCheck && !IsDomainNameAllowedForSite(privacyBits, siteId, domainName, keys))
182182
{
183-
return new DecryptionResponse(DecryptionStatus.DomainNameCheckFailed, null, established, siteId, siteKey.SiteId, privacyBits.IsClientSideGenerated);
183+
return new DecryptionResponse(DecryptionStatus.DomainNameCheckFailed, null, established, siteId, siteKey.SiteId, identityType, advertisingTokenVersion,
184+
privacyBits.IsClientSideGenerated);
184185
}
185186

186-
return new DecryptionResponse(DecryptionStatus.Success, idString, established, siteId, siteKey.SiteId, privacyBits.IsClientSideGenerated);
187+
return new DecryptionResponse(DecryptionStatus.Success, idString, established, siteId, siteKey.SiteId, identityType, advertisingTokenVersion, privacyBits.IsClientSideGenerated);
187188
}
188189

189190
private static bool IsDomainNameAllowedForSite(PrivacyBits privacyBits, int siteId, string domainName, KeyContainer keys)
@@ -468,5 +469,17 @@ private static IdentityScope DecodeIdentityScopeV3(byte value)
468469
{
469470
return (IdentityScope)((value >> 4) & 1);
470471
}
472+
473+
private static IdentityType GetIdentityType(byte[] encryptedId)
474+
{
475+
// For specifics about the bitwise logic, check:
476+
// Confluence - UID2-79 UID2 Token v3/v4 and Raw UID2 format v3
477+
// In the base64-encoded version of encryptedId, the first character is always either A/B/E/F.
478+
// After converting to binary and performing the AND operation against 1100,the result is always 0X00.
479+
// So just bitshift right twice to get 000X, which results in either 0 or 1.
480+
byte idType = encryptedId[0];
481+
byte piiType = (byte)((idType & 0b_1100) >> 2);
482+
return piiType == 0 ? IdentityType.Email : IdentityType.Phone;
483+
}
471484
}
472485
}

test/UID2.Client.Test/EncryptionTestsV2.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ public void SmokeTest()
1818
{
1919
var refreshResult = _client.RefreshJson(KeySetToJson(MASTER_KEY, SITE_KEY));
2020
Assert.True(refreshResult.Success);
21+
2122
var res = _client.Decrypt(_tokenBuilder.Build(), NOW);
2223
Assert.True(res.Success);
23-
Assert.Equal(EXAMPLE_UID, res.Uid);
24+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
25+
Assert.Null(res.IdentityType);
26+
Assert.Equal(2, res.AdvertisingTokenVersion);
2427
}
2528

2629
[Fact]
@@ -48,7 +51,7 @@ public void TokenIsCstgDerivedTest(string domainName)
4851
Assert.True(res.IsClientSideGenerated);
4952
Assert.True(res.Success);
5053
Assert.Equal(DecryptionStatus.Success, res.Status);
51-
Assert.Equal(EXAMPLE_UID, res.Uid);
54+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
5255
}
5356

5457
[Theory]
@@ -86,7 +89,7 @@ public void TokenIsCstgDerivedNoDomainNameTest()
8689
Assert.True(res.IsClientSideGenerated);
8790
Assert.True(res.Success);
8891
Assert.Equal(DecryptionStatus.Success, res.Status);
89-
Assert.Equal(EXAMPLE_UID, res.Uid);
92+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
9093
}
9194

9295
[Theory]
@@ -104,7 +107,7 @@ public void TokenIsNotCstgDerivedDomainNameSuccessTest(string domainName)
104107
Assert.False(res.IsClientSideGenerated);
105108
Assert.True(res.Success);
106109
Assert.Equal(DecryptionStatus.Success, res.Status);
107-
Assert.Equal(EXAMPLE_UID, res.Uid);
110+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
108111
}
109112

110113
[Fact]
@@ -156,13 +159,13 @@ public void TokenExpiryAndCustomNow()
156159
var expiry = NOW.AddDays(-60);
157160

158161
_client.RefreshJson(KeySetToJson(MASTER_KEY, SITE_KEY));
159-
var advertisingToken = _tokenBuilder.WithExpiry(expiry).Build();;
162+
var advertisingToken = _tokenBuilder.WithExpiry(expiry).Build();
160163

161164
var res = _client.Decrypt(advertisingToken, expiry.AddSeconds(1));
162165
Assert.Equal(DecryptionStatus.ExpiredToken, res.Status);
163166

164167
res = _client.Decrypt(advertisingToken, expiry.AddSeconds(-1));
165-
Assert.Equal(EXAMPLE_UID, res.Uid);
168+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
166169
}
167170

168171
[Fact]

test/UID2.Client.Test/EncryptionTestsV3.cs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,38 @@ public class EncryptionTestsV3
1212
private readonly UID2Client _client = new("endpoint", "authkey", CLIENT_SECRET, IdentityScope.UID2);
1313
private readonly AdvertisingTokenBuilder _tokenBuilder = AdvertisingTokenBuilder.Builder().WithVersion(AdvertisingTokenBuilder.TokenVersion.V3);
1414

15+
[Theory]
16+
[InlineData(EXAMPLE_EMAIL_RAW_UID2_V2, nameof(IdentityScope.UID2), IdentityType.Email)]
17+
[InlineData(EXAMPLE_PHONE_RAW_UID2_V3, nameof(IdentityScope.UID2), IdentityType.Phone)]
18+
[InlineData(EXAMPLE_EMAIL_RAW_UID2_V2, nameof(IdentityScope.EUID), IdentityType.Email)]
19+
[InlineData(EXAMPLE_PHONE_RAW_UID2_V3, nameof(IdentityScope.EUID), IdentityType.Phone)]
20+
public void IdentityScopeAndType_TestCases(String uid, string identityScope, IdentityType? identityType)
21+
{
22+
var client = new UID2Client("ep", "ak", CLIENT_SECRET, Enum.Parse<IdentityScope>(identityScope));
23+
var refreshResult = client.RefreshJson(KeySetToJson(MASTER_KEY, SITE_KEY));
24+
Assert.True(refreshResult.Success);
25+
26+
string advertisingToken = identityScope == "UID2"
27+
? UID2TokenGenerator.GenerateUid2TokenV3(uid, MASTER_KEY, SITE_ID, SITE_KEY, UID2TokenGenerator.DefaultParams)
28+
: UID2TokenGenerator.GenerateEuidTokenV3(uid, MASTER_KEY, SITE_ID, SITE_KEY);
29+
var res = client.Decrypt(advertisingToken, NOW);
30+
Assert.True(res.Success);
31+
Assert.Equal(uid, res.Uid);
32+
Assert.Equal(identityType, res.IdentityType);
33+
Assert.Equal(3, res.AdvertisingTokenVersion);
34+
}
35+
1536
[Fact]
1637
public void SmokeTest()
1738
{
1839
var refreshResult = _client.RefreshJson(KeySetToJson(MASTER_KEY, SITE_KEY));
1940
Assert.True(refreshResult.Success);
41+
2042
var res = _client.Decrypt(_tokenBuilder.Build(), NOW);
2143
Assert.True(res.Success);
22-
Assert.Equal(EXAMPLE_UID, res.Uid);
44+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
45+
Assert.Equal(IdentityType.Email, res.IdentityType);
46+
Assert.Equal(3, res.AdvertisingTokenVersion);
2347
}
2448

2549
[Fact]
@@ -47,7 +71,7 @@ public void TokenIsCstgDerivedTest(string domainName)
4771
Assert.True(res.IsClientSideGenerated);
4872
Assert.True(res.Success);
4973
Assert.Equal(DecryptionStatus.Success, res.Status);
50-
Assert.Equal(EXAMPLE_UID, res.Uid);
74+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
5175
}
5276

5377
[Theory]
@@ -85,7 +109,7 @@ public void TokenIsCstgDerivedNoDomainNameTest()
85109
Assert.True(res.IsClientSideGenerated);
86110
Assert.True(res.Success);
87111
Assert.Equal(DecryptionStatus.Success, res.Status);
88-
Assert.Equal(EXAMPLE_UID, res.Uid);
112+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
89113
}
90114

91115
[Theory]
@@ -103,7 +127,7 @@ public void TokenIsNotCstgDerivedDomainNameSuccessTest(string domainName)
103127
Assert.False(res.IsClientSideGenerated);
104128
Assert.True(res.Success);
105129
Assert.Equal(DecryptionStatus.Success, res.Status);
106-
Assert.Equal(EXAMPLE_UID, res.Uid);
130+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
107131
}
108132

109133
[Fact]
@@ -161,7 +185,7 @@ public void TokenExpiryAndCustomNow()
161185
Assert.Equal(DecryptionStatus.ExpiredToken, res.Status);
162186

163187
res = _client.Decrypt(advertisingToken, expiry.AddSeconds(-1));
164-
Assert.Equal(EXAMPLE_UID, res.Uid);
188+
Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid);
165189
}
166190

167191
[Fact]

0 commit comments

Comments
 (0)