Skip to content

Latest commit

 

History

History
759 lines (679 loc) · 32.3 KB

ad-server-implementation.md

File metadata and controls

759 lines (679 loc) · 32.3 KB

Ad Server Implementation

Goal of the document

This document provides the guidance and the requirements for implementing Prebid Addressability Framework (PAF) on an Ad Server.

Overview

For performance purposes, it is recommended to implement the Addressable Content Service directly in the Ad Server. Therefore, the following document is focused on this setup.

Compared to a usual setup with an Ad Server, the Publisher or the Provider of the Ad Server has to implement additional features to enable PAF.

Those features are:

  • The generation of the Seeds of the Addressable Contents;
  • The emission of Transmissions to the Ad Network;
  • The support of the Audit Log for the user;
  • The exposition of an Identity endpoint.

The following diagram introduces an overview of this setup:

sequenceDiagram
    participant User
    participant Publisher
    participant AdServer as Ad Server
    participant DSP

    User->>Publisher: Ask for a webpage
    Publisher->>AdServer: Offer Inventory<br />with Prebid SSO Data
    AdServer->>AdServer: Generate Seeds for <br /> the Addressable Contents
    AdServer->>DSP: Generate and send a <br /> Transmission Request
    DSP->>AdServer: Provide an Addressable Contents <br /> and a Transmission Response
    AdServer->>Publisher: Provide the Addressable Contents
    Publisher->>User: Display the Addressable Contents <br /> and their respective Audit Logs
Loading

Offer inventory with Prebid SSO Data

PAF doesn't standardize the API of the Ad Server for offering inventory because each Ad Server has an existing and specific API. However, to implement PAF, the Ad Server need to implement new features in the existing endpoints called by the Publisher website to offer inventory and get back Addressable Contents.

To understand the steps, it is important to overview how to generate the data and the relationships between them:

  • A Publisher offers multiple placements - one for each for Addressable Contents - via an Ad Server.
  • An Ad Server generates one Seed for each Addressable Content
  • An Ad Server generates and sends one Transaction Request per Seed and Supplier
  • A Supplier generates and sends one Transaction Response per Transaction Request
flowchart LR
    subgraph AdServer
        Seed-- 1-n -->TR[Transmission Request]
    end

    subgraph Publisher
        Placement-- 1-1 -->Seed
    end

     subgraph Supplier
         TR[Transmission Request]-- 1-1 -->TRp[Transmission Response]
    end
Loading

Step 1: Require the Prebid SSO Cookies

The Ad Server API has to be extended to require the Prebid SSO cookies of the Publisher website for a given user in addition to the inventory information. Those cookies contain the Prebid SSO Data. As a reminder, Prebid SSO Data are Pseudonymous-Identifiers and Preferences of the user.

flowchart LR
    Publisher-- Send Placements info <br /> with Prebid SSO Cookies -->AdServer[Ad Server]
Loading

Step 2: Deserialize the Prebid SSO Data

The Ad Server can then deserialize the cookie for having the Prebid SSO Data.

Here is the structure of a Pseudonymous-Identifier:

Field Type Details
version Number The version of PAF used.
type String The type of Pseudonymous-Identifier. For now, there is only one: "prebid_id".
value String The Pseudonymous-Identifier value in UTF-8.
source Source object The Source contains all the data for identifying and trusting the Operator that generated the Pseudonymous-Identifier.
FieldTypeDetails
domainStringThe domain of the Operator.
timestampIntegerThe timestamp of the signature.
signatureStringEncoded signature in UTF-8 of the Operator.

Here is the structure of the Preferences:

Field Type Details
version Number The Prebid SSO version used.
data Dictionary The keys are strings and represent the name of the preferences.
The values represent the value of the preference.
For now there is only one preference named "optin" and its value is a boolean.
source Source object The source contains the data for identifying and trusting the CMP that signed lastly the Preferences.
FieldTypeDetails
domainStringThe domain of the CMP.
timestampIntegerThe timestamp of the signature.
signatureStringEncoded signature in UTF-8 of the CMP.

