diff --git a/README.md b/README.md index c5ddd77..ecf59e4 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ dotnet test To run the sample app: ``` -dotnet run --project src/SampleApp/SampleApp.csproj https://integ.uidapi.com \ +dotnet run --project src/SampleApp/SampleApp.csproj https://operator-integ.uidapi.com \ ``` diff --git a/src/UID2.Client/BidstreamClient.cs b/src/UID2.Client/BidstreamClient.cs index 1018a82..6c44378 100644 --- a/src/UID2.Client/BidstreamClient.cs +++ b/src/UID2.Client/BidstreamClient.cs @@ -12,14 +12,14 @@ public BidstreamClient(string endpoint, string authKey, string secretKey) _tokenHelper = new TokenHelper(endpoint, authKey, secretKey); } - public DecryptionResponse DecryptTokenIntoRawUid(string token, string domainNameFromBidRequest) + public DecryptionResponse DecryptTokenIntoRawUid(string token, string domainOrAppNameFromBidRequest) { - return DecryptTokenIntoRawUid(token, domainNameFromBidRequest, DateTime.UtcNow); + return DecryptTokenIntoRawUid(token, domainOrAppNameFromBidRequest, DateTime.UtcNow); } - internal DecryptionResponse DecryptTokenIntoRawUid(string token, string domainNameFromBidRequest, DateTime utcNow) + internal DecryptionResponse DecryptTokenIntoRawUid(string token, string domainOrAppNameFromBidRequest, DateTime utcNow) { - return _tokenHelper.Decrypt(token, utcNow, domainNameFromBidRequest, ClientType.Bidstream); + return _tokenHelper.Decrypt(token, utcNow, domainOrAppNameFromBidRequest, ClientType.Bidstream); } diff --git a/src/UID2.Client/DecryptionStatus.cs b/src/UID2.Client/DecryptionStatus.cs index 00e4ec3..089050b 100644 --- a/src/UID2.Client/DecryptionStatus.cs +++ b/src/UID2.Client/DecryptionStatus.cs @@ -16,7 +16,7 @@ public enum DecryptionStatus /// DSPs are still expected to check their records for user opt out, even when this status is not returned /// UserOptedOut, - DomainNameCheckFailed, + DomainOrAppNameCheckFailed, InvalidTokenLifetime } } diff --git a/src/UID2.Client/IUID2Client.cs b/src/UID2.Client/IUID2Client.cs index 722f060..abf26f0 100644 --- a/src/UID2.Client/IUID2Client.cs +++ b/src/UID2.Client/IUID2Client.cs @@ -40,17 +40,17 @@ public interface IUID2Client DecryptionResponse Decrypt(string token, DateTime utcNow); DecryptionResponse Decrypt(string token); /// - /// Decrypt advertising token to extract UID2 details and does a domain name check with the provided domainNameFromBidRequest param + /// Decrypt advertising token to extract UID2 details and does a domain or app name check with the provided domainOrAppNameFromBidRequest param /// for tokens from Client Side Token Generation /// /// The UID2 Token - /// The domain name from bid request which should match the domain name of the publisher (registered with UID2 admin) + /// The domain or app name from bid request which should match the domain or app name of the publisher (registered with UID2 admin) /// generating this token previously using Client Side Token Generation /// /// Response showing if decryption is successful and the resulting UID if successful. - /// Or it could return error codes/string indicating what went wrong (such as DecryptionStatus.DomainNameCheckFailed) + /// Or it could return error codes/string indicating what went wrong (such as DecryptionStatus.DomainOrAppNameCheckFailed) /// - DecryptionResponse Decrypt(string token, string domainNameFromBidRequest); + DecryptionResponse Decrypt(string token, string domainOrAppNameFromBidRequest); EncryptionDataResponse Encrypt(string rawUid); [Obsolete("Please use Encrypt(string rawUid) instead.")] diff --git a/src/UID2.Client/KeyContainer.cs b/src/UID2.Client/KeyContainer.cs index 69865ce..5271eca 100644 --- a/src/UID2.Client/KeyContainer.cs +++ b/src/UID2.Client/KeyContainer.cs @@ -114,14 +114,14 @@ public bool TryGetMasterKey(DateTime now, out Key key) return TryGetKeysetActiveKey(_masterKeysetId, now, out key); } - public bool IsDomainNameAllowedForSite(int siteId, string domainName) + public bool IsDomainOrAppNameAllowedForSite(int siteId, string domainOrAppName) { - if (domainName == null) + if (domainOrAppName == null) { return false; } - return this._siteIdToSite.TryGetValue(siteId, out var site) && site.AllowDomainName(domainName); + return this._siteIdToSite.TryGetValue(siteId, out var site) && site.AllowDomainName(domainOrAppName); } private bool TryGetKeysetActiveKey(int keysetId, DateTime now, out Key key) diff --git a/src/UID2.Client/TokenHelper.cs b/src/UID2.Client/TokenHelper.cs index ee90620..9977c09 100644 --- a/src/UID2.Client/TokenHelper.cs +++ b/src/UID2.Client/TokenHelper.cs @@ -15,7 +15,7 @@ internal TokenHelper(string endpoint, string authKey, string secretKey) _uid2ClientHelper = new Uid2ClientHelper(endpoint, authKey, secretKey); } - internal DecryptionResponse Decrypt(string token, DateTime now, string domainNameFromBidRequest, ClientType clientType) + internal DecryptionResponse Decrypt(string token, DateTime now, string domainOrAppNameFromBidRequest, ClientType clientType) { var container = Volatile.Read(ref _container); if (container == null) @@ -30,7 +30,7 @@ internal DecryptionResponse Decrypt(string token, DateTime now, string domainNam try { - return UID2Encryption.Decrypt(token, container, now, domainNameFromBidRequest, container.IdentityScope, clientType); + return UID2Encryption.Decrypt(token, container, now, domainOrAppNameFromBidRequest, container.IdentityScope, clientType); } catch (Exception) { diff --git a/src/UID2.Client/UID2Client.cs b/src/UID2.Client/UID2Client.cs index 2e064f7..f09fc98 100644 --- a/src/UID2.Client/UID2Client.cs +++ b/src/UID2.Client/UID2Client.cs @@ -34,20 +34,20 @@ public UID2Client(string endpoint, string authKey, string secretKey, IdentitySco public DecryptionResponse Decrypt(string token) { - return Decrypt(token, DateTime.UtcNow, null, ClientType.LegacyWithoutDomainCheck); + return Decrypt(token, DateTime.UtcNow, null, ClientType.LegacyWithoutDomainOrAppNameCheck); } public DecryptionResponse Decrypt(string token, DateTime utcNow) { - return Decrypt(token, utcNow, null, ClientType.LegacyWithoutDomainCheck); + return Decrypt(token, utcNow, null, ClientType.LegacyWithoutDomainOrAppNameCheck); } - public DecryptionResponse Decrypt(string token, string domainNameFromBidRequest) + public DecryptionResponse Decrypt(string token, string domainOrAppNameFromBidRequest) { - return Decrypt(token, DateTime.UtcNow, domainNameFromBidRequest, ClientType.LegacyWithDomainCheck); + return Decrypt(token, DateTime.UtcNow, domainOrAppNameFromBidRequest, ClientType.LegacyWithDomainOrAppNameCheck); } - private DecryptionResponse Decrypt(string token, DateTime now, string domainNameFromBidRequest, ClientType clientType) + private DecryptionResponse Decrypt(string token, DateTime now, string domainOrAppNameFromBidRequest, ClientType clientType) { var container = Volatile.Read(ref _container); if (container == null) @@ -62,7 +62,7 @@ private DecryptionResponse Decrypt(string token, DateTime now, string domainName try { - return UID2Encryption.Decrypt(token, container, now, domainNameFromBidRequest, _identityScope, clientType); + return UID2Encryption.Decrypt(token, container, now, domainOrAppNameFromBidRequest, _identityScope, clientType); } catch (Exception) { diff --git a/src/UID2.Client/UID2Encryption.cs b/src/UID2.Client/UID2Encryption.cs index 03fa35d..673ffe9 100644 --- a/src/UID2.Client/UID2Encryption.cs +++ b/src/UID2.Client/UID2Encryption.cs @@ -13,8 +13,8 @@ internal enum ClientType { Sharing, Bidstream, - LegacyWithoutDomainCheck, - LegacyWithDomainCheck + LegacyWithoutDomainOrAppNameCheck, + LegacyWithDomainOrAppNameCheck } internal static class UID2Encryption @@ -26,7 +26,7 @@ internal static class UID2Encryption private static char[] BASE64_URL_SPECIAL_CHARS = { '-', '_' }; - internal static DecryptionResponse Decrypt(string token, KeyContainer keys, DateTime now, string domainName, IdentityScope identityScope, ClientType clientType) + internal static DecryptionResponse Decrypt(string token, KeyContainer keys, DateTime now, string domainOrAppName, IdentityScope identityScope, ClientType clientType) { if (token.Length < 4) { @@ -39,24 +39,24 @@ internal static DecryptionResponse Decrypt(string token, KeyContainer keys, Date if (data[0] == 2) { - return DecryptV2(Convert.FromBase64String(token), keys, now, domainName, clientType); + return DecryptV2(Convert.FromBase64String(token), keys, now, domainOrAppName, clientType); } if (data[1] == (int)AdvertisingTokenVersion.V3) { - return DecryptV3(Convert.FromBase64String(token), keys, now, identityScope, 3, domainName, clientType); + return DecryptV3(Convert.FromBase64String(token), keys, now, identityScope, 3, domainOrAppName, clientType); } if (data[1] == (int)AdvertisingTokenVersion.V4) { //same as V3 but use Base64URL encoding - return DecryptV3(UID2Base64UrlCoder.Decode(token), keys, now, identityScope, 4, domainName, clientType); + return DecryptV3(UID2Base64UrlCoder.Decode(token), keys, now, identityScope, 4, domainOrAppName, clientType); } return DecryptionResponse.MakeError(DecryptionStatus.VersionNotSupported); } - private static DecryptionResponse DecryptV2(byte[] encryptedId, KeyContainer keys, DateTime now, string domainName, ClientType clientType) + private static DecryptionResponse DecryptV2(byte[] encryptedId, KeyContainer keys, DateTime now, string domainOrAppName, ClientType clientType) { if (encryptedId.Length != TOKEN_V2_LENGTH) { @@ -118,9 +118,9 @@ private static DecryptionResponse DecryptV2(byte[] encryptedId, KeyContainer key return new DecryptionResponse(DecryptionStatus.UserOptedOut, null, established, siteId, siteKey.SiteId, null, advertisingTokenVersion, privacyBits.IsClientSideGenerated, expiry); } - if (!IsDomainNameAllowedForSite(clientType, privacyBits, siteId, domainName, keys)) + if (!IsDomainOrAppNameAllowedForSite(clientType, privacyBits, siteId, domainOrAppName, keys)) { - return new DecryptionResponse(DecryptionStatus.DomainNameCheckFailed, null, established, siteId, siteKey.SiteId, null, advertisingTokenVersion, privacyBits.IsClientSideGenerated, expiry); + return new DecryptionResponse(DecryptionStatus.DomainOrAppNameCheckFailed, null, established, siteId, siteKey.SiteId, null, advertisingTokenVersion, privacyBits.IsClientSideGenerated, expiry); } if (!DoesTokenHaveValidLifetime(clientType, keys, now, expiry, now)) @@ -129,7 +129,7 @@ private static DecryptionResponse DecryptV2(byte[] encryptedId, KeyContainer key return new DecryptionResponse(DecryptionStatus.Success, idString, established, siteId, siteKey.SiteId, null, advertisingTokenVersion, privacyBits.IsClientSideGenerated, expiry); } - private static DecryptionResponse DecryptV3(byte[] encryptedId, KeyContainer keys, DateTime now, IdentityScope identityScope, int advertisingTokenVersion, string domainName, ClientType clientType) + private static DecryptionResponse DecryptV3(byte[] encryptedId, KeyContainer keys, DateTime now, IdentityScope identityScope, int advertisingTokenVersion, string domainOrAppName, ClientType clientType) { if (encryptedId.Length < TOKEN_V3_MIN_LENGTH) { @@ -203,9 +203,9 @@ private static DecryptionResponse DecryptV3(byte[] encryptedId, KeyContainer key return new DecryptionResponse(DecryptionStatus.UserOptedOut, null, established, siteId, siteKey.SiteId, identityType, advertisingTokenVersion, privacyBits.IsClientSideGenerated, expiry); } - if (!IsDomainNameAllowedForSite(clientType, privacyBits, siteId, domainName, keys)) + if (!IsDomainOrAppNameAllowedForSite(clientType, privacyBits, siteId, domainOrAppName, keys)) { - return new DecryptionResponse(DecryptionStatus.DomainNameCheckFailed, null, established, siteId, siteKey.SiteId, identityType, advertisingTokenVersion, privacyBits.IsClientSideGenerated, expiry); + return new DecryptionResponse(DecryptionStatus.DomainOrAppNameCheckFailed, null, established, siteId, siteKey.SiteId, identityType, advertisingTokenVersion, privacyBits.IsClientSideGenerated, expiry); } if (!DoesTokenHaveValidLifetime(clientType, keys, generated, expiry, now)) @@ -242,15 +242,15 @@ private static bool DoesTokenHaveValidLifetimeImpl(DateTime generatedOrNow, Date return (generatedOrNow - now).TotalSeconds <= allowClockSkewSeconds; //returns false if token generated too far in the future } - private static bool IsDomainNameAllowedForSite(ClientType clientType, PrivacyBits privacyBits, int siteId, string domainName, KeyContainer keys) + private static bool IsDomainOrAppNameAllowedForSite(ClientType clientType, PrivacyBits privacyBits, int siteId, string domainOrAppName, KeyContainer keys) { if (!privacyBits.IsClientSideGenerated) return true; - if (clientType != ClientType.Bidstream && clientType != ClientType.LegacyWithDomainCheck) + if (clientType != ClientType.Bidstream && clientType != ClientType.LegacyWithDomainOrAppNameCheck) return true; - return keys.IsDomainNameAllowedForSite(siteId, domainName); + return keys.IsDomainOrAppNameAllowedForSite(siteId, domainOrAppName); } internal static EncryptionDataResponse Encrypt(string rawUid, KeyContainer keys, IdentityScope identityScope, DateTime now) @@ -327,8 +327,8 @@ internal static EncryptionDataResponse EncryptData(EncryptionDataRequest request { try { - // if the enableDomainNameCheck param is enabled , the caller would have to provide siteId as part of the EncryptionDataRequest. - DecryptionResponse decryptedToken = Decrypt(request.AdvertisingToken, keys, now, domainName: null, identityScope, ClientType.LegacyWithoutDomainCheck); + // if the enableDomainOrAppNameCheck param is enabled , the caller would have to provide siteId as part of the EncryptionDataRequest. + DecryptionResponse decryptedToken = Decrypt(request.AdvertisingToken, keys, now, domainOrAppName: null, identityScope, ClientType.LegacyWithoutDomainOrAppNameCheck); if (!decryptedToken.Success) { return EncryptionDataResponse.MakeError(EncryptionStatus.TokenDecryptFailure); diff --git a/test/UID2.Client.Test/BidstreamClientTests.cs b/test/UID2.Client.Test/BidstreamClientTests.cs index 207547f..1d59bc4 100644 --- a/test/UID2.Client.Test/BidstreamClientTests.cs +++ b/test/UID2.Client.Test/BidstreamClientTests.cs @@ -302,21 +302,52 @@ private void UserOptedOutTest(TokenVersion tokenVersion) } [Theory] - // These are the domains associated with site SITE_ID, as defined by KeySharingResponse(); + // These are the domain or app names associated with site SITE_ID, as defined by KeySharingResponse(); [InlineData("example.com", TokenVersion.V2)] [InlineData("example.org", TokenVersion.V2)] + [InlineData("com.123.Game.App.android", TokenVersion.V2)] + [InlineData("123456789", TokenVersion.V2)] [InlineData("example.com", TokenVersion.V3)] [InlineData("example.org", TokenVersion.V3)] + [InlineData("com.123.Game.App.android", TokenVersion.V3)] + [InlineData("123456789", TokenVersion.V3)] [InlineData("example.com", TokenVersion.V4)] [InlineData("example.org", TokenVersion.V4)] - private void TokenIsCstgDerivedTest(string domainName, TokenVersion tokenVersion) + [InlineData("com.123.Game.App.android", TokenVersion.V4)] + [InlineData("123456789", TokenVersion.V4)] + private void TokenIsCstgDerivedTest(string domainOrAppName, TokenVersion tokenVersion) { Refresh(KeySharingResponse(new[] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); string advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).WithVersion(tokenVersion).Build(); ValidateAdvertisingToken(advertisingToken, IdentityScope.UID2, IdentityType.Email, tokenVersion); - var res = _client.DecryptTokenIntoRawUid(advertisingToken, domainName); + var res = _client.DecryptTokenIntoRawUid(advertisingToken, domainOrAppName); + Assert.True(res.IsClientSideGenerated); + Assert.True(res.Success); + Assert.Equal(DecryptionStatus.Success, res.Status); + Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid); + } + + [Theory] + // These are the domain or app names associated with site SITE_ID but vary in capitalization, as defined by KeySharingResponse(); + [InlineData("Example.com", TokenVersion.V2)] + [InlineData("Example.Org", TokenVersion.V2)] + [InlineData("com.123.Game.app.android", TokenVersion.V2)] + [InlineData("Example.com", TokenVersion.V3)] + [InlineData("Example.Org", TokenVersion.V3)] + [InlineData("com.123.Game.app.android", TokenVersion.V3)] + [InlineData("Example.com", TokenVersion.V4)] + [InlineData("Example.Org", TokenVersion.V4)] + [InlineData("com.123.Game.app.android", TokenVersion.V4)] + private void DomainOrAppNameCaseInSensitiveTest(string domainOrAppName, TokenVersion tokenVersion) + { + Refresh(KeySharingResponse(new[] { MASTER_KEY, SITE_KEY })); + + var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); + string advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).WithVersion(tokenVersion).Build(); + ValidateAdvertisingToken(advertisingToken, IdentityScope.UID2, IdentityType.Email, tokenVersion); + var res = _client.DecryptTokenIntoRawUid(advertisingToken, domainOrAppName); Assert.True(res.IsClientSideGenerated); Assert.True(res.Success); Assert.Equal(DecryptionStatus.Success, res.Status); @@ -328,50 +359,59 @@ private void TokenIsCstgDerivedTest(string domainName, TokenVersion tokenVersion [InlineData("", TokenVersion.V2)] [InlineData("example.net", TokenVersion.V2)] // Domain associated with site SITE_ID2, as defined by KeySharingResponse(). [InlineData("example.edu", TokenVersion.V2)] // Domain associated with site SITE_ID2, as defined by KeySharingResponse(). + [InlineData("com.123.Game.App.ios", TokenVersion.V2)] // App associated with site SITE_ID2, as defined by KeySharingResponse(). + [InlineData("123456780", TokenVersion.V2)] // App associated with site SITE_ID2, as defined by KeySharingResponse(). [InlineData("foo.com", TokenVersion.V2)] // Domain not associated with any site. [InlineData((string)null, TokenVersion.V3)] [InlineData("", TokenVersion.V3)] [InlineData("example.net", TokenVersion.V3)] // Domain associated with site SITE_ID2, as defined by KeySharingResponse(). [InlineData("example.edu", TokenVersion.V3)] // Domain associated with site SITE_ID2, as defined by KeySharingResponse(). + [InlineData("com.123.Game.App.ios", TokenVersion.V3)] // App associated with site SITE_ID2, as defined by KeySharingResponse(). + [InlineData("123456780", TokenVersion.V3)] // App associated with site SITE_ID2, as defined by KeySharingResponse(). [InlineData("foo.com", TokenVersion.V3)] // Domain not associated with any site. [InlineData((string)null, TokenVersion.V4)] [InlineData("", TokenVersion.V4)] [InlineData("example.net", TokenVersion.V4)] // Domain associated with site SITE_ID2, as defined by KeySharingResponse(). [InlineData("example.edu", TokenVersion.V4)] // Domain associated with site SITE_ID2, as defined by KeySharingResponse(). + [InlineData("com.123.Game.App.ios", TokenVersion.V4)] // App associated with site SITE_ID2, as defined by KeySharingResponse(). + [InlineData("123456780", TokenVersion.V4)] // App associated with site SITE_ID2, as defined by KeySharingResponse(). [InlineData("foo.com", TokenVersion.V4)] // Domain not associated with any site. - private void TokenIsCstgDerivedDomainNameFailTest(string domainName, TokenVersion tokenVersion) + private void TokenIsCstgDerivedDomainOrAppNameFailTest(string domainOrAppName, TokenVersion tokenVersion) { Refresh(KeySharingResponse(new[] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).WithVersion(tokenVersion).Build(); - var res = _client.DecryptTokenIntoRawUid(advertisingToken, domainName); + var res = _client.DecryptTokenIntoRawUid(advertisingToken, domainOrAppName); Assert.True(res.IsClientSideGenerated); Assert.False(res.Success); - Assert.Equal(DecryptionStatus.DomainNameCheckFailed, res.Status); + Assert.Equal(DecryptionStatus.DomainOrAppNameCheckFailed, res.Status); Assert.Null(res.Uid); } [Theory] - // Any domain name is OK, because the token is not client-side generated. + // Any domain or app name is OK, because the token is not client-side generated. [InlineData((string)null, TokenVersion.V2)] [InlineData("", TokenVersion.V2)] [InlineData("example.com", TokenVersion.V2)] [InlineData("foo.com", TokenVersion.V2)] + [InlineData("com.uid2.devapp", TokenVersion.V2)] [InlineData((string)null, TokenVersion.V3)] [InlineData("", TokenVersion.V3)] [InlineData("example.com", TokenVersion.V3)] [InlineData("foo.com", TokenVersion.V3)] + [InlineData("com.uid2.devapp", TokenVersion.V3)] [InlineData((string)null, TokenVersion.V4)] [InlineData("", TokenVersion.V4)] [InlineData("example.com", TokenVersion.V4)] [InlineData("foo.com", TokenVersion.V4)] - private void TokenIsNotCstgDerivedDomainNameSuccessTest(string domainName, TokenVersion tokenVersion) + [InlineData("com.uid2.devapp", TokenVersion.V4)] + private void TokenIsNotCstgDerivedDomainNameSuccessTest(string domainOrAppName, TokenVersion tokenVersion) { Refresh(KeySharingResponse(new[] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(false).Build(); string advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).WithVersion(tokenVersion).Build(); ValidateAdvertisingToken(advertisingToken, IdentityScope.UID2, IdentityType.Email, tokenVersion); - var res = _client.DecryptTokenIntoRawUid(advertisingToken, domainName); + var res = _client.DecryptTokenIntoRawUid(advertisingToken, domainOrAppName); Assert.False(res.IsClientSideGenerated); Assert.True(res.Success); Assert.Equal(DecryptionStatus.Success, res.Status); diff --git a/test/UID2.Client.Test/EncryptionTestsV2.cs b/test/UID2.Client.Test/EncryptionTestsV2.cs index 57189b8..79492bf 100644 --- a/test/UID2.Client.Test/EncryptionTestsV2.cs +++ b/test/UID2.Client.Test/EncryptionTestsV2.cs @@ -39,15 +39,34 @@ public void UserOptedOutTest() } [Theory] - // These are the domains associated with site SITE_ID, as defined by KeySharingResponse(); + // These are the domain or app names associated with site SITE_ID, as defined by KeySharingResponse(); [InlineData("example.com")] [InlineData("example.org")] - public void TokenIsCstgDerivedTest(string domainName) + [InlineData("com.123.Game.App.android")] + [InlineData("123456789")] + public void TokenIsCstgDerivedTest(string domainOrAppName) { _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build(); - var res = _client.Decrypt(advertisingToken, domainName); + var res = _client.Decrypt(advertisingToken, domainOrAppName); + Assert.True(res.IsClientSideGenerated); + Assert.True(res.Success); + Assert.Equal(DecryptionStatus.Success, res.Status); + Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid); + } + + [Theory] + // These are the domain or app names associated with site SITE_ID but vary in capitalization, as defined by KeySharingResponse(); + [InlineData("example.Com")] + [InlineData("Example.org")] + [InlineData("com.123.game.App.android")] + public void DomainOrAppNameCaseInSensitiveTest(string domainOrAppName) + { + _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); + var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); + string advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build(); + var res = _client.Decrypt(advertisingToken, domainOrAppName); Assert.True(res.IsClientSideGenerated); Assert.True(res.Success); Assert.Equal(DecryptionStatus.Success, res.Status); @@ -57,30 +76,32 @@ public void TokenIsCstgDerivedTest(string domainName) [Theory] [InlineData((string)null)] [InlineData("")] - // Domains associated with site SITE_ID2, as defined by KeySharingResponse(). + // Domain or app names associated with site SITE_ID2, as defined by KeySharingResponse(). [InlineData("example.net")] [InlineData("example.edu")] + [InlineData("com.123.Game.App.ios")] + [InlineData("123456780")] // Domain not associated with any site. [InlineData("foo.com")] - public void TokenIsCstgDerivedDomainNameFailTest(string domainName) + public void TokenIsCstgDerivedDomainOrAppNameFailTest(string domainOrAppName) { _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build(); - var res = _client.Decrypt(advertisingToken, domainName); + var res = _client.Decrypt(advertisingToken, domainOrAppName); Assert.True(res.IsClientSideGenerated); Assert.False(res.Success); - Assert.Equal(DecryptionStatus.DomainNameCheckFailed, res.Status); + Assert.Equal(DecryptionStatus.DomainOrAppNameCheckFailed, res.Status); Assert.Null(res.Uid); } - // if there is domain name associated with sites but we explicitly call + // if there is domain or app name associated with sites but we explicitly call // DecryptionResponse Decrypt(string token) or DecryptionResponse Decrypt(string token, DateTime utcNow) // and we do not want to do domain name check // the Decrypt function would still decrypt successfully // in case DSP does not want to enable domain name check [Fact] - public void TokenIsCstgDerivedNoDomainNameTest() + public void TokenIsCstgDerivedNoDomainOrAppNameTest() { _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); @@ -98,12 +119,13 @@ public void TokenIsCstgDerivedNoDomainNameTest() [InlineData("")] [InlineData("example.com")] [InlineData("foo.com")] - public void TokenIsNotCstgDerivedDomainNameSuccessTest(string domainName) + [InlineData("com.uid2.devapp")] + public void TokenIsNotCstgDerivedDomainOrAppNameSuccessTest(string domainOrAppName) { _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(false).Build(); var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build(); - var res = _client.Decrypt(advertisingToken, domainName); + var res = _client.Decrypt(advertisingToken, domainOrAppName); Assert.False(res.IsClientSideGenerated); Assert.True(res.Success); Assert.Equal(DecryptionStatus.Success, res.Status); diff --git a/test/UID2.Client.Test/EncryptionTestsV3.cs b/test/UID2.Client.Test/EncryptionTestsV3.cs index 5b56094..b8eddbe 100644 --- a/test/UID2.Client.Test/EncryptionTestsV3.cs +++ b/test/UID2.Client.Test/EncryptionTestsV3.cs @@ -59,15 +59,34 @@ public void UserOptedOutTest() } [Theory] - // These are the domains associated with site SITE_ID, as defined by KeySharingResponse(); + // These are the domain or app names associated with site SITE_ID, as defined by KeySharingResponse(); [InlineData("example.com")] [InlineData("example.org")] - public void TokenIsCstgDerivedTest(string domainName) + [InlineData("com.123.Game.App.android")] + [InlineData("123456789")] + public void TokenIsCstgDerivedTest(string domainOrAppName) { _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build(); - var res = _client.Decrypt(advertisingToken, domainName); + var res = _client.Decrypt(advertisingToken, domainOrAppName); + Assert.True(res.IsClientSideGenerated); + Assert.True(res.Success); + Assert.Equal(DecryptionStatus.Success, res.Status); + Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid); + } + + [Theory] + // These are the domain or app names associated with site SITE_ID but vary in capitalization, as defined by KeySharingResponse(); + [InlineData("example.Com")] + [InlineData("Example.org")] + [InlineData("com.123.game.App.android")] + public void DomainOrAppNameCaseInSensitiveTest(string domainOrAppName) + { + _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); + var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); + string advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build(); + var res = _client.Decrypt(advertisingToken, domainOrAppName); Assert.True(res.IsClientSideGenerated); Assert.True(res.Success); Assert.Equal(DecryptionStatus.Success, res.Status); @@ -77,30 +96,32 @@ public void TokenIsCstgDerivedTest(string domainName) [Theory] [InlineData((string)null)] [InlineData("")] - // Domains associated with site SITE_ID2, as defined by KeySharingResponse(). + // Domain or app names associated with site SITE_ID2, as defined by KeySharingResponse(). [InlineData("example.net")] [InlineData("example.edu")] + [InlineData("com.123.Game.App.ios")] + [InlineData("123456780")] // Domain not associated with any site. [InlineData("foo.com")] - public void TokenIsCstgDerivedDomainNameFailTest(string domainName) + public void TokenIsCstgDerivedDomainOrAppNameFailTest(string domainOrAppName) { _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build(); - var res = _client.Decrypt(advertisingToken, domainName); + var res = _client.Decrypt(advertisingToken, domainOrAppName); Assert.True(res.IsClientSideGenerated); Assert.False(res.Success); - Assert.Equal(DecryptionStatus.DomainNameCheckFailed, res.Status); + Assert.Equal(DecryptionStatus.DomainOrAppNameCheckFailed, res.Status); Assert.Null(res.Uid); } - // if there is domain name associated with sites but we explicitly call + // if there is domain or app name associated with sites but we explicitly call // DecryptionResponse Decrypt(string token) or DecryptionResponse Decrypt(string token, DateTime utcNow) // and we do not want to do domain name check // the Decrypt function would still decrypt successfully // in case DSP does not want to enable domain name check [Fact] - public void TokenIsCstgDerivedNoDomainNameTest() + public void TokenIsCstgDerivedNoDomainOrAppNameTest() { _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); @@ -113,17 +134,18 @@ public void TokenIsCstgDerivedNoDomainNameTest() } [Theory] - // Any domain name is OK, because the token is not client-side generated. + // Any domain or app name is OK, because the token is not client-side generated. [InlineData((string) null)] [InlineData("")] [InlineData("example.com")] [InlineData("foo.com")] - public void TokenIsNotCstgDerivedDomainNameSuccessTest(string domainName) + [InlineData("com.uid2.devapp")] + public void TokenIsNotCstgDerivedDomainOrAppNameSuccessTest(string domainOrAppName) { _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(false).Build(); var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build(); - var res = _client.Decrypt(advertisingToken, domainName); + var res = _client.Decrypt(advertisingToken, domainOrAppName); Assert.False(res.IsClientSideGenerated); Assert.True(res.Success); Assert.Equal(DecryptionStatus.Success, res.Status); diff --git a/test/UID2.Client.Test/EncryptionTestsV4.cs b/test/UID2.Client.Test/EncryptionTestsV4.cs index 23289f8..34b1dd1 100644 --- a/test/UID2.Client.Test/EncryptionTestsV4.cs +++ b/test/UID2.Client.Test/EncryptionTestsV4.cs @@ -189,16 +189,36 @@ public void UserOptedOutTest() } [Theory] - // These are the domains associated with site SITE_ID, as defined by KeySharingResponse(); + // These are the domain or app names associated with site SITE_ID, as defined by KeySharingResponse(); [InlineData("example.com")] [InlineData("example.org")] - public void TokenIsCstgDerivedTest(string domainName) + [InlineData("com.123.Game.App.android")] + [InlineData("123456789")] + public void TokenIsCstgDerivedTest(string domainOrAppName) { _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); string advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build(); ValidateAdvertisingToken(advertisingToken, IdentityScope.UID2, IdentityType.Email); - var res = _client.Decrypt(advertisingToken, domainName); + var res = _client.Decrypt(advertisingToken, domainOrAppName); + Assert.True(res.IsClientSideGenerated); + Assert.True(res.Success); + Assert.Equal(DecryptionStatus.Success, res.Status); + Assert.Equal(EXAMPLE_EMAIL_RAW_UID2_V2, res.Uid); + } + + [Theory] + // These are the domain or app names associated with site SITE_ID but vary in capitalization, as defined by KeySharingResponse(); + [InlineData("example.Com")] + [InlineData("Example.org")] + [InlineData("com.123.game.App.android")] + public void DomainOrAppNameCaseInSensitiveTest(string domainOrAppName) + { + _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); + var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); + string advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build(); + ValidateAdvertisingToken(advertisingToken, IdentityScope.UID2, IdentityType.Email); + var res = _client.Decrypt(advertisingToken, domainOrAppName); Assert.True(res.IsClientSideGenerated); Assert.True(res.Success); Assert.Equal(DecryptionStatus.Success, res.Status); @@ -208,30 +228,32 @@ public void TokenIsCstgDerivedTest(string domainName) [Theory] [InlineData((string)null)] [InlineData("")] - // Domains associated with site SITE_ID2, as defined by KeySharingResponse(). + // Domain or app names associated with site SITE_ID2, as defined by KeySharingResponse(). [InlineData("example.net")] [InlineData("example.edu")] + [InlineData("com.123.Game.App.ios")] + [InlineData("123456780")] // Domain not associated with any site. [InlineData("foo.com")] - public void TokenIsCstgDerivedDomainNameFailTest(string domainName) + public void TokenIsCstgDerivedDomainOrAppNameFailTest(string domainOrAppName) { _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); var advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build(); - var res = _client.Decrypt(advertisingToken, domainName); + var res = _client.Decrypt(advertisingToken, domainOrAppName); Assert.True(res.IsClientSideGenerated); Assert.False(res.Success); - Assert.Equal(DecryptionStatus.DomainNameCheckFailed, res.Status); + Assert.Equal(DecryptionStatus.DomainOrAppNameCheckFailed, res.Status); Assert.Null(res.Uid); } - // if there is domain name associated with sites but we explicitly call + // if there is domain or app name associated with sites but we explicitly call // DecryptionResponse Decrypt(string token) or DecryptionResponse Decrypt(string token, DateTime utcNow) // and we do not want to do domain name check // the Decrypt function would still decrypt successfully // in case DSP does not want to enable domain name check [Fact] - public void TokenIsCstgDerivedNoDomainNameTest() + public void TokenIsCstgDerivedNoDomainOrAppNameTest() { _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(true).Build(); @@ -249,13 +271,14 @@ public void TokenIsCstgDerivedNoDomainNameTest() [InlineData("")] [InlineData("example.com")] [InlineData("foo.com")] - public void TokenIsNotCstgDerivedDomainNameSuccessTest(string domainName) + [InlineData("com.uid2.devapp")] + public void TokenIsNotCstgDerivedDomainNameSuccessTest(string domainOrAppName) { _client.RefreshJson(KeySharingResponse(new [] { MASTER_KEY, SITE_KEY })); var privacyBits = PrivacyBitsBuilder.Builder().WithClientSideGenerated(false).Build(); string advertisingToken = _tokenBuilder.WithPrivacyBits(privacyBits).Build(); ValidateAdvertisingToken(advertisingToken, IdentityScope.UID2, IdentityType.Email); - var res = _client.Decrypt(advertisingToken, domainName); + var res = _client.Decrypt(advertisingToken, domainOrAppName); Assert.False(res.IsClientSideGenerated); Assert.True(res.Success); Assert.Equal(DecryptionStatus.Success, res.Status); diff --git a/test/UID2.Client.Test/KeyParserTests.cs b/test/UID2.Client.Test/KeyParserTests.cs index 22760fd..19a9143 100644 --- a/test/UID2.Client.Test/KeyParserTests.cs +++ b/test/UID2.Client.Test/KeyParserTests.cs @@ -156,9 +156,9 @@ public void ParseMissingSiteData() var keyContainer = KeyParser.Parse(json); - var isDomainNameForSite = keyContainer.IsDomainNameAllowedForSite(1, "example.com"); + var isDomainOrAppNameForSite = keyContainer.IsDomainOrAppNameAllowedForSite(1, "example.com"); - Assert.False(isDomainNameForSite); + Assert.False(isDomainOrAppNameForSite); Assert.True(keyContainer.TryGetKey(3, out var key)); } @@ -184,9 +184,9 @@ public void ParseEmptySiteData() var keyContainer = KeyParser.Parse(json); - var isDomainNameForSite = keyContainer.IsDomainNameAllowedForSite(1, "example.com"); - Assert.False(isDomainNameForSite); - Assert.False(keyContainer.IsDomainNameAllowedForSite(1, null)); + var isDomainOrAppNameForSite = keyContainer.IsDomainOrAppNameAllowedForSite(1, "example.com"); + Assert.False(isDomainOrAppNameForSite); + Assert.False(keyContainer.IsDomainOrAppNameAllowedForSite(1, null)); Assert.True(keyContainer.TryGetKey(3, out var key)); } @@ -220,14 +220,14 @@ public void ParseSiteDataSharingEndpoint() var keyContainer = KeyParser.Parse(s); - Assert.True(keyContainer.IsDomainNameAllowedForSite(9, "example.com")); - Assert.False(keyContainer.IsDomainNameAllowedForSite(9, "example.org")); - Assert.False(keyContainer.IsDomainNameAllowedForSite(9, "example.net")); + Assert.True(keyContainer.IsDomainOrAppNameAllowedForSite(9, "example.com")); + Assert.False(keyContainer.IsDomainOrAppNameAllowedForSite(9, "example.org")); + Assert.False(keyContainer.IsDomainOrAppNameAllowedForSite(9, "example.net")); - Assert.False(keyContainer.IsDomainNameAllowedForSite(100, "example.com")); - Assert.True(keyContainer.IsDomainNameAllowedForSite(100, "example.org")); - Assert.True(keyContainer.IsDomainNameAllowedForSite(100, "example.net")); - Assert.False(keyContainer.IsDomainNameAllowedForSite(100, null)); + Assert.False(keyContainer.IsDomainOrAppNameAllowedForSite(100, "example.com")); + Assert.True(keyContainer.IsDomainOrAppNameAllowedForSite(100, "example.org")); + Assert.True(keyContainer.IsDomainOrAppNameAllowedForSite(100, "example.net")); + Assert.False(keyContainer.IsDomainOrAppNameAllowedForSite(100, null)); Assert.True(keyContainer.TryGetKey(3, out var key)); } diff --git a/test/UID2.Client.Test/TestData.cs b/test/UID2.Client.Test/TestData.cs index 981a547..b078ba3 100644 --- a/test/UID2.Client.Test/TestData.cs +++ b/test/UID2.Client.Test/TestData.cs @@ -79,12 +79,12 @@ internal static string KeySharingResponse(IEnumerable keys, int? callerSite new { id = SITE_ID, - domain_names = new[] { "example.com", "example.org" } + domain_names = new[] { "example.com", "example.org", "com.123.Game.App.android", "123456789" } }, new { id = SITE_ID2, - domain_names = new[] { "example.net", "example.edu" } + domain_names = new[] { "example.net", "example.edu", "com.123.Game.App.ios", "123456780" } } } }