Skip to content

Commit c7f19e8

Browse files
sunnywuaulmemcollins-ttd
authored
Cstg (#22)
* Added DecryptionResponse.IsClientSideGenerated for decrypting tokens generated from Client Side Token Generation way. They are usually filled but can be null if the token decryption fails and we cannot determine the property. * Added DecryptionStatus.UserOptedOut when a Client Side Token Generated token is decrypted and it indicates the user actually has opted out previously so no valid raw UID2 can be used after this decrypt call. * Created the method interface "DecryptionResponse Decrypt(string token, string expectedDomainName)" for domain name check when decrypting UID2 token, to verify if the expectedDomainName param belongs to the UID2 creator's allowed domain name list (which needs to be registered with UID2 admins). If they don't match then it's a domain name check failure. * new DecryptionStatus.DomainNameCheckFailed status to indicate if domain name check failed * Refactored encryption tests to use a builder for advertiser token * Make it a 5.3.0 version --------- Co-authored-by: Sunny Wu <[email protected]> --------- Co-authored-by: Aleksandrs Ulme <[email protected]> Co-authored-by: mcollins-ttd <[email protected]>
1 parent 12ca430 commit c7f19e8

25 files changed

+1210
-527
lines changed

.editorconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
max_line_length=200

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.2.0</version>
5+
<version>5.3.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: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Text;
32
using System.Threading;
43
using UID2.Client;
54

@@ -11,6 +10,7 @@ class Program
1110
static string _authKey;
1211
static string _secretKey;
1312
static string _advertisingToken;
13+
static string _domain;
1414

1515
static void StartExample(string name)
1616
{
@@ -32,11 +32,12 @@ static void ExampleBasicRefresh()
3232
return;
3333
}
3434

35-
var result = client.Decrypt(_advertisingToken);
35+
var result = client.Decrypt(_advertisingToken, _domain);
3636
Console.WriteLine($"DecryptedSuccess={result.Success} Status={result.Status}");
3737
Console.WriteLine($"UID={result.Uid}");
3838
Console.WriteLine($"EstablishedAt={result.Established}");
3939
Console.WriteLine($"SiteId={result.SiteId}");
40+
Console.WriteLine($"IsClientSideGenerated={result.IsClientSideGenerated}");
4041
}
4142

4243
static void ExampleAutoRefresh()
@@ -59,7 +60,7 @@ static void ExampleAutoRefresh()
5960