Step 3: Generate the Seeds

The Seed is the association of the Pseudonymous-Identifiers and the Preferences of the user for a given Addressable Content. The Ad Server must generate this association and sign it. Once generated, a Contracting Party - including the Ad Server- in possession of a Seed can share it with another party via Transmissions (see the next steps). A Seed is signed by the Ad Server for audit purposes.

Here is the composition of a Seed:

Field Type Details
version Number The PAF version used.
transaction_id String A GUID in a String format dedicated to the share of the Prebid SSO data for one Addressable Content.
publisher String The domain name of the Publisher that displays the Addressable Content
source Source object The source contains data for identifying and trusting the Publisher.
FieldTypeDetails
domainStringThe domain of the Root Party (Publisher in most of the cases).
timestampIntegerThe timestamp of the signature.
signatureStringEncoded signature in UTF-8 of the Root Party/Publisher.

Here is a JSON example of the Seed:

{
    "version": 0,
    "transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
    "publisher": "publisher.com",
    "source": {
        "domain": "adserver-company.com",
        "timestamp": 1639582000,
        "signature": "12345_signature"
    }
}

The Ad Server must sign the Seeds ("source"."signature"). The Elliptic Curve Digital Signature Algorithm (ECDSA) is used for this purpose. NIST P-256 coupled with the hash algorithm SHA-256 is applied on a built string relying on the Seed data and the Prebid SSO Data.

Here is how to build the UTF-8 string for then generating the signature:

seed.source.domain + '\u2063' + 
seed.source.timestamp + '\u2063' + 
seed.transaction_id + '\u2063' + 
seed.publisher + '\u2063' + 
data.identifiers[0].source.signature + '\u2063' +
data.identifiers[1].source.signature + '\u2063' +
... + '\u2063' + 
data.identifiers[n].source.signature + '\u2063' +
data.preferences.source.signature

Step 4: Send Transmission Requests with Inventory

Once the Seeds are generated (one per Addressable Content), the Ad Server shares the Prebid SSO Data via Transmissions with placement data to Contracting Parties. In the case of an existing custom communication (a.k.a not OpenRTB), Transmission Requests must be included in the existing communication and bound structurally or by references to the data of the impressions (also named Addressable Content). One Transmission Requests per Supplier and Seed.

flowchart LR
    TR[Transmission Request]-- n-1 ---Supplier
    TR-- 1-1 ---Seed
Loading

A Transmission Request is composed as followed:

Field Type Details
version Number The PAF version used.
seed Seed object A Seed object contains all the Prebid SSO Data gathered and signed by the Publisher concerning the user.
parents Array of Transmission Results A list of Transmission Results that participate to a chain of Transmissions and make this Transmission possible.
source Source object The source object contains data for identifying the Sender of the Transmission.
FieldTypeDetails
domainStringThe domain of the Sender.
timestampIntegerThe timestamp of the signature.
signatureStringEncoded signature in UTF-8 of the Tranmission sender.

The Transmission Request list is always associated to Prebid SSO Data that has been used for generating the Seed. Here is a hypothetical structure of it that we name data in the following example:

Field Type Details
preferences Preferences object The Preferences of the user.
identifiers Array of Pseudonymous-Identifier objects The Pseudonymous-Identifiers of the user. For now, it only contains a Prebid ID.

Similar to the Seed, each Transmission Request contains a signature for audit purposes. It used the data of the Transaction Request and the domain name of the Receiver with the same cryptographic algorithm.

Here is how to build the UTF-8 string:

transmission_request_receiver_domain        + '\u2063' +
transmission_request.source.domain          + '\u2063' + 
transmission_request.source.timestamp       + '\u2063' + 
seed.source.signature

In the communication, the Transmission Requests must be associated to the Prebid SSO Data. Depending on the existing structure of the communication, it makes sense to have a shared structure for the Prebid SSO Data and multiple Transmissions referring to it.

Here is an example that must be adapted to the existing API of the Ad Server:

