diff --git a/paf-mvp-core-js/scripts/generate-model-from-schemas.ts b/paf-mvp-core-js/scripts/generate-model-from-schemas.ts
index c68d9b50..40c41865 100644
--- a/paf-mvp-core-js/scripts/generate-model-from-schemas.ts
+++ b/paf-mvp-core-js/scripts/generate-model-from-schemas.ts
@@ -27,6 +27,8 @@ const removeRefDescription = (schema: JSONSchema4): JSONSchema4 => {
Object.keys(schema.properties).forEach(currentKey => {
schema.properties![currentKey] = removeRefDescription(schema.properties![currentKey])
})
+ } else if (schema.items) {
+ schema.items = removeRefDescription(schema.items)
}
return schema
diff --git a/paf-mvp-core-js/src/endpoints.ts b/paf-mvp-core-js/src/endpoints.ts
index 8412f3d7..aea77240 100644
--- a/paf-mvp-core-js/src/endpoints.ts
+++ b/paf-mvp-core-js/src/endpoints.ts
@@ -1,24 +1,34 @@
-// TODO refactor to group endpoints and params
-export const proxyEndpoints = {
- verifyRedirectRead: '/verify/redirectRead',
- signWrite: '/sign/write',
- signPrefs: '/sign/prefs',
-}
-
-export const proxyUriParams = {
- returnUrl: 'returnUrl',
- message: 'message'
-}
+// TODO refactor to group by operator / operator proxy
// Endpoints exposed by the operator API
export const redirectEndpoints = {
read: '/v1/redirect/get-ids-prefs',
- write: "/v1/redirect/post-ids-prefs"
+ write: '/v1/redirect/post-ids-prefs'
}
export const jsonEndpoints = {
read: '/v1/ids-prefs',
- write: "/v1/ids-prefs",
+ write: '/v1/ids-prefs',
verify3PC: '/v1/3pc',
newId: '/v1/new-id',
identity: '/v1/identity'
}
+
+// Endpoints exposed by the operator proxy
+const proxyPrefix = '/paf-proxy'
+export const jsonProxyEndpoints = {
+ verifyRead: `${proxyPrefix}/v1/verify/read`,
+ signWrite: `${proxyPrefix}/v1/sign/write`,
+ signPrefs: `${proxyPrefix}/v1/sign/prefs`,
+ read: `${proxyPrefix}${jsonEndpoints.read}`,
+ write: `${proxyPrefix}${jsonEndpoints.write}`,
+ verify3PC: `${proxyPrefix}${jsonEndpoints.verify3PC}`,
+}
+export const redirectProxyEndpoints = {
+ read: `${proxyPrefix}${redirectEndpoints.read}`,
+ write: `${proxyPrefix}${redirectEndpoints.write}`,
+}
+
+export const proxyUriParams = {
+ returnUrl: 'returnUrl',
+ message: 'message'
+}
diff --git a/paf-mvp-core-js/src/model/generated-model.ts b/paf-mvp-core-js/src/model/generated-model.ts
index 0b201333..d41cb289 100644
--- a/paf-mvp-core-js/src/model/generated-model.ts
+++ b/paf-mvp-core-js/src/model/generated-model.ts
@@ -27,14 +27,6 @@ export type GetIdentityRequest = null;
* To be detailed.
*/
export type Version = string;
-/**
- * Number of seconds since UNIX Epoch time (1970/01/01 00:00:00)
- */
-export type Timestamp1 = number;
-/**
- * Number of seconds since UNIX Epoch time (1970/01/01 00:00:00)
- */
-export type Timestamp2 = number;
/**
* The base64 representation of a data signature
*/
@@ -42,7 +34,7 @@ export type Signature = string;
/**
* List of identifiers
*/
-export type Identifiers = Identifier1[];
+export type Identifiers = Identifier[];
/**
* The URL that the user should be be redirected to, to provide response data
*/
@@ -74,6 +66,8 @@ export interface _ {
"message-base"?: MessageBase;
"post-ids-prefs-request"?: PostIdsPrefsRequest;
"post-ids-prefs-response"?: PostIdsPrefsResponse;
+ "post-sign-preferences-request"?: PostSignPreferencesRequest;
+ "preferences-data"?: PreferencesData;
preferences?: Preferences;
"redirect-get-ids-prefs-request"?: RedirectGetIdsPrefsRequest;
"redirect-get-ids-prefs-response"?: RedirectGetIdsPrefsResponse;
@@ -85,6 +79,7 @@ export interface _ {
source?: Source;
"test-3pc"?: Test3Pc;
timestamp?: Timestamp;
+ "unsigned-preferences"?: UnsignedPreferences;
version?: Version;
}
/**
@@ -130,8 +125,8 @@ export interface GetIdentityResponse {
* Public key string value
*/
key: string;
- start: Timestamp1;
- end?: Timestamp2;
+ start: Timestamp;
+ end?: Timestamp;
}[];
}
/**
@@ -165,14 +160,18 @@ export interface IdsAndOptionalPreferences {
*/
export interface Preferences {
version: Version;
- data: {
- /**
- * `true` if the user accepted the usage of browsing history for ad personalization, `false` otherwise
- */
- use_browsing_for_personalization: boolean;
- };
+ data: PreferencesData;
source: Source;
}
+/**
+ * Preferences data
+ */
+export interface PreferencesData {
+ /**
+ * Whether the user accepts (`true`) or not (`false`) that their browsing is used for personalization
+ */
+ use_browsing_for_personalization: boolean;
+}
/**
* Source of data representing what contracting party created and signed the data
*/
@@ -221,25 +220,6 @@ export interface GetNewIdResponse {
identifiers: Identifier[];
};
}
-/**
- * A pseudonymous identifier generated for a web user
- */
-export interface Identifier1 {
- version: Version;
- /**
- * The identifier type, identifier of type `paf_browser_id` is mandatory and is "pivot"
- */
- type: "paf_browser_id";
- /**
- * If set to `false`, means the identifier has not yet been persisted as a cookie.
Otherwise, means this identifier is persisted as a PAF cookie
(default value = `true` meaning if the property is omitted the identifier *is* persisted)
- */
- persisted?: boolean;
- /**
- * The identifier value
- */
- value: string;
- source: Source;
-}
/**
* A list of identifiers and some preferences
*/
@@ -276,6 +256,20 @@ export interface PostIdsPrefsResponse {
signature: Signature;
body: IdsAndPreferences;
}
+/**
+ * POST /paf-proxy/v1/sign/prefs request
+ */
+export interface PostSignPreferencesRequest {
+ unsignedPreferences: UnsignedPreferences;
+ identifiers: Identifiers;
+}
+/**
+ * The current preferences of the user before they are signed
+ */
+export interface UnsignedPreferences {
+ version: Version;
+ data: PreferencesData;
+}
/**
* GET /v1/redirect/get-ids-prefs request
*/
@@ -283,6 +277,9 @@ export interface RedirectGetIdsPrefsRequest {
returnUrl: ReturnUrl;
request: GetIdsPrefsRequest;
}
+/**
+ * GET /v1/redirect/get-ids-prefs response
+ */
export interface RedirectGetIdsPrefsResponse {
code: ResponseCode;
response?: GetIdsPrefsResponse;
@@ -295,6 +292,9 @@ export interface RedirectPostIdsPrefsRequest {
returnUrl: ReturnUrl;
request: PostIdsPrefsRequest;
}
+/**
+ * GET /v1/redirect/post-ids-prefs response
+ */
export interface RedirectPostIdsPrefsResponse {
code: ResponseCode;
response?: PostIdsPrefsResponse;
diff --git a/paf-mvp-core-js/src/model/request-builders.ts b/paf-mvp-core-js/src/model/operator-request-builders.ts
similarity index 100%
rename from paf-mvp-core-js/src/model/request-builders.ts
rename to paf-mvp-core-js/src/model/operator-request-builders.ts
diff --git a/paf-mvp-core-js/src/model/response-builders.ts b/paf-mvp-core-js/src/model/operator-response-builders.ts
similarity index 100%
rename from paf-mvp-core-js/src/model/response-builders.ts
rename to paf-mvp-core-js/src/model/operator-response-builders.ts
diff --git a/paf-mvp-core-js/src/model/proxy-request-builders.ts b/paf-mvp-core-js/src/model/proxy-request-builders.ts
new file mode 100644
index 00000000..9e9fd69d
--- /dev/null
+++ b/paf-mvp-core-js/src/model/proxy-request-builders.ts
@@ -0,0 +1,67 @@
+import {
+ GetIdsPrefsResponse,
+ Identifiers,
+ IdsAndPreferences,
+ PostSignPreferencesRequest,
+ Preferences,
+ PreferencesData
+} from "./generated-model";
+import {jsonProxyEndpoints} from "../endpoints";
+import {setInQueryString} from "../express";
+
+export abstract class ProxyRestRequestBuilder {
+ constructor(public proxyHost: string, protected restEndpoint: string) {
+ }
+
+ protected getProxyUrl(endpoint: string, pafQuery: object | undefined = undefined): URL {
+ let url = new URL(`https://${this.proxyHost}${endpoint}`);
+
+ if (pafQuery) {
+ url = setInQueryString(url, pafQuery)
+ }
+
+ return url
+ }
+
+ getRestUrl(request: T): URL {
+ return this.getProxyUrl(this.restEndpoint, request)
+ }
+}
+
+export class ProxyRestSignPreferencesRequestBuilder extends ProxyRestRequestBuilder {
+
+ constructor(proxyHost: string) {
+ super(proxyHost, jsonProxyEndpoints.signPrefs);
+ }
+
+ buildRequest(identifiers: Identifiers, data: PreferencesData): PostSignPreferencesRequest {
+ return {
+ identifiers,
+ unsignedPreferences: {
+ version: "0.1",
+ data
+ }
+ }
+ }
+}
+
+export class ProxyRestSignPostIdsPrefsRequestBuilder extends ProxyRestRequestBuilder {
+
+ constructor(proxyHost: string) {
+ super(proxyHost, jsonProxyEndpoints.signWrite);
+ }
+
+ buildRequest(identifiers: Identifiers, preferences: Preferences): IdsAndPreferences {
+ return {
+ identifiers,
+ preferences
+ }
+ }
+}
+
+export class ProxyRestVerifyGetIdsPrefsRequestBuilder extends ProxyRestRequestBuilder {
+
+ constructor(proxyHost: string) {
+ super(proxyHost, jsonProxyEndpoints.verifyRead);
+ }
+}
diff --git a/paf-mvp-demo-express/scripts/generate-examples.ts b/paf-mvp-demo-express/scripts/generate-examples.ts
index 4abc9c2f..e51f0987 100644
--- a/paf-mvp-demo-express/scripts/generate-examples.ts
+++ b/paf-mvp-demo-express/scripts/generate-examples.ts
@@ -1,21 +1,22 @@
import {
+ Error,
+ Get3PcResponse,
+ GetIdentityResponse,
GetIdsPrefsRequest,
GetIdsPrefsResponse,
- RedirectGetIdsPrefsRequest,
- RedirectGetIdsPrefsResponse,
GetNewIdRequest,
GetNewIdResponse,
- Get3PcResponse,
- GetIdentityResponse,
Identifier,
+ Identifiers,
+ PostSignPreferencesRequest,
PostIdsPrefsRequest,
- RedirectPostIdsPrefsRequest,
- RedirectPostIdsPrefsResponse,
PostIdsPrefsResponse,
Preferences,
- Identifiers,
- Test3Pc,
- Error
+ RedirectGetIdsPrefsRequest,
+ RedirectGetIdsPrefsResponse,
+ RedirectPostIdsPrefsRequest,
+ RedirectPostIdsPrefsResponse,
+ Test3Pc, IdsAndPreferences
} from "@core/model/generated-model";
import {toIdsCookie, toPrefsCookie, toTest3pcCookie} from "@core/cookies";
import {getTimeStampInSec} from "@core/timestamp";
@@ -23,21 +24,32 @@ import {advertiser, cmp, operator, publisher} from "../src/config";
import path from "path";
import {OperatorClient} from "@operator-client/operator-client";
import {
- GetIdsPrefsRequestBuilder,
- GetNewIdRequestBuilder,
Get3PCRequestBuilder,
GetIdentityRequestBuilder,
+ GetIdsPrefsRequestBuilder,
+ GetNewIdRequestBuilder,
PostIdsPrefsRequestBuilder
-} from "@core/model/request-builders";
+} from "@core/model/operator-request-builders";
import {OperatorApi} from "@operator/operator-api";
-import {GetNewIdResponseBuilder, GetIdsPrefsResponseBuilder, PostIdsPrefsResponseBuilder, Get3PCResponseBuilder, GetIdentityResponseBuilder} from "@core/model/response-builders";
+import {
+ Get3PCResponseBuilder,
+ GetIdentityResponseBuilder,
+ GetIdsPrefsResponseBuilder,
+ GetNewIdResponseBuilder,
+ PostIdsPrefsResponseBuilder
+} from "@core/model/operator-response-builders";
import {Validator} from "jsonschema";
import {publicKeys} from "../src/public-keys";
import * as fs from "fs";
+import {
+ ProxyRestSignPostIdsPrefsRequestBuilder,
+ ProxyRestSignPreferencesRequestBuilder,
+ ProxyRestVerifyGetIdsPrefsRequestBuilder
+} from "@core/model/proxy-request-builders";
const getTimestamp = (dateString: string) => getTimeStampInSec(new Date(dateString))
-const getUrl = (method: "POST"|"GET", url: URL): string => `${method} ${url.pathname}${url.search}\nHost: ${url.host}`
-const getGetUrl = (url: URL): string => getUrl("GET", url)
+const getUrl = (method: "POST" | "GET", url: URL): string => `${method} ${url.pathname}${url.search}\nHost: ${url.host}`
+const getGETUrl = (url: URL): string => getUrl("GET", url)
const getPOSTUrl = (url: URL): string => getUrl("POST", url)
const getRedirect = (url: URL): string => `303 ${url}`
@@ -125,6 +137,14 @@ class Examples {
getIdentityRequest_cmpHttp: string
getIdentityResponse_cmpJson: GetIdentityResponse
+ // **************************** Proxy
+ signPreferencesHttp: string
+ signPreferencesJson: PostSignPreferencesRequest
+ signPostIdsPrefsHttp: string
+ signPostIdsPrefsJson: IdsAndPreferences
+ verifyGetIdsPrefsHttp: string
+ verifyGetIdsPrefs_invalidJson: Error
+
constructor() {
const operatorAPI = new OperatorApi(operator.host, operator.privateKey)
const originalAdvertiserUrl = new URL(`https://${advertiser.host}/news/2022/02/07/something-crazy-happened?utm_content=campaign%20content`)
@@ -138,7 +158,7 @@ class Examples {
this.idJson = operatorAPI.signId("7435313e-caee-4889-8ad7-0acd0114ae3c", getTimestamp("2022/01/18 12:13"));
const cmpClient = new OperatorClient(operator.host, cmp.host, cmp.privateKey, publicKeys)
- this.preferencesJson = cmpClient.buildPreferences([this.idJson], true, getTimestamp("2022/01/18 12:16"))
+ this.preferencesJson = cmpClient.buildPreferences([this.idJson], {use_browsing_for_personalization: true}, getTimestamp("2022/01/18 12:16"))
// **************************** Cookies
this['ids_cookie-prettyJson'] = [this.idJson]
@@ -156,7 +176,7 @@ class Examples {
const getIdsPrefsRequestBuilder = new GetIdsPrefsRequestBuilder(operator.host, cmp.host, cmp.privateKey)
const getIdsPrefsResponseBuilder = new GetIdsPrefsResponseBuilder(operator.host, cmp.privateKey)
this.getIdsPrefsRequestJson = getIdsPrefsRequestBuilder.buildRequest(getTimestamp("2022/01/24 17:19"))
- this.getIdsPrefsRequestHttp = getGetUrl(getIdsPrefsRequestBuilder.getRestUrl(this.getIdsPrefsRequestJson))
+ this.getIdsPrefsRequestHttp = getGETUrl(getIdsPrefsRequestBuilder.getRestUrl(this.getIdsPrefsRequestJson))
this.getIdsPrefsResponse_knownJson = getIdsPrefsResponseBuilder.buildResponse(
advertiser.host,
{
@@ -173,7 +193,7 @@ class Examples {
)
this.redirectGetIdsPrefsRequestJson = getIdsPrefsRequestBuilder.toRedirectRequest(this.getIdsPrefsRequestJson, originalAdvertiserUrl)
- this.redirectGetIdsPrefsRequestHttp = getGetUrl(getIdsPrefsRequestBuilder.getRedirectUrl(this.redirectGetIdsPrefsRequestJson))
+ this.redirectGetIdsPrefsRequestHttp = getGETUrl(getIdsPrefsRequestBuilder.getRedirectUrl(this.redirectGetIdsPrefsRequestJson))
this.redirectGetIdsPrefsResponse_knownJson = getIdsPrefsResponseBuilder.toRedirectResponse(this.getIdsPrefsResponse_knownJson, 200)
this.redirectGetIdsPrefsResponse_knownTxt = getRedirect(getIdsPrefsResponseBuilder.getRedirectUrl(originalAdvertiserUrl, this.redirectGetIdsPrefsResponse_knownJson))
@@ -194,7 +214,7 @@ class Examples {
}, getTimestamp("2022/01/25 09:01:03"))
this.redirectPostIdsPrefsRequestJson = postIdsPrefsRequestBuilder.toRedirectRequest(this.postIdsPrefsRequestJson, originalAdvertiserUrl)
- this.redirectPostIdsPrefsRequestHttp = getGetUrl(postIdsPrefsRequestBuilder.getRedirectUrl(this.redirectPostIdsPrefsRequestJson))
+ this.redirectPostIdsPrefsRequestHttp = getGETUrl(postIdsPrefsRequestBuilder.getRedirectUrl(this.redirectPostIdsPrefsRequestJson))
this.redirectPostIdsPrefsResponseJson = postIdsPrefsResponseBuilder.toRedirectResponse(this.postIdsPrefsResponseJson, 200)
this.redirectPostIdsPrefsResponseTxt = getRedirect(postIdsPrefsResponseBuilder.getRedirectUrl(originalAdvertiserUrl, this.redirectPostIdsPrefsResponseJson))
@@ -202,14 +222,14 @@ class Examples {
const getNewIdRequestBuilder = new GetNewIdRequestBuilder(operator.host, cmp.host, cmp.privateKey)
const getNewIdResponseBuilder = new GetNewIdResponseBuilder(operator.host, operator.privateKey)
this.getNewIdRequestJson = getNewIdRequestBuilder.buildRequest(getTimestamp("2022/03/01 19:04"))
- this.getNewIdRequestHttp = getGetUrl(getNewIdRequestBuilder.getRestUrl(this.getNewIdRequestJson))
+ this.getNewIdRequestHttp = getGETUrl(getNewIdRequestBuilder.getRestUrl(this.getNewIdRequestJson))
this.getNewIdResponseJson = getNewIdResponseBuilder.buildResponse(cmp.host, newId, getTimestamp("2022/03/01 19:04:47"))
// **************************** Verify 3PC
const get3PCRequestBuilder = new Get3PCRequestBuilder(operator.host, cmp.host, cmp.privateKey)
const get3PCResponseBuilder = new Get3PCResponseBuilder(operator.host, operator.privateKey)
- this.get3pcRequestHttp = getGetUrl(get3PCRequestBuilder.getRestUrl())
+ this.get3pcRequestHttp = getGETUrl(get3PCRequestBuilder.getRestUrl())
this.get3pcResponse_supportedJson = get3PCResponseBuilder.buildResponse(this["test_3pc_cookie-prettyJson"]) as Get3PcResponse
this.get3pcResponse_unsupportedJson = get3PCResponseBuilder.buildResponse(undefined) as Error
@@ -217,7 +237,7 @@ class Examples {
// **************************** Identity
const getIdentityRequestBuilder_operator = new GetIdentityRequestBuilder(operator.host, advertiser.host, cmp.privateKey)
const getIdentityResponseBuilder_operator = new GetIdentityResponseBuilder(operator.host, operator.privateKey, operator.name, operator.type)
- this.getIdentityRequest_operatorHttp = getGetUrl(getIdentityRequestBuilder_operator.getRestUrl(undefined))
+ this.getIdentityRequest_operatorHttp = getGETUrl(getIdentityRequestBuilder_operator.getRestUrl(undefined))
this.getIdentityResponse_operatorJson = getIdentityResponseBuilder_operator.buildResponse([
{
publicKey: operator.publicKey,
@@ -229,13 +249,26 @@ class Examples {
// TODO add examples with multiple keys
const getIdentityRequestBuilder_cmp = new GetIdentityRequestBuilder(cmp.host, advertiser.host, cmp.privateKey)
const getIdentityResponseBuilder_cmp = new GetIdentityResponseBuilder(cmp.host, cmp.privateKey, cmp.name, cmp.type)
- this.getIdentityRequest_cmpHttp = getGetUrl(getIdentityRequestBuilder_cmp.getRestUrl(undefined))
+ this.getIdentityRequest_cmpHttp = getGETUrl(getIdentityRequestBuilder_cmp.getRestUrl(undefined))
this.getIdentityResponse_cmpJson = getIdentityResponseBuilder_cmp.buildResponse([
{
publicKey: cmp.publicKey,
start: new Date("2022/01/15 11:50")
}
])
+
+ // **************************** Proxy
+ const signPreferencesRequestBuilder = new ProxyRestSignPreferencesRequestBuilder(cmp.host)
+ this.signPreferencesHttp = getPOSTUrl(signPreferencesRequestBuilder.getRestUrl(undefined)) // Notice is POST url
+ this.signPreferencesJson = signPreferencesRequestBuilder.buildRequest([this.idJson], {use_browsing_for_personalization: true})
+
+ const signPostIdsPrefsRequestBuilder = new ProxyRestSignPostIdsPrefsRequestBuilder(cmp.host)
+ this.signPostIdsPrefsHttp = getPOSTUrl(signPostIdsPrefsRequestBuilder.getRestUrl(undefined)) // Notice is POST url
+ this.signPostIdsPrefsJson = signPostIdsPrefsRequestBuilder.buildRequest([this.idJson], this.preferencesJson)
+
+ const verifyGetIdsPrefsRequestBuilder = new ProxyRestVerifyGetIdsPrefsRequestBuilder(cmp.host)
+ this.verifyGetIdsPrefsHttp = getPOSTUrl(verifyGetIdsPrefsRequestBuilder.getRestUrl(undefined)) // Notice is POST url
+ this.verifyGetIdsPrefs_invalidJson = {message: 'Invalid signature'}
}
}
@@ -247,7 +280,7 @@ class SchemasValidator {
async initValidator(): Promise {
// FIXME use a parameter to validate examples. Or ignore validation
- const inputDir = path.join(__dirname, '..', '..','paf-mvp-core-js', 'json-schemas');
+ const inputDir = path.join(__dirname, '..', '..', 'paf-mvp-core-js', 'json-schemas');
const files = await fs.promises.readdir(inputDir);
const schemas = await Promise.all(files
.map(async (f: string) => JSON.parse(await fs.promises.readFile(path.join(inputDir, f), 'utf-8')))
diff --git a/paf-mvp-demo-express/src/cmp/js/cmp.ts b/paf-mvp-demo-express/src/cmp/js/cmp.ts
index 9222974c..543156b1 100644
--- a/paf-mvp-demo-express/src/cmp/js/cmp.ts
+++ b/paf-mvp-demo-express/src/cmp/js/cmp.ts
@@ -13,26 +13,35 @@ declare global {
}
// Using the CMP backend as a PAF operator proxy
-const proxyBase = `https://${cmp.host}`;
+const proxyHostName = cmp.host;
export const cmpCheck = async () => {
- const pafData = await PAF.refreshIdsAndPreferences({proxyBase, triggerRedirectIfNeeded: true});
+ const pafData = await PAF.refreshIdsAndPreferences({proxyHostName, triggerRedirectIfNeeded: true});
if (pafData === undefined) {
// Will trigger a redirect
return;
}
-
+
const returnedId = pafData.identifiers?.[0]
const hasPersistedId = returnedId?.persisted === undefined || returnedId?.persisted
if (!hasPersistedId || pafData.preferences === undefined) {
const optIn = await window.__promptConsent();
// 1. sign preferences
- const signedPreferences = await PAF.signPreferences({proxyBase}, {identifier: returnedId, optIn})
+ const unsignedPreferences = {
+ version: "0.1",
+ data: {
+ use_browsing_for_personalization: optIn
+ }
+ };
+ const signedPreferences = await PAF.signPreferences({proxyHostName}, {
+ identifiers: pafData.identifiers,
+ unsignedPreferences
+ })
// 2. write
- await PAF.writeIdsAndPref({proxyBase}, {
+ await PAF.writeIdsAndPref({proxyHostName}, {
identifiers: pafData.identifiers,
preferences: signedPreferences
})
diff --git a/paf-mvp-demo-express/src/portal.ts b/paf-mvp-demo-express/src/portal.ts
index b143eebf..b165e3b9 100644
--- a/paf-mvp-demo-express/src/portal.ts
+++ b/paf-mvp-demo-express/src/portal.ts
@@ -4,7 +4,7 @@ import {OperatorClient} from "@operator-client/operator-client";
import {Cookies, fromIdsCookie, fromPrefsCookie} from "@core/cookies";
import {Preferences, RedirectGetIdsPrefsResponse} from "@core/model/generated-model";
import {getPafDataFromQueryString, getRequestUrl, httpRedirect, removeCookie} from "@core/express";
-import {GetIdsPrefsRequestBuilder, PostIdsPrefsRequestBuilder} from "@core/model/request-builders";
+import {GetIdsPrefsRequestBuilder, PostIdsPrefsRequestBuilder} from "@core/model/operator-request-builders";
import {publicKeys} from "./public-keys";
const domainParser = require('tld-extract');
@@ -33,7 +33,7 @@ const getWritePrefsUrl = (identifiers: any, preferences: Preferences, returnUrl:
};
const getWritePrefsUrlFromOptin = (identifiers: any, optIn: boolean, returnUrl: any) => {
- const preferences = client.buildPreferences(identifiers, optIn);
+ const preferences = client.buildPreferences(identifiers, {use_browsing_for_personalization: optIn});
return getWritePrefsUrl(identifiers, preferences, returnUrl);
};
@@ -43,7 +43,7 @@ const tld = domainParser(`https://${portal.host}`).domain
portalApp.get('/', (req, res) => {
const cookies = req.cookies;
- const formatCookie = (value: string|undefined) => value ? JSON.stringify(JSON.parse(value), null, 2) : undefined
+ const formatCookie = (value: string | undefined) => value ? JSON.stringify(JSON.parse(value), null, 2) : undefined
const request = getIdsPrefsRequestBuilder.buildRequest()
const redirectRequest = getIdsPrefsRequestBuilder.toRedirectRequest(request, new URL(writeNewId, `${req.protocol}://${req.get('host')}`))
diff --git a/paf-mvp-demo-express/src/views/advertiser/index.hbs b/paf-mvp-demo-express/src/views/advertiser/index.hbs
index 35d78295..f8ac2f28 100644
--- a/paf-mvp-demo-express/src/views/advertiser/index.hbs
+++ b/paf-mvp-demo-express/src/views/advertiser/index.hbs
@@ -2,7 +2,7 @@
-
+
diff --git a/paf-mvp-frontend/src/lib/paf-lib.ts b/paf-mvp-frontend/src/lib/paf-lib.ts
index 575b1838..db81c1a5 100644
--- a/paf-mvp-frontend/src/lib/paf-lib.ts
+++ b/paf-mvp-frontend/src/lib/paf-lib.ts
@@ -6,15 +6,14 @@ import {
IdsAndOptionalPreferences,
IdsAndPreferences,
PostIdsPrefsRequest,
+ PostSignPreferencesRequest,
Preferences,
- Test3Pc,
} from '@core/model/generated-model';
import {Cookies, getPrebidDataCacheExpiration} from '@core/cookies';
-import {NewPrefs} from '@core/model/model';
-import {jsonEndpoints, proxyEndpoints, proxyUriParams, redirectEndpoints} from '@core/endpoints';
+import {jsonProxyEndpoints, proxyUriParams, redirectProxyEndpoints} from '@core/endpoints';
import {isBrowserKnownToSupport3PC} from '@core/user-agent';
import {QSParam} from '@core/query-string';
-import {fromClientCookieValues, PafStatus, getPafStatus} from '@core/operator-client-commons';
+import {fromClientCookieValues, getPafStatus, PafStatus} from '@core/operator-client-commons';
import {getCookieValue} from '../utils/cookie';
const logger = console;
@@ -60,11 +59,7 @@ const setCookie = (name: string, value: string, expiration: Date) => {
// Update the URL shown in the address bar, without PAF data
const cleanUpUrL = () => history.pushState(null, '', removeUrlParameter(location.href, QSParam.paf));
-const getProxyUrl =
- (proxyBase: string) =>
- (endpoint: string): string => {
- return `${proxyBase}/paf${endpoint}`;
- };
+const getProxyUrl = (proxyHost: string) => (endpoint: string): string => `https://${proxyHost}${endpoint}`;
const saveCookieValue = (cookieName: string, cookieValue: T | undefined): string => {
logger.info(`Operator returned value for ${cookieName}: ${cookieValue !== undefined ? 'YES' : 'NO'}`);
@@ -86,7 +81,7 @@ const removeCookie = (cookieName: string) => {
let thirdPartyCookiesSupported: boolean | undefined;
export interface Options {
- proxyBase: string;
+ proxyHostName: string;
}
export interface RefreshIdsAndPrefsOptions extends Options {
@@ -106,14 +101,14 @@ export type SignPrefsOptions = Options;
* @return ids and preferences or undefined if user is not participating or if values can't be refreshed
*/
export const refreshIdsAndPreferences = async ({
- proxyBase,
+ proxyHostName,
triggerRedirectIfNeeded,
}: RefreshIdsAndPrefsOptions): Promise => {
- const getUrl = getProxyUrl(proxyBase);
+ const getUrl = getProxyUrl(proxyHostName);
const redirectToRead = () => {
logger.info('Redirect to operator');
- const redirectUrl = new URL(getUrl(redirectEndpoints.read));
+ const redirectUrl = new URL(getUrl(redirectProxyEndpoints.read));
redirectUrl.searchParams.set(proxyUriParams.returnUrl, location.href);
redirect(redirectUrl.toString());
};
@@ -136,7 +131,7 @@ export const refreshIdsAndPreferences = async ({
thirdPartyCookiesSupported = false;
// Verify message
- const response = await fetch(getUrl(proxyEndpoints.verifyRedirectRead), {
+ const response = await fetch(getUrl(jsonProxyEndpoints.verifyRead), {
method: 'POST',
body: uriData,
credentials: 'include',
@@ -189,7 +184,7 @@ export const refreshIdsAndPreferences = async ({
logger.info('Browser known to support 3PC: YES');
logger.info('Attempt to read from JSON');
- const readResponse = await fetch(getUrl(jsonEndpoints.read), {credentials: 'include'});
+ const readResponse = await fetch(getUrl(jsonProxyEndpoints.read), {credentials: 'include'});
const operatorData = (await readResponse.json()) as GetIdsPrefsResponse;
const persistedIds = operatorData.body.identifiers?.filter((identifier) => identifier?.persisted !== false);
@@ -212,7 +207,7 @@ export const refreshIdsAndPreferences = async ({
logger.info('Verify 3PC on operator');
// Note: need to include credentials to make sure cookies are sent
- const verifyResponse = await fetch(getUrl(jsonEndpoints.verify3PC), {credentials: 'include'});
+ const verifyResponse = await fetch(getUrl(jsonProxyEndpoints.verify3PC), {credentials: 'include'});
const testOk: Get3PcResponse | Error = (await verifyResponse.json());
// 4. 3d party cookie ok?
@@ -263,10 +258,10 @@ export const refreshIdsAndPreferences = async ({
* @return the written identifiers and preferences
*/
export const writeIdsAndPref = async (
- {proxyBase}: WriteIdsAndPrefsOptions,
+ {proxyHostName}: WriteIdsAndPrefsOptions,
input: IdsAndPreferences
): Promise => {
- const getUrl = getProxyUrl(proxyBase);
+ const getUrl = getProxyUrl(proxyHostName);
const processWriteIdsAndPref = async (): Promise => {
console.log('Attempt to write:');
@@ -282,7 +277,7 @@ export const writeIdsAndPref = async (
console.log('3PC supported');
// 1) sign the request
- const signedResponse = await fetch(getUrl(proxyEndpoints.signWrite), {
+ const signedResponse = await fetch(getUrl(jsonProxyEndpoints.signWrite), {
method: 'POST',
body: JSON.stringify(input),
credentials: 'include',
@@ -290,7 +285,7 @@ export const writeIdsAndPref = async (
const signedData = (await signedResponse.json()) as PostIdsPrefsRequest;
// 2) send
- const response = await fetch(getUrl(jsonEndpoints.write), {
+ const response = await fetch(getUrl(jsonProxyEndpoints.write), {
method: 'POST',
body: JSON.stringify(signedData),
credentials: 'include',
@@ -308,7 +303,7 @@ export const writeIdsAndPref = async (
console.log('3PC not supported: redirect');
// Redirect. Signing of the request will happen on the backend proxy
- const redirectUrl = new URL(getUrl(redirectEndpoints.write));
+ const redirectUrl = new URL(getUrl(redirectProxyEndpoints.write));
redirectUrl.searchParams.set(proxyUriParams.returnUrl, location.href);
redirectUrl.searchParams.set(proxyUriParams.message, JSON.stringify(input));
@@ -333,10 +328,11 @@ export const writeIdsAndPref = async (
* @param input the main identifier of the web user, and the optin value
* @return the signed Preferences
*/
-export const signPreferences = async ({proxyBase}: SignPrefsOptions, input: NewPrefs): Promise => {
- const getUrl = getProxyUrl(proxyBase);
+export const signPreferences = async ({proxyHostName}: SignPrefsOptions, input: PostSignPreferencesRequest): Promise => {
+ const getUrl = getProxyUrl(proxyHostName);
- const signedResponse = await fetch(getUrl(proxyEndpoints.signPrefs), {
+ // TODO use ProxyRestSignPreferencesRequestBuilder
+ const signedResponse = await fetch(getUrl(jsonProxyEndpoints.signPrefs), {
method: 'POST',
body: JSON.stringify(input),
credentials: 'include',
diff --git a/paf-mvp-operator-client-express/src/operator-backend-client.ts b/paf-mvp-operator-client-express/src/operator-backend-client.ts
index 2eb04453..06f26ce3 100644
--- a/paf-mvp-operator-client-express/src/operator-backend-client.ts
+++ b/paf-mvp-operator-client-express/src/operator-backend-client.ts
@@ -15,7 +15,7 @@ import {
} from "@core/express";
import {isBrowserKnownToSupport3PC} from "@core/user-agent";
import {PublicKeys} from "@core/crypto/keys";
-import {GetIdsPrefsRequestBuilder} from "@core/model/request-builders";
+import {GetIdsPrefsRequestBuilder} from "@core/model/operator-request-builders";
export enum RedirectType {
http = "http",
diff --git a/paf-mvp-operator-client-express/src/operator-client-proxy.ts b/paf-mvp-operator-client-express/src/operator-client-proxy.ts
index 5bef6063..0e575fcc 100644
--- a/paf-mvp-operator-client-express/src/operator-client-proxy.ts
+++ b/paf-mvp-operator-client-express/src/operator-client-proxy.ts
@@ -1,9 +1,13 @@
import {Express, Request, Response} from "express";
import cors, {CorsOptions} from "cors";
import {OperatorClient} from "./operator-client";
-import {Error, IdsAndPreferences, RedirectGetIdsPrefsResponse} from "@core/model/generated-model";
-import {NewPrefs} from "@core/model/model";
-import {jsonEndpoints, proxyEndpoints, proxyUriParams, redirectEndpoints} from "@core/endpoints";
+import {
+ Error,
+ IdsAndPreferences,
+ PostSignPreferencesRequest,
+ RedirectGetIdsPrefsResponse
+} from "@core/model/generated-model";
+import {jsonProxyEndpoints, proxyUriParams, redirectProxyEndpoints} from "@core/endpoints";
import {httpRedirect} from "@core/express";
import {PublicKeys} from "@core/crypto/keys";
import {fromDataToObject} from "@core/query-string";
@@ -11,7 +15,7 @@ import {
Get3PCRequestBuilder,
GetIdsPrefsRequestBuilder,
PostIdsPrefsRequestBuilder
-} from "@core/model/request-builders";
+} from "@core/model/operator-request-builders";
/**
* Get return URL parameter, otherwise set response code 400
@@ -60,32 +64,62 @@ export const addOperatorClientProxyEndpoints = (app: Express, operatorHost: stri
// ************************************************************************************************************ JSON
// *****************************************************************************************************************
- app.get(`/paf${jsonEndpoints.read}`, cors(corsOptions), (req, res) => {
+ app.get(jsonProxyEndpoints.read, cors(corsOptions), (req, res) => {
const getIdsPrefsRequestJson = getIdsPrefsRequestBuilder.buildRequest()
const url = getIdsPrefsRequestBuilder.getRestUrl(getIdsPrefsRequestJson)
httpRedirect(res, url.toString(), 302)
});
- app.post(`/paf${jsonEndpoints.write}`, cors(corsOptions), (req, res) => {
+ app.post(jsonProxyEndpoints.write, cors(corsOptions), (req, res) => {
const url = postIdsPrefsRequestBuilder.getRestUrl()
- // Note: the message is assumed to be signed with proxyEndpoints.signWrite beforehand
+ // Note: the message is assumed to be signed with jsonProxyEndpoints.signWrite beforehand
// /!\ Notice return code 307!
httpRedirect(res, url.toString(), 307)
});
- app.get(`/paf${jsonEndpoints.verify3PC}`, cors(corsOptions), (req, res) => {
+ app.get(jsonProxyEndpoints.verify3PC, cors(corsOptions), (req, res) => {
const url = get3PCRequestBuilder.getRestUrl()
httpRedirect(res, url.toString(), 302)
});
+ // *****************************************************************************************************************
+ // ******************************************************************************************** JSON - SIGN & VERIFY
+ // *****************************************************************************************************************
+ app.post(jsonProxyEndpoints.verifyRead, cors(corsOptions), (req, res) => {
+ const message = fromDataToObject(req.body);
+
+ if (!message.response) {
+ // FIXME do something smart in case of error
+ throw message.error
+ }
+
+ const verification = client.verifyReadResponseSignature(message.response);
+ if (!verification) {
+ const error: Error = {message: 'verification failed'}
+ res.send(error)
+ } else {
+ res.send(message.response)
+ }
+ });
+
+ app.post(jsonProxyEndpoints.signPrefs, cors(corsOptions), (req, res) => {
+ const {identifiers, unsignedPreferences} = JSON.parse(req.body as string) as PostSignPreferencesRequest;
+ res.send(client.buildPreferences(identifiers, unsignedPreferences.data))
+ });
+
+ app.post(jsonProxyEndpoints.signWrite, cors(corsOptions), (req, res) => {
+ const message = JSON.parse(req.body as string) as IdsAndPreferences;
+ res.send(postIdsPrefsRequestBuilder.buildRequest(message))
+ });
+
// *****************************************************************************************************************
// ******************************************************************************************************* REDIRECTS
// *****************************************************************************************************************
- app.get(`/paf${redirectEndpoints.read}`, cors(corsOptions), (req, res) => {
+ app.get(redirectProxyEndpoints.read, cors(corsOptions), (req, res) => {
const returnUrl = getReturnUrl(req, res);
@@ -101,12 +135,13 @@ export const addOperatorClientProxyEndpoints = (app: Express, operatorHost: stri
});
- app.get(`/paf${redirectEndpoints.write}`, cors(corsOptions), (req, res) => {
+ app.get(redirectProxyEndpoints.write, cors(corsOptions), (req, res) => {
const returnUrl = getReturnUrl(req, res);
const input = getMessageObject(req, res);
if (input && returnUrl) {
+ // Note: the message is assumed to be signed with jsonProxyEndpoints.signWrite beforehand
const postIdsPrefsRequestJson = postIdsPrefsRequestBuilder.toRedirectRequest(
postIdsPrefsRequestBuilder.buildRequest(input),
@@ -118,36 +153,4 @@ export const addOperatorClientProxyEndpoints = (app: Express, operatorHost: stri
httpRedirect(res, url.toString(), 302)
}
});
-
- // *****************************************************************************************************************
- // *************************************************************************************************** SIGN & VERIFY
- // *****************************************************************************************************************
-
- app.post(`/paf${proxyEndpoints.verifyRedirectRead}`, cors(corsOptions), (req, res) => {
- const message = fromDataToObject(req.body);
-
- if (!message.response) {
- // FIXME do something smart in case of error
- throw message.error
- }
-
- const verification = client.verifyReadResponseSignature(message.response);
- if (!verification) {
- const error: Error = {message: 'verification failed'}
- res.send(error)
- } else {
- console.debug(message.response)
- res.send(message.response)
- }
- });
-
- app.post(`/paf${proxyEndpoints.signPrefs}`, cors(corsOptions), (req, res) => {
- const {identifier, optIn} = JSON.parse(req.body as string) as NewPrefs;
- res.send(client.buildPreferences([identifier], optIn))
- });
-
- app.post(`/paf${proxyEndpoints.signWrite}`, cors(corsOptions), (req, res) => {
- const message = JSON.parse(req.body as string) as IdsAndPreferences;
- res.send(postIdsPrefsRequestBuilder.buildRequest(message))
- });
}
diff --git a/paf-mvp-operator-client-express/src/operator-client.ts b/paf-mvp-operator-client-express/src/operator-client.ts
index 3bcbbe2e..830b44ee 100644
--- a/paf-mvp-operator-client-express/src/operator-client.ts
+++ b/paf-mvp-operator-client-express/src/operator-client.ts
@@ -18,12 +18,10 @@ export class OperatorClient {
return this.readVerifier.verify(this.publicKeys[message.sender], message)
}
- buildPreferences(identifiers: Identifiers, optIn: boolean, timestamp = new Date().getTime()): Preferences {
+ buildPreferences(identifiers: Identifiers, data: { use_browsing_for_personalization: boolean; }, timestamp = new Date().getTime()): Preferences {
const unsignedPreferences: UnsignedData = {
version: "0.1",
- data: {
- use_browsing_for_personalization: optIn
- },
+ data,
source: {
domain: this.host,
timestamp,
@@ -36,6 +34,7 @@ export class OperatorClient {
...rest,
source: {
...source,
+ // FIXME use identifiers to sign preferences!
signature: this.prefsSigner.sign(this.ecdsaKey, unsignedPreferences)
}
};
diff --git a/paf-mvp-operator-express/src/operator-api.ts b/paf-mvp-operator-express/src/operator-api.ts
index 53dc6850..bc3a529d 100644
--- a/paf-mvp-operator-express/src/operator-api.ts
+++ b/paf-mvp-operator-express/src/operator-api.ts
@@ -27,7 +27,7 @@ import {
Get3PCResponseBuilder,
GetIdsPrefsResponseBuilder,
PostIdsPrefsResponseBuilder
-} from "@core/model/response-builders";
+} from "@core/model/operator-response-builders";
const domainParser = require('tld-extract');