6061
for (int i = 0; i < 5; ++i)
6162
{
62-
var result = client.Decrypt(_advertisingToken);
63+
var result = client.Decrypt(_advertisingToken, _domain);
6364
Console.WriteLine($"DecryptSuccess={result.Success} Status={result.Status} UID={result.Uid}");
6465
Console.Out.Flush();
6566
Thread.Sleep(TimeSpan.FromSeconds(5));
@@ -106,15 +107,18 @@ static int Main(string[] args)
106107
{
107108
if (args.Length < 4)
108109
{
109-
Console.Error.WriteLine("Usage: test-client <base-url> <auth-key> <secret-key> <ad-token>");
110+
Console.Error.WriteLine("Usage: test-client <base-url> <auth-key> <secret-key> <ad-token> [<domain-name>]");
110111
return 1;
111112
}
112113

113114
_baseUrl = args[0];
114115
_authKey = args[1];
115116
_secretKey = args[2];
116117
_advertisingToken = args[3];
117-
118+
if (args.Length >= 5)
119+
{
120+
_domain = args[4];
121+
}
118122

119123
ExampleBasicRefresh();
120124
ExampleAutoRefresh();

src/UID2.Client/DecryptionResponse.cs

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,30 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Text;
42

53
namespace UID2.Client
64
{
7-
public struct DecryptionResponse
5+
public readonly struct DecryptionResponse
86
{
9-
private readonly DecryptionStatus _status;
10-
private readonly string _uid;
11-
private readonly DateTime? _established;
12-
private readonly int? _siteId;
13-
private readonly int? _siteKeySiteId;
14-
15-
public DecryptionResponse(DecryptionStatus status, string uid, DateTime? established, int? siteId, int? siteKeySiteId)
7+
public DecryptionResponse(DecryptionStatus status, string uid, DateTime? established, int? siteId, int? siteKeySiteId, bool? isClientSideGenerated = false)
168
{
17-
_status = status;
18-
_uid = uid;
19-
_established = established;
20-
_siteId = siteId;
21-
_siteKeySiteId = siteKeySiteId;
9+
Status = status;
10+
Uid = uid;
11+
Established = established;
12+
SiteId = siteId;
13+
SiteKeySiteId = siteKeySiteId;
14+
IsClientSideGenerated = isClientSideGenerated;
2215
}
2316

2417
public static DecryptionResponse MakeError(DecryptionStatus status)
2518
{
26-
return new DecryptionResponse(status, null, null, null, null);
19+
return new DecryptionResponse(status, null, null, null, null, null);
2720
}
2821

29-
public bool Success => _status == DecryptionStatus.Success;
30-
public DecryptionStatus Status => _status;
31-
public string Uid => _uid;
32-
public DateTime? Established => _established;
33-
public int? SiteId => _siteId;
34-
public int? SiteKeySiteId => _siteKeySiteId;
22+
public bool Success => Status == DecryptionStatus.Success;
23+
public DecryptionStatus Status { get; }
24+
public string Uid { get; }
25+
public DateTime? Established { get; }
26+
public int? SiteId { get; }
27+
public int? SiteKeySiteId { get; }
28+
public bool? IsClientSideGenerated { get; }
3529
}
36-
}
30+
}

src/UID2.Client/DecryptionStatus.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,10 @@ public enum DecryptionStatus
1212
VersionNotSupported,
1313
InvalidPayloadType,
1414
InvalidIdentityScope,
15+
/// <summary>
16+
/// DSPs are still expected to check their records for user opt out, even when this status is not returned
17+
/// </summary>
18+
UserOptedOut,
19+
DomainNameCheckFailed
1520
}
1621
}

src/UID2.Client/IUID2Client.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ public interface IUID2Client
3535
/// <returns>Response showing if decryption is successful and the resulting UID if successful.
3636
/// Or it could return error codes/string indicating what went wrong
3737
/// </returns>
38-
[Obsolete("Please use Decrypt(string token) instead.")]
38+
[Obsolete("Please use Decrypt(string token) instead.")]
3939
DecryptionResponse Decrypt(string token, DateTime utcNow);
4040
DecryptionResponse Decrypt(string token);
41+
DecryptionResponse Decrypt(string token, string expectedDomainName);
4142

4243
EncryptionDataResponse Encrypt(string rawUid);
4344
[Obsolete("Please use Encrypt(string rawUid) instead.")]

src/UID2.Client/KeyContainer.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using UID2.Client.Utils;
45

56
namespace UID2.Client
@@ -12,6 +13,9 @@ internal class KeyContainer
1213
private readonly Dictionary<int, List<Key>> _keysBySite = new Dictionary<int, List<Key>>(); //for legacy /key/latest
1314

1415
private readonly Dictionary<int, List<Key>> _keysByKeyset = new Dictionary<int, List<Key>>();
16+
17+
private readonly Dictionary<int, Site> _siteIdToSite = new Dictionary<int, Site>();
18+
1519
private readonly int _callerSiteId;
1620
private readonly int _masterKeysetId;
1721
private readonly int _defaultKeysetId;
@@ -45,7 +49,7 @@ internal KeyContainer(List<Key> keys)
4549
}
4650
}
4751