{
    "data": {
        "identifiers": [
            {
                "version": 0,
                "type": "prebid_id",
                "value": "7435313e-caee-4889-8ad7-0acd0114ae3c",
                "source": {
                    "domain": "operator0.com",
                    "timestamp": 1639580000,
                    "signature": "12345_signature"
                }
            }
        ],
        "preferences": {
            "version": 0,
            "data": { 
                "opt_in": true 
            },
            "source": {
                "domain": "cmp1.com",
                "timestamp": 1639581000,
                "signature": "12345_signature"
            }
        }
    },
    "transmissions": [
        {
            "version": 0,
            "seed": {
                "version": 0,
                "transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
                "publisher": "publisher.com",
                "source": {
                    "domain": "publisher.com",
                    "timestamp": 1639582000,
                    "signature": "12345_signature"
                }
            },
            "source": {
                "domain": "dsp1.com",
                "timestamp": 1639581000,
                "signature": "12345_signature"
            },
            "parents": []
        },
        {
            "version": 0,
            "seed": {
                "version": 0,
                "transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
                "publisher": "publisher.com",
                "source": {
                    "domain": "publisher.com",
                    "timestamp": 1639582000,
                    "signature": "12345_signature"
                }
            },
            "source": {
                "domain": "dps1.com",
                "timestamp": 1639581000,
                "signature": "12345_signature"
            },
            "parents": []
        }
    ]
}

Step 5: Receive Transmission Responses

The Receiver of the Transmission Requests answers back with Transmission Responses. Those Transmission Responses are included in the usual response of an inventory offer. Each Transmission Response doesn't necessary have a Transmission Response. Indeed, only the Transmission Response that participated in the generation of the Addressable Content is required.

A Transmission Response is composed as followed:

Field Type Details
version Number The version of the PAF used.
transaction_id String A GUID dedicated to the Addressable Content. It allows associating the Transmission Responses to Transmission Request
receiver String The domain name of the DSP.
status String Equals "success" if the DSP signed the Transmission and returns it to the sender.
Equals "error_bad_request" if the receiver doesn't understand or see inconsistency in the Transmission Request.
Equals "error_cannot_process" if the receiver cannot handle the Transmission Request properly.
details String In case of an error status, the DSP can provide details concerning the error.
children Array of Transmission Results An empty array as we consider that the DSP doesn't share the Prebid SSO Data to its suppliers via new transmissions.
source Source object The source contains all the data for identifying the DSP and verifying the Transmission.

Therefore, here is an example of Transmission Responses that must be adapted to the existing API:

{
    "transmissions": [
        {
            "version": 0,
            "transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
            "receiver": "dsp1.com",
            "status": "success",
            "details": "",
            "source": {
                "domain": "dsp1.com",
                "timestamp": 1639589531,
                "signature": "12345_signature"
            },
            "children": []
        },
        {
            "version": 0,
            "transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743"8,
            "receiver": "dsp1.com",
            "status": "success",
            "details": "",
            "source": {
                "domain": "dsp1.com",
                "timestamp": 1639589531,
                "signature": "12345_signature"
            },
            "children": []
        }
    ]
}

Step 6: Generate the Audit Log

Once the Ad Server has selected the supplier that will display the Addressable Content, it must generate the Audit Log based on the related Transmission Response and the Prebid SSO Data.

The Audit Log has the following structure:

Field Type Detail
data Prebid SSO Data Object List the Pseudonymous-Identifiers and the Preferences of the user.
seed Seed Object The Seed object is the association of an Addressable Content to the Prebid SSO Data.
transmissions List of Transmission Results A list of Transmission Results

As described, the Audit Log contains a list of Transmission Results. The Transmission Results are built thanks to the data within the received Transmission Response that participates in the Addressable Content. The required data are the status and the signature of the Transmission Response and its children.

Here is the structure of a Transmission Result:

