From 81250a405465b271e38435d6d414581bf3b27940 Mon Sep 17 00:00:00 2001 From: Olivier Chirouze Date: Thu, 3 Mar 2022 15:49:39 +0100 Subject: [PATCH 1/7] Clean up operator backend proxy endpoints --- paf-mvp-core-js/src/endpoints.ts | 36 +++++---- paf-mvp-demo-express/src/cmp/js/cmp.ts | 10 +-- .../src/views/advertiser/index.hbs | 2 +- paf-mvp-frontend/src/lib/paf-lib.ts | 56 +++++++------ .../src/operator-client-proxy.ts | 78 +++++++++---------- 5 files changed, 94 insertions(+), 88 deletions(-) diff --git a/paf-mvp-core-js/src/endpoints.ts b/paf-mvp-core-js/src/endpoints.ts index 8412f3d7..c144bd73 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 = { + verifyRedirectRead: `${proxyPrefix}/verify/redirectRead`, + signWrite: `${proxyPrefix}/sign/write`, + signPrefs: `${proxyPrefix}/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-demo-express/src/cmp/js/cmp.ts b/paf-mvp-demo-express/src/cmp/js/cmp.ts index 4066d201..035b9bd4 100644 --- a/paf-mvp-demo-express/src/cmp/js/cmp.ts +++ b/paf-mvp-demo-express/src/cmp/js/cmp.ts @@ -8,16 +8,16 @@ declare const PAF: { } // 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 @@ -27,10 +27,10 @@ export const cmpCheck = async () => { Please confirm if you want to opt-in, otherwise click cancel`) // 1. sign preferences - const signedPreferences = await PAF.signPreferences({proxyBase}, {identifier: returnedId, optIn}) + const signedPreferences = await PAF.signPreferences({proxyHostName}, {identifier: returnedId, optIn}) // 2. write - await PAF.writeIdsAndPref({proxyBase}, { + await PAF.writeIdsAndPref({proxyHostName}, { identifiers: pafData.identifiers, preferences: signedPreferences }) 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 a6cde52a..83740d79 100644 --- a/paf-mvp-frontend/src/lib/paf-lib.ts +++ b/paf-mvp-frontend/src/lib/paf-lib.ts @@ -7,12 +7,12 @@ import { 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 { isBrowserKnownToSupport3PC } from '@core/user-agent'; -import { QSParam } from '@core/query-string'; -import { fromClientCookieValues, PafStatus, getPafStatus } from '@core/operator-client-commons'; +import {Cookies, getPrebidDataCacheExpiration} from '@core/cookies'; +import {NewPrefs} from '@core/model/model'; +import {jsonProxyEndpoints, proxyUriParams, redirectProxyEndpoints} from '@core/endpoints'; +import {isBrowserKnownToSupport3PC} from '@core/user-agent'; +import {QSParam} from '@core/query-string'; +import {fromClientCookieValues, getPafStatus, PafStatus} from '@core/operator-client-commons'; const logger = console; @@ -37,7 +37,7 @@ const removeUrlParameter = (url: string, parameter: string) => { const parts = queryString.split(/[&;]/g); // Reverse iteration as may be destructive - for (let i = parts.length; i-- > 0; ) { + for (let i = parts.length; i-- > 0;) { // Idiom for string.startsWith if (parts[i].lastIndexOf(prefix, 0) !== -1) { parts.splice(i, 1); @@ -59,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 = (proxyBase: string) => (endpoint: string): string => `https://${proxyBase}${endpoint}`; const saveCookieValue = (cookieName: string, cookieValue: T | undefined): string => { logger.info(`Operator returned value for ${cookieName}: ${cookieValue !== undefined ? 'YES' : 'NO'}`); @@ -85,7 +81,7 @@ const removeCookie = (cookieName: string) => { let thirdPartyCookiesSupported: boolean | undefined; export interface Options { - proxyBase: string; + proxyHostName: string; } export interface RefreshIdsAndPrefsOptions extends Options { @@ -105,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, - triggerRedirectIfNeeded, -}: RefreshIdsAndPrefsOptions): Promise => { - const getUrl = getProxyUrl(proxyBase); + proxyHostName, + triggerRedirectIfNeeded, + }: RefreshIdsAndPrefsOptions): Promise => { + 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()); }; @@ -135,7 +131,7 @@ export const refreshIdsAndPreferences = async ({ thirdPartyCookiesSupported = false; // Verify message - const response = await fetch(getUrl(proxyEndpoints.verifyRedirectRead), { + const response = await fetch(getUrl(jsonProxyEndpoints.verifyRedirectRead), { method: 'POST', body: uriData, credentials: 'include', @@ -188,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); @@ -211,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 = (await verifyResponse.json()) as Test3Pc; // 4. 3d party cookie ok? @@ -225,7 +221,7 @@ export const refreshIdsAndPreferences = async ({ setCookie(Cookies.identifiers, PafStatus.NOT_PARTICIPATING, getPrebidDataCacheExpiration()); setCookie(Cookies.preferences, PafStatus.NOT_PARTICIPATING, getPrebidDataCacheExpiration()); - return { identifiers: operatorData.body.identifiers }; + return {identifiers: operatorData.body.identifiers}; } logger.info('3PC verification OK: NO'); thirdPartyCookiesSupported = false; @@ -262,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:'); @@ -281,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', @@ -289,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', @@ -307,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)); @@ -332,10 +328,10 @@ 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: NewPrefs): Promise => { + const getUrl = getProxyUrl(proxyHostName); - const signedResponse = await fetch(getUrl(proxyEndpoints.signPrefs), { + 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-client-proxy.ts b/paf-mvp-operator-client-express/src/operator-client-proxy.ts index 5bef6063..837070f2 100644 --- a/paf-mvp-operator-client-express/src/operator-client-proxy.ts +++ b/paf-mvp-operator-client-express/src/operator-client-proxy.ts @@ -3,7 +3,7 @@ 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 {jsonProxyEndpoints, proxyUriParams, redirectProxyEndpoints} from "@core/endpoints"; import {httpRedirect} from "@core/express"; import {PublicKeys} from "@core/crypto/keys"; import {fromDataToObject} from "@core/query-string"; @@ -60,32 +60,63 @@ 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.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(jsonProxyEndpoints.signPrefs, cors(corsOptions), (req, res) => { + const {identifier, optIn} = JSON.parse(req.body as string) as NewPrefs; + res.send(client.buildPreferences([identifier], optIn)) + }); + + 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 +132,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 +150,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)) - }); } From bce0ab16bc84d5154c47c817f0522e8c9f36b5d4 Mon Sep 17 00:00:00 2001 From: Olivier Chirouze Date: Thu, 3 Mar 2022 18:09:52 +0100 Subject: [PATCH 2/7] Add version in "proxy specific" endpoints --- paf-mvp-core-js/src/endpoints.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/paf-mvp-core-js/src/endpoints.ts b/paf-mvp-core-js/src/endpoints.ts index c144bd73..3cac12b3 100644 --- a/paf-mvp-core-js/src/endpoints.ts +++ b/paf-mvp-core-js/src/endpoints.ts @@ -16,9 +16,9 @@ export const jsonEndpoints = { // Endpoints exposed by the operator proxy const proxyPrefix = '/paf-proxy' export const jsonProxyEndpoints = { - verifyRedirectRead: `${proxyPrefix}/verify/redirectRead`, - signWrite: `${proxyPrefix}/sign/write`, - signPrefs: `${proxyPrefix}/sign/prefs`, + verifyRedirectRead: `${proxyPrefix}/v1/verify/redirectRead`, + signWrite: `${proxyPrefix}/v1/sign/write`, + signPrefs: `${proxyPrefix}/v1/sign/prefs`, read: `${proxyPrefix}${jsonEndpoints.read}`, write: `${proxyPrefix}${jsonEndpoints.write}`, verify3PC: `${proxyPrefix}${jsonEndpoints.verify3PC}`, From af98a1325c06ad45ab933388b6455d9a6d819505 Mon Sep 17 00:00:00 2001 From: Olivier Chirouze Date: Fri, 4 Mar 2022 10:28:49 +0100 Subject: [PATCH 3/7] Proxy "sign preferences": refactor for better message model --- paf-mvp-core-js/src/model/generated-model.ts | 28 +++++++++++++++---- .../scripts/generate-examples.ts | 18 ++++++++++-- paf-mvp-demo-express/src/cmp/js/cmp.ts | 11 +++++++- paf-mvp-demo-express/src/portal.ts | 4 +-- paf-mvp-frontend/src/lib/paf-lib.ts | 4 +-- .../src/operator-client-proxy.ts | 13 +++++---- .../src/operator-client.ts | 7 ++--- 7 files changed, 63 insertions(+), 22 deletions(-) diff --git a/paf-mvp-core-js/src/model/generated-model.ts b/paf-mvp-core-js/src/model/generated-model.ts index 0b201333..fd89e86d 100644 --- a/paf-mvp-core-js/src/model/generated-model.ts +++ b/paf-mvp-core-js/src/model/generated-model.ts @@ -72,8 +72,10 @@ export interface _ { "ids-and-optional-preferences"?: IdsAndOptionalPreferences; "ids-and-preferences"?: IdsAndPreferences; "message-base"?: MessageBase; + "new-unsigned-preferences"?: NewUnsignedPreferences; "post-ids-prefs-request"?: PostIdsPrefsRequest; "post-ids-prefs-response"?: PostIdsPrefsResponse; + "preferences-data"?: PreferencesData; preferences?: Preferences; "redirect-get-ids-prefs-request"?: RedirectGetIdsPrefsRequest; "redirect-get-ids-prefs-response"?: RedirectGetIdsPrefsResponse; @@ -165,14 +167,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 */ @@ -256,6 +262,16 @@ export interface MessageBase { timestamp: Timestamp; signature: Signature; } +/** + * A list of identifiers and a new (unsigned) value for preferences + */ +export interface NewUnsignedPreferences { + unsignedPreferences?: { + version: Version; + data: PreferencesData; + }; + identifiers: Identifier[]; +} /** * POST /v1/ids-prefs request */ diff --git a/paf-mvp-demo-express/scripts/generate-examples.ts b/paf-mvp-demo-express/scripts/generate-examples.ts index 4abc9c2f..05f6b37e 100644 --- a/paf-mvp-demo-express/scripts/generate-examples.ts +++ b/paf-mvp-demo-express/scripts/generate-examples.ts @@ -15,7 +15,7 @@ import { Preferences, Identifiers, Test3Pc, - Error + Error, NewUnsignedPreferences } from "@core/model/generated-model"; import {toIdsCookie, toPrefsCookie, toTest3pcCookie} from "@core/cookies"; import {getTimeStampInSec} from "@core/timestamp"; @@ -125,6 +125,9 @@ class Examples { getIdentityRequest_cmpHttp: string getIdentityResponse_cmpJson: GetIdentityResponse + // **************************** Proxy + signPreferencesJson: NewUnsignedPreferences + 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 +141,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] @@ -236,6 +239,17 @@ class Examples { start: new Date("2022/01/15 11:50") } ]) + + // **************************** Proxy + this.signPreferencesJson = { + identifiers: [this.idJson], + unsignedPreferences: { + version: "0.1", + data: { + use_browsing_for_personalization: true + } + } + } } } diff --git a/paf-mvp-demo-express/src/cmp/js/cmp.ts b/paf-mvp-demo-express/src/cmp/js/cmp.ts index 035b9bd4..3a30f114 100644 --- a/paf-mvp-demo-express/src/cmp/js/cmp.ts +++ b/paf-mvp-demo-express/src/cmp/js/cmp.ts @@ -27,7 +27,16 @@ export const cmpCheck = async () => { Please confirm if you want to opt-in, otherwise click cancel`) // 1. sign preferences - const signedPreferences = await PAF.signPreferences({proxyHostName}, {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({proxyHostName}, { diff --git a/paf-mvp-demo-express/src/portal.ts b/paf-mvp-demo-express/src/portal.ts index b143eebf..71e53a49 100644 --- a/paf-mvp-demo-express/src/portal.ts +++ b/paf-mvp-demo-express/src/portal.ts @@ -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-frontend/src/lib/paf-lib.ts b/paf-mvp-frontend/src/lib/paf-lib.ts index 83740d79..397c3eaf 100644 --- a/paf-mvp-frontend/src/lib/paf-lib.ts +++ b/paf-mvp-frontend/src/lib/paf-lib.ts @@ -2,7 +2,7 @@ import UAParser from 'ua-parser-js'; import { GetIdsPrefsResponse, IdsAndOptionalPreferences, - IdsAndPreferences, + IdsAndPreferences, NewUnsignedPreferences, PostIdsPrefsRequest, Preferences, Test3Pc, @@ -328,7 +328,7 @@ 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 ({proxyHostName}: SignPrefsOptions, input: NewPrefs): Promise => { +export const signPreferences = async ({proxyHostName}: SignPrefsOptions, input: NewUnsignedPreferences): Promise => { const getUrl = getProxyUrl(proxyHostName); const signedResponse = await fetch(getUrl(jsonProxyEndpoints.signPrefs), { 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 837070f2..c6b464f0 100644 --- a/paf-mvp-operator-client-express/src/operator-client-proxy.ts +++ b/paf-mvp-operator-client-express/src/operator-client-proxy.ts @@ -1,8 +1,12 @@ 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 { + Error, + IdsAndPreferences, + NewUnsignedPreferences, + RedirectGetIdsPrefsResponse +} from "@core/model/generated-model"; import {jsonProxyEndpoints, proxyUriParams, redirectProxyEndpoints} from "@core/endpoints"; import {httpRedirect} from "@core/express"; import {PublicKeys} from "@core/crypto/keys"; @@ -97,14 +101,13 @@ export const addOperatorClientProxyEndpoints = (app: Express, operatorHost: stri const error: Error = {message: 'verification failed'} res.send(error) } else { - console.debug(message.response) res.send(message.response) } }); app.post(jsonProxyEndpoints.signPrefs, cors(corsOptions), (req, res) => { - const {identifier, optIn} = JSON.parse(req.body as string) as NewPrefs; - res.send(client.buildPreferences([identifier], optIn)) + const {identifiers, unsignedPreferences} = JSON.parse(req.body as string) as NewUnsignedPreferences; + res.send(client.buildPreferences(identifiers, unsignedPreferences.data)) }); app.post(jsonProxyEndpoints.signWrite, cors(corsOptions), (req, res) => { 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) } }; From a5694f30edf98921735b4d1d4f9bd1983bd755ce Mon Sep 17 00:00:00 2001 From: Olivier Chirouze Date: Fri, 4 Mar 2022 16:53:01 +0100 Subject: [PATCH 4/7] Introduce ProxyRestRequestBuilder to contact the operator proxy - prefix request builders to "operator - " - generate examples for proxy endpoints --- paf-mvp-core-js/src/endpoints.ts | 2 +- ...ilders.ts => operator-request-builders.ts} | 0 ...lders.ts => operator-response-builders.ts} | 0 .../src/model/proxy-request-builders.ts | 67 +++++++++++++++ .../scripts/generate-examples.ts | 83 ++++++++++++------- paf-mvp-demo-express/src/portal.ts | 2 +- paf-mvp-frontend/src/lib/paf-lib.ts | 3 +- .../src/operator-backend-client.ts | 2 +- .../src/operator-client-proxy.ts | 4 +- paf-mvp-operator-express/src/operator-api.ts | 2 +- 10 files changed, 126 insertions(+), 39 deletions(-) rename paf-mvp-core-js/src/model/{request-builders.ts => operator-request-builders.ts} (100%) rename paf-mvp-core-js/src/model/{response-builders.ts => operator-response-builders.ts} (100%) create mode 100644 paf-mvp-core-js/src/model/proxy-request-builders.ts diff --git a/paf-mvp-core-js/src/endpoints.ts b/paf-mvp-core-js/src/endpoints.ts index 3cac12b3..aea77240 100644 --- a/paf-mvp-core-js/src/endpoints.ts +++ b/paf-mvp-core-js/src/endpoints.ts @@ -16,7 +16,7 @@ export const jsonEndpoints = { // Endpoints exposed by the operator proxy const proxyPrefix = '/paf-proxy' export const jsonProxyEndpoints = { - verifyRedirectRead: `${proxyPrefix}/v1/verify/redirectRead`, + verifyRead: `${proxyPrefix}/v1/verify/read`, signWrite: `${proxyPrefix}/v1/sign/write`, signPrefs: `${proxyPrefix}/v1/sign/prefs`, read: `${proxyPrefix}${jsonEndpoints.read}`, 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..5afa5d97 --- /dev/null +++ b/paf-mvp-core-js/src/model/proxy-request-builders.ts @@ -0,0 +1,67 @@ +import { + GetIdsPrefsResponse, + Identifiers, + IdsAndPreferences, + NewUnsignedPreferences, + 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): NewUnsignedPreferences { + 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 05f6b37e..301aa9a4 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, + NewUnsignedPreferences, PostIdsPrefsRequest, - RedirectPostIdsPrefsRequest, - RedirectPostIdsPrefsResponse, PostIdsPrefsResponse, Preferences, - Identifiers, - Test3Pc, - Error, NewUnsignedPreferences + RedirectGetIdsPrefsRequest, + RedirectGetIdsPrefsResponse, + RedirectPostIdsPrefsRequest, + RedirectPostIdsPrefsResponse, + Test3Pc } 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}` @@ -126,7 +138,12 @@ class Examples { getIdentityResponse_cmpJson: GetIdentityResponse // **************************** Proxy + signPreferencesHttp: string signPreferencesJson: NewUnsignedPreferences + signPostIdsPrefsHttp: string + signPostIdsPrefsJson: NewUnsignedPreferences + verifyGetIdsPrefsHttp: string + verifyGetIdsPrefs_invalidJson: Error constructor() { const operatorAPI = new OperatorApi(operator.host, operator.privateKey) @@ -159,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, { @@ -176,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)) @@ -197,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)) @@ -205,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 @@ -220,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, @@ -232,7 +249,7 @@ 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, @@ -241,15 +258,17 @@ class Examples { ]) // **************************** Proxy - this.signPreferencesJson = { - identifiers: [this.idJson], - unsignedPreferences: { - version: "0.1", - data: { - use_browsing_for_personalization: true - } - } - } + 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'} } } @@ -261,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/portal.ts b/paf-mvp-demo-express/src/portal.ts index 71e53a49..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'); diff --git a/paf-mvp-frontend/src/lib/paf-lib.ts b/paf-mvp-frontend/src/lib/paf-lib.ts index 397c3eaf..c18357e4 100644 --- a/paf-mvp-frontend/src/lib/paf-lib.ts +++ b/paf-mvp-frontend/src/lib/paf-lib.ts @@ -131,7 +131,7 @@ export const refreshIdsAndPreferences = async ({ thirdPartyCookiesSupported = false; // Verify message - const response = await fetch(getUrl(jsonProxyEndpoints.verifyRedirectRead), { + const response = await fetch(getUrl(jsonProxyEndpoints.verifyRead), { method: 'POST', body: uriData, credentials: 'include', @@ -331,6 +331,7 @@ export const writeIdsAndPref = async ( export const signPreferences = async ({proxyHostName}: SignPrefsOptions, input: NewUnsignedPreferences): Promise => { const getUrl = getProxyUrl(proxyHostName); + // TODO use ProxyRestSignPreferencesRequestBuilder const signedResponse = await fetch(getUrl(jsonProxyEndpoints.signPrefs), { method: 'POST', body: JSON.stringify(input), 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 c6b464f0..25ddae9a 100644 --- a/paf-mvp-operator-client-express/src/operator-client-proxy.ts +++ b/paf-mvp-operator-client-express/src/operator-client-proxy.ts @@ -15,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 @@ -88,7 +88,7 @@ export const addOperatorClientProxyEndpoints = (app: Express, operatorHost: stri // ***************************************************************************************************************** // ******************************************************************************************** JSON - SIGN & VERIFY // ***************************************************************************************************************** - app.post(jsonProxyEndpoints.verifyRedirectRead, cors(corsOptions), (req, res) => { + app.post(jsonProxyEndpoints.verifyRead, cors(corsOptions), (req, res) => { const message = fromDataToObject(req.body); if (!message.response) { 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'); From 473acf09ad28bba6171bbb43a829a03f511608f7 Mon Sep 17 00:00:00 2001 From: Olivier Chirouze Date: Tue, 8 Mar 2022 10:28:49 +0100 Subject: [PATCH 5/7] [generate-model] fix to avoid having duplicated types --- paf-mvp-core-js/scripts/generate-model-from-schemas.ts | 2 ++ 1 file changed, 2 insertions(+) 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 From de5da017bf95a8fb9812d0681013a7833367eb1a Mon Sep 17 00:00:00 2001 From: Olivier Chirouze Date: Tue, 8 Mar 2022 10:32:50 +0100 Subject: [PATCH 6/7] [model] rename PostSignPreferencesRequest, remove duplicate types, add missing comments --- paf-mvp-core-js/src/model/generated-model.ts | 66 +++++++------------ .../src/model/proxy-request-builders.ts | 6 +- .../scripts/generate-examples.ts | 8 +-- paf-mvp-frontend/src/lib/paf-lib.ts | 4 +- .../src/operator-client-proxy.ts | 4 +- 5 files changed, 36 insertions(+), 52 deletions(-) diff --git a/paf-mvp-core-js/src/model/generated-model.ts b/paf-mvp-core-js/src/model/generated-model.ts index fd89e86d..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 */ @@ -72,9 +64,9 @@ export interface _ { "ids-and-optional-preferences"?: IdsAndOptionalPreferences; "ids-and-preferences"?: IdsAndPreferences; "message-base"?: MessageBase; - "new-unsigned-preferences"?: NewUnsignedPreferences; "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; @@ -87,6 +79,7 @@ export interface _ { source?: Source; "test-3pc"?: Test3Pc; timestamp?: Timestamp; + "unsigned-preferences"?: UnsignedPreferences; version?: Version; } /** @@ -132,8 +125,8 @@ export interface GetIdentityResponse { * Public key string value */ key: string; - start: Timestamp1; - end?: Timestamp2; + start: Timestamp; + end?: Timestamp; }[]; } /** @@ -227,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 */ @@ -262,16 +236,6 @@ export interface MessageBase { timestamp: Timestamp; signature: Signature; } -/** - * A list of identifiers and a new (unsigned) value for preferences - */ -export interface NewUnsignedPreferences { - unsignedPreferences?: { - version: Version; - data: PreferencesData; - }; - identifiers: Identifier[]; -} /** * POST /v1/ids-prefs request */ @@ -292,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 */ @@ -299,6 +277,9 @@ export interface RedirectGetIdsPrefsRequest { returnUrl: ReturnUrl; request: GetIdsPrefsRequest; } +/** + * GET /v1/redirect/get-ids-prefs response + */ export interface RedirectGetIdsPrefsResponse { code: ResponseCode; response?: GetIdsPrefsResponse; @@ -311,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/proxy-request-builders.ts b/paf-mvp-core-js/src/model/proxy-request-builders.ts index 5afa5d97..9e9fd69d 100644 --- a/paf-mvp-core-js/src/model/proxy-request-builders.ts +++ b/paf-mvp-core-js/src/model/proxy-request-builders.ts @@ -2,7 +2,7 @@ import { GetIdsPrefsResponse, Identifiers, IdsAndPreferences, - NewUnsignedPreferences, + PostSignPreferencesRequest, Preferences, PreferencesData } from "./generated-model"; @@ -28,13 +28,13 @@ export abstract class ProxyRestRequestBuilder { } } -export class ProxyRestSignPreferencesRequestBuilder extends ProxyRestRequestBuilder { +export class ProxyRestSignPreferencesRequestBuilder extends ProxyRestRequestBuilder { constructor(proxyHost: string) { super(proxyHost, jsonProxyEndpoints.signPrefs); } - buildRequest(identifiers: Identifiers, data: PreferencesData): NewUnsignedPreferences { + buildRequest(identifiers: Identifiers, data: PreferencesData): PostSignPreferencesRequest { return { identifiers, unsignedPreferences: { diff --git a/paf-mvp-demo-express/scripts/generate-examples.ts b/paf-mvp-demo-express/scripts/generate-examples.ts index 301aa9a4..e51f0987 100644 --- a/paf-mvp-demo-express/scripts/generate-examples.ts +++ b/paf-mvp-demo-express/scripts/generate-examples.ts @@ -8,7 +8,7 @@ import { GetNewIdResponse, Identifier, Identifiers, - NewUnsignedPreferences, + PostSignPreferencesRequest, PostIdsPrefsRequest, PostIdsPrefsResponse, Preferences, @@ -16,7 +16,7 @@ import { RedirectGetIdsPrefsResponse, RedirectPostIdsPrefsRequest, RedirectPostIdsPrefsResponse, - Test3Pc + Test3Pc, IdsAndPreferences } from "@core/model/generated-model"; import {toIdsCookie, toPrefsCookie, toTest3pcCookie} from "@core/cookies"; import {getTimeStampInSec} from "@core/timestamp"; @@ -139,9 +139,9 @@ class Examples { // **************************** Proxy signPreferencesHttp: string - signPreferencesJson: NewUnsignedPreferences + signPreferencesJson: PostSignPreferencesRequest signPostIdsPrefsHttp: string - signPostIdsPrefsJson: NewUnsignedPreferences + signPostIdsPrefsJson: IdsAndPreferences verifyGetIdsPrefsHttp: string verifyGetIdsPrefs_invalidJson: Error diff --git a/paf-mvp-frontend/src/lib/paf-lib.ts b/paf-mvp-frontend/src/lib/paf-lib.ts index c18357e4..4063f92d 100644 --- a/paf-mvp-frontend/src/lib/paf-lib.ts +++ b/paf-mvp-frontend/src/lib/paf-lib.ts @@ -2,7 +2,7 @@ import UAParser from 'ua-parser-js'; import { GetIdsPrefsResponse, IdsAndOptionalPreferences, - IdsAndPreferences, NewUnsignedPreferences, + IdsAndPreferences, PostSignPreferencesRequest, PostIdsPrefsRequest, Preferences, Test3Pc, @@ -328,7 +328,7 @@ 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 ({proxyHostName}: SignPrefsOptions, input: NewUnsignedPreferences): Promise => { +export const signPreferences = async ({proxyHostName}: SignPrefsOptions, input: PostSignPreferencesRequest): Promise => { const getUrl = getProxyUrl(proxyHostName); // TODO use ProxyRestSignPreferencesRequestBuilder 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 25ddae9a..0e575fcc 100644 --- a/paf-mvp-operator-client-express/src/operator-client-proxy.ts +++ b/paf-mvp-operator-client-express/src/operator-client-proxy.ts @@ -4,7 +4,7 @@ import {OperatorClient} from "./operator-client"; import { Error, IdsAndPreferences, - NewUnsignedPreferences, + PostSignPreferencesRequest, RedirectGetIdsPrefsResponse } from "@core/model/generated-model"; import {jsonProxyEndpoints, proxyUriParams, redirectProxyEndpoints} from "@core/endpoints"; @@ -106,7 +106,7 @@ export const addOperatorClientProxyEndpoints = (app: Express, operatorHost: stri }); app.post(jsonProxyEndpoints.signPrefs, cors(corsOptions), (req, res) => { - const {identifiers, unsignedPreferences} = JSON.parse(req.body as string) as NewUnsignedPreferences; + const {identifiers, unsignedPreferences} = JSON.parse(req.body as string) as PostSignPreferencesRequest; res.send(client.buildPreferences(identifiers, unsignedPreferences.data)) }); From 14605ab416b2edd89bfee8dc6a0b41a59133ed87 Mon Sep 17 00:00:00 2001 From: Olivier Chirouze Date: Tue, 8 Mar 2022 10:40:17 +0100 Subject: [PATCH 7/7] [paf-lib] rename proxyhost --- paf-mvp-frontend/src/lib/paf-lib.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/paf-mvp-frontend/src/lib/paf-lib.ts b/paf-mvp-frontend/src/lib/paf-lib.ts index 248d210e..db81c1a5 100644 --- a/paf-mvp-frontend/src/lib/paf-lib.ts +++ b/paf-mvp-frontend/src/lib/paf-lib.ts @@ -8,14 +8,13 @@ import { PostIdsPrefsRequest, PostSignPreferencesRequest, Preferences, - Test3Pc, } from '@core/model/generated-model'; import {Cookies, getPrebidDataCacheExpiration} from '@core/cookies'; import {jsonProxyEndpoints, proxyUriParams, redirectProxyEndpoints} from '@core/endpoints'; import {isBrowserKnownToSupport3PC} from '@core/user-agent'; import {QSParam} from '@core/query-string'; import {fromClientCookieValues, getPafStatus, PafStatus} from '@core/operator-client-commons'; -import { getCookieValue } from '../utils/cookie'; +import {getCookieValue} from '../utils/cookie'; const logger = console; @@ -60,7 +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 => `https://${proxyBase}${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'}`);