48-
internal KeyContainer(int callerSiteId, int masterKeysetId, int defaultKeysetId, long tokenExpirySeconds, List<Key> keys)
52+
internal KeyContainer(int callerSiteId, int masterKeysetId, int defaultKeysetId, long tokenExpirySeconds, List<Key> keys, IEnumerable<Site> sites)
4953
{ //key/sharing
5054
_callerSiteId = callerSiteId;
5155
_masterKeysetId = masterKeysetId;
@@ -77,6 +81,8 @@ internal KeyContainer(int callerSiteId, int masterKeysetId, int defaultKeysetId,
7781
{
7882
kv.Value.Sort((Key a, Key b) => a.Activates.CompareTo(b.Activates));
7983
}
84+
85+
this._siteIdToSite = sites.ToDictionary(site => site.Id, site => site);
8086
}
8187

8288
public bool IsValid(DateTime asOf)
@@ -104,6 +110,16 @@ public bool TryGetMasterKey(DateTime now, out Key key)
104110
return TryGetKeysetActiveKey(_masterKeysetId, now, out key);
105111
}
106112

113+
public bool IsDomainNameAllowedForSite(int siteId, string domainName)
114+
{
115+
if (domainName == null)
116+
{
117+
return false;
118+
}
119+
120+
return this._siteIdToSite.TryGetValue(siteId, out var site) && site.AllowDomainName(domainName);
121+
}
122+
107123
private bool TryGetKeysetActiveKey(int keysetId, DateTime now, out Key key)
108124
{
109125
if (!_keysByKeyset.TryGetValue(keysetId, out var keyset) || keyset.Count == 0)

src/UID2.Client/KeyParser.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,32 @@ internal static KeyContainer Parse(JObject json)
6161
Convert.FromBase64String(item.Value<string>("secret"))
6262
)).ToList();
6363

64-
return new KeyContainer(callerSiteId, masterKeysetId, defaultKeysetId, tokenExpirySeconds, keys);
64+
var sites = Enumerable.Empty<Site>();
65+
if (TryGetSitesJson(body, out var sitesJson))
66+
{
67+
sites = sitesJson.Select(SiteFromJson).ToList();
68+
}
69+
70+
return new KeyContainer(callerSiteId, masterKeysetId, defaultKeysetId, tokenExpirySeconds, keys, sites);
71+
}
72+
}
6573

74+
private static bool TryGetSitesJson(JObject obj, out JArray value)
75+
{
76+
if (obj.TryGetValue("site_data", StringComparison.OrdinalIgnoreCase, out var sites) && sites.Type == JTokenType.Array)
77+
{
78+
value = (JArray)sites;
79+
return true;
6680
}
81+
82+
value = default;
83+
return false;
84+
}
85+
86+
private static Site SiteFromJson(JToken item)
87+
{
88+
var domainNames = (JArray)item["domain_names"];
89+
return new Site(item.Value<int>("id"), domainNames.Select(x => (string)x));
6790
}
6891
}
6992
}

src/UID2.Client/PrivacyBits.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Collections;
2+
3+
namespace UID2.Client
4+
{
5+
internal class PrivacyBits
6+
{
7+
// Bit 0 is legacy and is no longer in use
8+
private const int BitClientSideGenerated = 1;
9+
private const int BitOptedOut = 2;
10+
11+
private readonly BitArray _bits;
12+
13+
public PrivacyBits(int bitsAsInt)
14+
{
15+
_bits = new BitArray(new [] {bitsAsInt});
16+
}
17+
18+
public bool IsClientSideGenerated => _bits.Get(BitClientSideGenerated);
19+
20+
public bool IsOptedOut => _bits.Get(BitOptedOut);
21+
}
22+
}

src/UID2.Client/Site.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace UID2.Client
5+
{
6+
internal class Site
7+
{
8+
private readonly HashSet<string> _domainNames;
9+
10+
public Site(int id, IEnumerable<string> domainNames)
11+
{
12+
Id = id;
13+
_domainNames = new HashSet<string>(domainNames, StringComparer.OrdinalIgnoreCase);
14+
}
15+
16+
public int Id { get; }
17+
18+
public bool AllowDomainName(string domainName) => _domainNames.Contains(domainName);
19+
}
20+
}

0 commit comments

Comments
 (0)