Field Type Details
version Number The version of the PAF used.
receiver String The domain name of the DSP.
status String Equals "success" if the DSP signed the Transmission and returns it to the sender.
Equals "error_bad_request" if the receiver doesn't understand or see inconsistency in the Transmission Request.
Equals "error_cannot_process" if the receiver failed to use the data of the Transmission Request properly.
details String In case of an error status, the DSP can provide details concerning the error.
source Source object The source contains all the data for identifying the DSP and verifying the Transmission.

Let's take an example of a transformation to Transmission Results. Here is a received Transmission Response that helps to generate the Addressable Content:

{
    "version": 0,
    "transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
    "receiver": "ssp1.com",
    "status": "success",
    "details": "",
    "source": {
        "domain": "ssp1.com",
        "timestamp": 1639589531,
        "signature": "12345_signature"
    },
    "children": [
        {
            "receiver": "ssp2.com",
            "status": "success",
            "details": "",
            "source": {
                "domain": "ssp2.com",
                "timestamp": 1639589531,
                "signature": "12345_signature"
            }
        },
        {
            "receiver": "dsp.com",
            "status": "success",
            "details": "",
            "source": {
                "domain": "dsp.com",
                "timestamp": 1639589531,
                "signature": "12345_signature"
            }
        }
    ]
}

Here is the associated list of Transmission Results:

{
    "transmissions": [
        {
            "version": 0,
            "receiver": "ssp1.com",
            "status": "success",
            "details": "",
            "source": {
                "domain": "ssp1.com",
                "timestamp": 1639589531,
                "signature": "12345_signature"
            }
        },
        {
            "version": 0,
            "receiver": "ssp2.com",
            "status": "success",
            "details": "",
            "source": {
                "domain": "ssp2.com",
                "timestamp": 1639589531,
                "signature": "12345_signature"
            }
        },
        {
            "version": 0,
            "receiver": "dsp.com",
            "status": "success",
            "details": "",
            "source": {
                "domain": "dsp.com",
                "timestamp": 1639589531,
                "signature": "12345_signature"
            }
        }
    ]
}

After this transformation, it is possible to generate the Audit Log. Here is an example:

{
    "data": {
        "identifiers": [
            {
                "version": 0,
                "type": "prebid_id",
                "value": "7435313e-caee-4889-8ad7-0acd0114ae3c",
                "source": {
                    "domain": "operotor0.com",
                    "timestamp": 1639589531,
                    "signature": "12345_signature"
                }
            }
        ],
        "preferences": {
            "version": 0,
            "data": { 
                "opt_in": true 
            },
            "source": {
                "domain": "cmp1.com",
                "timestamp": 1639589531,
                "signature": "12345_signature"
            }
        }
    },
    "seed": {
        "version": 0,
        "transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
        "publisher": "publisher.com",
        "source": {
          "domain": "ad-server.com",
          "timestamp": 1639589531,
          "signature": "12345_signature"
        }
    },
    "transmissions": [
        {
            "version": 0,
            "receiver": "ssp1.com",
            "status": "success",
            "details": "",
            "source": {
                "domain": "ssp1.com",
                "timestamp": 1639589531,
                "signature": "12345_signature"
            }
        },
        {
            "version": 0,
            "receiver": "ssp2.com",
            "status": "success",
            "details": "",
            "source": {
                "domain": "ssp2.com",
                "timestamp": 1639589531,
                "signature": "12345_signature"
            }
        },
        {
            "version": 0,
            "receiver": "dsp.com",
            "status": "success",
            "details": "",
            "source": {
                "domain": "dps.com",
                "timestamp": 1639589531,
                "signature": "12345_signature"
            }
        }
    ]
}

Step 7: Display the Addressable Content and make the Audit Log available

Finally, the Addressable Content can be displayed to the user on the Publisher page. An Audit Button (ideally per Addressable Content) is available for displaying the Audit UI.

Transmissions with OpenRTB

If the used protocol for offering the inventory is OpenRTB, the Ad Server needs to respect the following for integrating PAF.

The OpenRTB Bid Request

