Skip to content

Commit 3f9bbbe

Browse files
authored
advertiser and data provider support (#55)
advertiser and data provider support - /v2/identity/map
1 parent e8a299a commit 3f9bbbe

15 files changed

+720
-363
lines changed

README.md

Lines changed: 6 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,8 @@ This document includes:
66
* [Who Is this SDK for?](#who-is-this-sdk-for)
77
* [Requirements](#requirements)
88
* [Install](#install)
9-
* [Usage for DSPs](#usage-for-dsps)
10-
* [Usage for Publishers](#usage-for-publishers)
11-
- [Basic Usage](#basic-usage)
12-
- [Advanced Usage](#advanced-usage)
13-
* [Usage for UID2 Sharers](#usage-for-uid2-sharers)
9+
* [Usage](#usage)
10+
* [Example usage for DSPs](#example-usage-for-dsps)
1411

1512
## Who Is this SDK for?
1613

@@ -24,198 +21,9 @@ This SDK requires Java version 1.8 or later.
2421

2522
To install the SDK, add the `uid2-client` dependency to your project's `pom.xml` file [as shown here](https://central.sonatype.com/artifact/com.uid2/uid2-client).
2623

27-
## Usage for DSPs
24+
## Usage
2825

29-
For an example of usage for DSPs, see [com.uid2.client.test.IntegrationExamples](https://github.com/IABTechLab/uid2-client-java/blob/master/src/test/java/com/uid2/client/test/IntegrationExamples.java).
30-
31-
## Usage for Publishers
32-
33-
As a publisher, there are two ways to use this SDK:
34-
1. [**Basic Usage**](#basic-usage) is for publishers who want to use this SDK's HTTP implementation (synchronous [OkHttp](https://square.github.io/okhttp/)).
35-
2. [**Advanced Usage**](#advanced-usage) is for publishers who prefer to use their own HTTP library.
36-
37-
For an example application that demonstrates both Basic and Advanced usage, see [Java UID2 Integration Example](https://github.com/UnifiedID2/uid2-examples/tree/main/publisher/uid2-java-test-site#readme).
38-
39-
### Basic Usage
40-
41-
If you're using the SDK's HTTP implementation, follow these steps.
42-
43-
1. Create an instance of PublisherUid2Client as an instance variable.
44-
45-
`private final PublisherUid2Client publisherUid2Client = new PublisherUid2Client(UID2_BASE_URL, UID2_API_KEY, UID2_SECRET_KEY);`
46-
47-
2. Call a function that takes the user's email address or phone number as input and generates a `TokenGenerateResponse` object. The following example uses an email address:
48-
49-
`TokenGenerateResponse tokenGenerateResponse = publisherUid2Client.generateTokenResponse(TokenGenerateInput.fromEmail(emailAddress).doNotGenerateTokensForOptedOut());`
50-
51-
>IMPORTANT: Be sure to call this function only when you have obtained legal basis to convert the user’s [directly identifying information (DII)](https://unifiedid.com/docs/ref-info/glossary-uid#gl-dii) to UID2 tokens for targeted advertising.
52-
53-
>IMPORTANT: Always apply `doNotGenerateTokensForOptedOut()`. This applies a parameter similar to setting `optout_check=1` in the call to the POST /token/generate endpoint (see [Unencrypted JSON Body Parameters](https://unifiedid.com/docs/endpoints/post-token-generate#unencrypted-json-body-parameters)).
54-
55-
#### Standard Integration
56-
57-
If you're using standard integration (client and server) (see [JavaScript Standard Integration Guide](https://unifiedid.com/docs/guides/integration-javascript-standard)), follow this step:
58-
59-
* Send this identity as a JSON string back to the client (to use in the [identity field](https://unifiedid.com/docs/sdks/client-side-identity#initopts-object-void)) using the following:
60-
61-
```
62-
tokenGenerateResponse.getIdentityJsonString()
63-
```
64-
65-
>NOTE: If the user has opted out, this method returns `null`, so be sure to handle that case.
66-
67-
#### Server-Only Integration
68-
69-
If you're using server-only integration (see [Publisher Integration Guide, Server-Only](https://unifiedid.com/docs/guides/custom-publisher-integration)):
70-
71-
1. Store this identity as a JSON string in the user's session, using the `tokenGenerateResponse.getIdentityJsonString()` function.
72-
73-
If the user has opted out, this method returns `null`, so be sure to handle that case.
74-
2. To retrieve the user's UID2 token, use:
75-
76-
```
77-
IdentityTokens identity = tokenGenerateResponse.getIdentity();
78-
if (identity != null) { String advertisingToken = identity.getAdvertisingToken(); }
79-
```
80-
4. When the user accesses another page, or on a timer, determine whether a refresh is needed:
81-
1. Retrieve the identity JSON string from the user's session, and then call the following function that takes the identity information as input and generates an `IdentityTokens` object:
82-
83-
`IdentityTokens identity = IdentityTokens.fromJsonString(identityJsonString);`
84-
2. Determine if the identity can be refreshed (that is, the refresh token hasn't expired):
85-
86-
` if (identity == null || !identity.isRefreshable()) { we must no longer use this identity (for example, remove this identity from the user's session) }`
87-
3. Determine if a refresh is needed:
88-
89-
`if (identity.isDueForRefresh()) {..}`
90-
5. If needed, refresh the token and associated values:
91-
92-
`TokenRefreshResponse tokenRefreshResponse = publisherUid2Client.refreshToken(identity);`
93-
94-
6. Store `tokenRefreshResponse.getIdentityJsonString()` in the user's session.
95-
96-
If the user has opted out, this method returns `null`, indicating that the user's identity should be removed from the session. To confirm optout, you can use the `tokenRefreshResponse.isOptout()` function.
97-
98-
### Advanced Usage
99-
100-
1. Create an instance of PublisherUid2Helper as an instance variable:
101-
102-
`private final PublisherUid2Helper publisherUid2Helper = new PublisherUid2Helper(UID2_SECRET_KEY);`
103-
2. Call a function that takes the user's email address or phone number as input and creates a secure request data envelope. See [Encrypting requests](https://unifiedid.com/docs/getting-started/gs-encryption-decryption#encrypting-requests). The following example uses an email address:
104-
105-
`EnvelopeV2 envelope = publisherUid2Helper.createEnvelopeForTokenGenerateRequest(TokenGenerateInput.fromEmail(emailAddress).doNotGenerateTokensForOptedOut());`
106-
3. Using an HTTP client library of your choice, post this envelope to the [POST token/generate](https://unifiedid.com/docs/endpoints/post-token-generate) endpoint, including headers and body:
107-
1. Headers: Depending on your HTTP library, this might look something like the following:
108-
109-
`.putHeader("Authorization", "Bearer " + UID2_API_KEY)`
110-
`.putHeader("X-UID2-Client-Version", PublisherUid2Helper.getVersionHeader())`
111-
2. Body: `envelope.getEnvelope()`
112-
>IMPORTANT: Be sure to call this endpoint only when you have obtained legal basis to convert the user’s [directly identifying information (DII)](https://unifiedid.com/docs/ref-info/glossary-uid#gl-dii) to UID2 tokens for targeted advertising.
113-
114-
>IMPORTANT: Always apply `doNotGenerateTokensForOptedOut()`. This applies a parameter similar to setting `optout_check=1` in the call to the POST /token/generate endpoint (see [Unencrypted JSON Body Parameters](https://unifiedid.com/docs/endpoints/post-token-generate#unencrypted-json-body-parameters)).
26+
For documentation on usage, see the [UID2 SDK for Java Reference Guide](https://unifiedid.com/docs/sdks/uid2-sdk-ref-java).
11527

116-
4. If the HTTP response status code is _not_ 200, see [Response Status Codes](https://unifiedid.com/docs/endpoints/post-token-generate#response-status-codes) to determine next steps. Otherwise, convert the UID2 identity response content into a `TokenGenerateResponse` object:
117-
118-
`TokenGenerateResponse tokenGenerateResponse = publisherUid2Helper.createTokenGenerateResponse({response body}, envelope);`
119-
120-
#### Standard Integration
121-
122-
If you're using standard integration (client and server) (see [JavaScript Standard Integration Guide](https://unifiedid.com/docs/guides/integration-javascript-standard)):
123-
124-
* Send this identity as a JSON string back to the client (to use in the [identity field](https://unifiedid.com/docs/sdks/client-side-identity#initopts-object-void)) using the following:
125-
126-
```
127-
tokenGenerateResponse.getIdentityJsonString()
128-
```
129-
130-
>NOTE: If the user has opted out, this method returns `null`, so be sure to handle that case.
131-
132-
#### Server-Only Integration
133-
134-
If you're using server-only integration (see [Publisher Integration Guide, Server-Only](https://unifiedid.com/docs/guides/custom-publisher-integration)):
135-
136-
1. Store this identity as a JSON string in the user's session, using: `tokenGenerateResponse.getIdentityJsonString()`.
137-
138-
If the user has opted out, this method returns null, so be sure to handle that case.
139-
2. To retrieve the user's UID2 token, use:
140-
141-
```
142-
IdentityTokens identity = tokenGenerateResponse.getIdentity();
143-
if (identity != null) { String advertisingToken = identity.getAdvertisingToken(); }
144-
```
145-
146-
3. When the user accesses another page, or on a timer, determine whether a refresh is needed:
147-
1. Retrieve the identity JSON string from the user's session, and then call the following function that generates an `IdentityTokens` object:
148-
149-
```
150-
IdentityTokens identity = IdentityTokens.fromJsonString(identityJsonString);
151-
```
152-
153-
2. Determine if the identity can be refreshed (that is, the refresh token hasn't expired):
154-
155-
```
156-
if (identity == null || !identity.isRefreshable()) { we must no longer use this identity (for example, remove this identity from the user's session) }
157-
```
158-
159-
3. Determine if a refresh is needed:
160-
161-
```
162-
if (identity.isDueForRefresh()) {..}
163-
```
164-
165-
4. If a refresh is needed, call the [POST token/refresh](https://unifiedid.com/docs/endpoints/post-token-refresh) endpoint, with:
166-
1. Headers (depending on your HTTP library, this might look something like):
167-
168-
`.putHeader("Authorization", "Bearer " + UID2_API_KEY)`
169-
`.putHeader("X-UID2-Client-Version", PublisherUid2Helper.getVersionHeader())`.
170-
2. Body: `identity.getRefreshToken()`
171-
5. If the refresh HTTP response status code is 200:
172-
173-
```
174-
TokenRefreshResponse tokenRefreshResponse = PublisherUid2Helper.createTokenRefreshResponse({response body}, identity);
175-
```
176-
177-
6. Store `tokenRefreshResponse.getIdentityJsonString()` in the user's session.
178-
179-
If the user has opted out, this method returns null, indicating that the user's identity should be removed from the session. To confirm optout, you can use the `tokenRefreshResponse.isOptout()` function.
180-
181-
## Usage for UID2 Sharers
182-
183-
A UID2 sharer is a participant that wants to share UID2s or EUIDs with another participant. Raw UID2s must be encrypted into UID2 tokens before sending them to another participant. For an example of usage, see [com.uid2.client.test.IntegrationExamples](https://github.com/IABTechLab/uid2-client-java/blob/master/src/test/java/com/uid2/client/test/IntegrationExamples.java) (runSharingExample method).
184-
185-
1. Use UID2ClientFactory.create() to create an IUID2Client reference:
186-
187-
```
188-
private final IUID2Client client = UID2ClientFactory.create(UID2_BASE_URL, UID2_API_KEY, UID2_SECRET_KEY);
189-
```
190-
191-
2. Call IUID2Client.refresh once at startup, and then periodically (for example, every hour):
192-
193-
```
194-
client.refresh();
195-
```
196-
197-
3. Senders:
198-
1. Call the following:
199-
200-
```
201-
EncryptionDataResponse encrypted = client.encrypt(rawUid);
202-
```
203-
204-
2. If encryption succeeded, send the UID2 token to the receiver:
205-
206-
```
207-
if (encrypted.isSuccess()) {` send `encrypted.getEncryptedData()` to receiver`} else {`check `encrypted.getStatus()` for the failure reason}
208-
```
209-
210-
4. Receivers:
211-
1. Call the following:
212-
213-
```
214-
DecryptionResponse decrypted = client.decrypt(uidToken);
215-
```
216-
217-
2. If decryption succeeded, use the raw UID2:
218-
219-
```
220-
if (decrypted.isSuccess()) {`use `decrypted.getUid() } else {`check `decrypted.getStatus()` for the failure reason `}
221-
```
28+
## Example usage for DSPs
29+
For an example of usage for DSPs, see [com.uid2.client.test.IntegrationExamples](https://github.com/IABTechLab/uid2-client-java/blob/master/src/test/java/com/uid2/client/test/IntegrationExamples.java).
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.uid2.client;
2+
3+
public class IdentityMapClient {
4+
/**
5+
* @param uid2BaseUrl The <a href="https://unifiedid.com/docs/getting-started/gs-environments">UID2 Base URL</a>
6+
* @param clientApiKey Your client API key
7+
* @param base64SecretKey Your client secret key
8+
*/
9+
public IdentityMapClient(String uid2BaseUrl, String clientApiKey, String base64SecretKey) {
10+
identityMapHelper = new IdentityMapHelper(base64SecretKey);
11+
uid2ClientHelper = new Uid2ClientHelper(uid2BaseUrl, clientApiKey);
12+
}
13+
14+
/**
15+
* @param identityMapInput represents the input required for <a href="https://unifiedid.com/docs/endpoints/post-identity-map">/identity/map</a>
16+
* @return an IdentityMapResponse instance
17+
* @throws Uid2Exception if the response did not contain a "success" status, or the response code was not 200, or there was an error communicating with the provided UID2 Base URL
18+
*/
19+
public IdentityMapResponse generateIdentityMap(IdentityMapInput identityMapInput) {
20+
EnvelopeV2 envelope = identityMapHelper.createEnvelopeForIdentityMapRequest(identityMapInput);
21+
22+
String responseString = uid2ClientHelper.makeRequest(envelope, "/v2/identity/map");
23+
return identityMapHelper.createIdentityMapResponse(responseString, envelope, identityMapInput);
24+
}
25+
26+
private final IdentityMapHelper identityMapHelper;
27+
private final Uid2ClientHelper uid2ClientHelper;
28+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.uid2.client;
2+
3+
import com.google.gson.Gson;
4+
5+
import java.nio.charset.StandardCharsets;
6+
7+
public class IdentityMapHelper {
8+
/**
9+
* @param base64SecretKey your UID2 client secret
10+
*/
11+
public IdentityMapHelper(String base64SecretKey) {uid2Helper = new Uid2Helper(base64SecretKey);}
12+
13+
/**
14+
* @param identityMapInput represents the input required for <a href="https://unifiedid.com/docs/endpoints/post-identity-map">/identity/map</a>
15+
* @return an EnvelopeV2 instance to use in the POST body of <a href="https://unifiedid.com/docs/endpoints/post-identity-map">/identity/map</a>
16+
*/
17+
public EnvelopeV2 createEnvelopeForIdentityMapRequest(IdentityMapInput identityMapInput) {
18+
byte[] jsonBytes = new Gson().toJson(identityMapInput).getBytes(StandardCharsets.UTF_8);
19+
return uid2Helper.createEnvelopeV2(jsonBytes);
20+
}
21+
22+
23+
/**
24+
* @param responseString the response body returned by a call to <a href="https://unifiedid.com/docs/endpoints/post-identity-map">/identity/map</a>
25+
* @param envelope the EnvelopeV2 instance returned by {@link #createEnvelopeForIdentityMapRequest}
26+
* @param identityMapInput the same instance that was passed to {@link #createEnvelopeForIdentityMapRequest}.
27+
* @return an IdentityMapResponse instance
28+
*/
29+
public IdentityMapResponse createIdentityMapResponse(String responseString, EnvelopeV2 envelope, IdentityMapInput identityMapInput) {
30+
String decryptedResponseString = uid2Helper.decrypt(responseString, envelope.getNonce());
31+
return new IdentityMapResponse(decryptedResponseString, identityMapInput);
32+
}
33+
34+
Uid2Helper uid2Helper;
35+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.uid2.client;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import java.util.ArrayList;
5+
import java.util.Collections;
6+
import java.util.HashMap;
7+
import java.util.List;
8+
9+
public class IdentityMapInput {
10+
/**
11+
* @param emails a list of normalized or unnormalized email addresses
12+
* @return a IdentityMapInput instance, to be used in {@link IdentityMapHelper#createEnvelopeForIdentityMapRequest}
13+
*/
14+
public static IdentityMapInput fromEmails(Iterable<String> emails) {
15+
return new IdentityMapInput(IdentityType.Email, emails, false);
16+
}
17+
18+
/**
19+
* @param phones a <a href="https://unifiedid.com/docs/getting-started/gs-normalization-encoding#phone-number-normalization">normalized</a> phone number
20+
* @return an IdentityMapInput instance
21+
*/
22+
public static IdentityMapInput fromPhones(Iterable<String> phones) {
23+
return new IdentityMapInput(IdentityType.Phone, phones, false);
24+
}
25+
26+
/**
27+
* @param hashedEmails a <a href="https://unifiedid.com/docs/getting-started/gs-normalization-encoding#email-address-normalization">normalized</a> and <a href="https://unifiedid.com/docs/getting-started/gs-normalization-encoding#email-address-hash-encoding">hashed</a> email address
28+
* @return an IdentityMapInput instance
29+
*/
30+
public static IdentityMapInput fromHashedEmails(Iterable<String> hashedEmails) {
31+
return new IdentityMapInput(IdentityType.Email, hashedEmails, true);
32+
}
33+
34+
/**
35+
* @param hashedPhones a <a href="https://unifiedid.com/docs/getting-started/gs-normalization-encoding#phone-number-normalization">normalized</a> and <a href="https://unifiedid.com/docs/getting-started/gs-normalization-encoding#phone-number-hash-encoding">hashed</a> phone number
36+
* @return an IdentityMapInput instance
37+
*/
38+
public static IdentityMapInput fromHashedPhones(Iterable<String> hashedPhones) {
39+
return new IdentityMapInput(IdentityType.Phone, hashedPhones, true);
40+
}
41+
42+
private IdentityMapInput(IdentityType identityType, Iterable<String> emailsOrPhones, boolean alreadyHashed) {
43+
if (identityType == IdentityType.Email) {
44+
hashedNormalizedEmails = new ArrayList<>();
45+
for (String email : emailsOrPhones) {
46+
if (alreadyHashed) {
47+
hashedNormalizedEmails.add(email);
48+
} else {
49+
String hashedEmail = InputUtil.normalizeAndHashEmail(email);
50+
hashedNormalizedEmails.add(hashedEmail);
51+
addHashedToRawDiiMapping(hashedEmail, email);
52+
}
53+
}
54+
} else { //phone
55+
hashedNormalizedPhones = new ArrayList<>();
56+
for (String phone : emailsOrPhones) {
57+
if (alreadyHashed) {
58+
hashedNormalizedPhones.add(phone);
59+
} else {
60+
if (!InputUtil.isPhoneNumberNormalized(phone)) {
61+
throw new IllegalArgumentException("phone number is not normalized: " + phone);
62+
}
63+
64+
String hashedNormalizedPhone = InputUtil.getBase64EncodedHash(phone);
65+
addHashedToRawDiiMapping(hashedNormalizedPhone, phone);
66+
hashedNormalizedPhones.add(hashedNormalizedPhone);
67+
}
68+
}
69+
}
70+
}
71+
72+
private void addHashedToRawDiiMapping(String hashedDii, String rawDii) {
73+
hashedDiiToRawDiis.computeIfAbsent(hashedDii, k -> new ArrayList<>()).add(rawDii);
74+
}
75+
76+
77+
List<String> getRawDiis(String identifier) {
78+
final boolean wasInputAlreadyHashed = hashedDiiToRawDiis.isEmpty();
79+
if (wasInputAlreadyHashed)
80+
return Collections.singletonList(identifier);
81+
return hashedDiiToRawDiis.get(identifier);
82+
}
83+
84+
@SerializedName("email_hash")
85+
private List<String> hashedNormalizedEmails;
86+
@SerializedName("phone_hash")
87+
private List<String> hashedNormalizedPhones;
88+
89+
private final transient HashMap<String, List<String>> hashedDiiToRawDiis = new HashMap<>();
90+
}

0 commit comments

Comments
 (0)