In step Step 4, the Ad Server must share the Prebid SSO Data in the extensions of the Bid Request:

First, The Transmission Request object in an OpenRTB request keeps the same structure. It is embedded in the ext field of each impression. It is reachable at imp.ext.paf.

Second, the Pseudonymous-Identifiers and the Preferences structures change in the OpenRTB request to take the advantage of the Extended Identifiers. One eid per Pseudonymous-Identifier (and Preferences). It is reachable at user.ext.eids.

Comparing to the solution without OpenRTB:

  1. The Pseudonymous-Identifier value is stored in the eids.id field.
  2. The eids.atype is set to 1 because the ID is tied to a specific browser for nom.
  3. The version, type, and source fields are gathered in an extension of the eid: eids.ext.paf.
  4. The Preferences are attached as an extention of the eid.

Example of a OpenRTB Bid Request

{
    "id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
    "at": 1, 
    "cur": [ "USD" ],
    "imp": [
        {
            "id": "1",
            "bidfloor": 0.03,
            "banner": {
                "h": 250,
                "w": 300,
                "pos": 0
            },
            "ext": {
                "paf": {
                    "version": 0,
                    "seed": {
                        "version": 0,
                        "transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
                        "publisher": "publisher.com",
                        "source": {
                            "domain": "publisher0.com",
                            "timestamp": 1639589531,
                            "signature": "12345_signature"
                        }
                    },
                    "parents": []
                }
            }
        }
    ],
    "site": {
        "id": "102855",
        "cat": [ "IAB3-1" ],
        "domain": "www.publisher.com",
        "page": "http://www.publisher.com/1234.html ",
        "publisher": {
            "id": "8953",
            "name": "publisher.com",
            "cat": ["IAB3-1"],
            "domain": "publisher.com"
        }
    },
    "device": {
        "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2",
        "ip": "123.145.167.10"
    },
    "user": {
        "id": "55816b39711f9b5acf3b90e313ed29e51665623f",
         "ext":
         {
            "eids": 
            [
                {
                    "source": "paf",
                    "uids": [
                        {
                            "atype": 1,
                            "id": "7435313e-caee-4889-8ad7-0acd0114ae3c",
                            "ext": 
                            {
                                "version": 0,
                                "type": "prebid_id",
                                "source": 
                                {
                                    "domain": "operotor0.com",
                                    "timestamp": 1639589531,
                                    "signature": "12345_signature"
                                }
                            }
                        }
                    ],
                    "ext": {
                        "preferences": {
                            "version": 0,
                            "data": { 
                                "opt_in": true 
                            },
                            "source": {
                                "domain": "cmp1.com",
                                "timestamp": 1639589531,
                                "signature": "12345_signature"
                            }
                        }
                    }
                }
            ]
        }
    }
}

The OpenRTB Bid Response

In step 5, the bidder (named Receiver in PAF Transmission) send back a OpenRTB Bid Response. Each bid is associated with a Transaction Response. The Transaction has the same structure explained in Step 5 and is reachable in the ext field of a bid (full path: seatbid[].bid.ext.paf).

Here is an example:

{
    "id": "1234567890",
    "bidid": "abc1123",
    "cur": "USD",
    "seatbid": [
        {
            "seat": "512",
            "bid": [
                {
                    "id": "1",
                    "impid": "1",
                    "price": 9.43,
                    "nurl": "http://adserver.com/winnotice?impid=102",
                    "iurl": "http://adserver.com/pathtosampleimage",
                    "adomain": [ "advertiserdomain.com" ],
                    "cid": "campaign111",
                    "crid": "creative112",
                    "attr": [ 1, 2, 3, 4, 5, 6, 7, 12 ],
                    "ext": {
                        "paf": {
                            "version": 0,
                            "receiver": "dsp1.com",
                            "status": "success",
                            "details": "",
                            "source": {
                                "domain": "dsp1.com",
                                "timestamp": 1639589531,
                                "signature": "12345_signature"
                            },
                            "children": []
                        }
                    }
                }
            ]
        }
    ]
}