出价和竞价 (B&A) 服务是一组面向广告买方和卖方的服务,可在可信执行环境 (TEE) 中运行,以协助进行 Protected Audience (PA) 竞价。本开发者指南介绍了卖方如何与 Chrome PA 竞价集成以进行 B&A。
演示
这些步骤可总结如下:
- 调用
getInterestGroupAdAuctionData()
以从浏览器获取加密载荷 - 调用
fetch('https://your-ad-server.example')
,并将包含加密载荷的统一竞价请求发送到您的 SAS - 从 SAS 调用 SFE 的
SelectAd()
操作以运行 B&A 竞价 - 将 B&A 竞价结果及其响应的哈希值返回给页面
- 在浏览器中调用
runAdAuction()
以运行单卖家、混合模式或多卖家 PA 竞价,并将服务器端 B&A 竞价结果传入调用
获取加密的广告竞价数据
为了获取运行服务器端 B&A 竞价所需的数据,发布商页面上的卖方的 JavaScript 代码会调用 navigator.getInterestGroupAdAuctionData()
。
const adAuctionData = await navigator.getInterestGroupAdAuctionData({
seller: 'https://ssp.example', // Required
requestSize: 51200,
coordinatorOrigin: 'https://publickeyservice.pa.gcp.privacysandboxservices.com/',
perBuyerConfig: {
'https://dsp-x.example': { targetSize: 8192 },
'https://dsp-y.example': { targetSize: 8192 }
}
});
const { requestId, request } = adAuctionData;
字段 | 说明 |
---|---|
seller |
必需。进行竞价的卖家的来源。此值必须与稍后 runAdAuction() 调用中的 seller 值一致。
|
requestSize |
可选。设置所有买方数据的载荷大小上限。如需了解详情,请参阅说明文档中的请求大小部分。 |
perBuyerConfig |
可选。为每个买方设置配置,还控制哪些买方参与 B&A 竞价。
如果 |
targetSize |
如果设置了 requestSize ,则为可选。如果在 perBuyerConfig 中设置了买方来源,但未设置 requestSize ,则此字段为必填字段。设置该买方数据的载荷大小上限。如需了解详情,请参阅该说明文档的请求大小部分。 |
coordinatorOrigin |
可选,但最终将变为必填。如果未设置,则默认为 https://publickeyservice.pa.gcp.privacysandboxservices.com 。设置用于提取用于加密载荷的密钥的协调者。如需了解详情,请参阅说明文档的协调器部分。 |
进行调用时,浏览器会读取 perBuyerConfig
中列出的买方的兴趣群组,并对买方数据进行加密。这些买方数据包含要用于出价的跨网站信息,并且无法在 TEE 之外解密。对于载荷优化,载荷中仅包含兴趣群体名称、可信出价信号键和浏览器信号。
在 getInterestGroupAdAuctionData()
调用返回的广告竞价数据对象中,requestId
字符串和加密的 request
字节数组可用。
稍后在调用 runAdAuction()
以在浏览器中完成竞价时,系统会使用 requestId
字符串。加密的 request
载荷会作为统一竞价请求的一部分发送到卖方广告服务。
如需查看此调用的示例,请参阅本地测试应用的卖方 JavaScript 代码。
将统一竞价请求发送到 SAS
统一竞价请求是指包含明文内容相关广告竞价载荷和 PA B&A 竞价载荷的请求。PA B&A 竞价载荷是浏览器在 getInterestGroupAdAuctionData()
调用中生成的加密 request
数据。此请求会发送到 SAS,SAS 会协调内容相关竞价和 PA B&A 竞价。
fetch('https://ssp.example/ad-auction', {
method: 'POST',
adAuctionHeaders: true,
body: JSON.stringify({
contextualAuctionPayload: { somePayload },
protectedAudienceAuctionPayload: encodeBinaryData(request)
}),
});
如需将请求发送到 SAS,系统会从该页面调用 fetch()
:
- 该调用必须包含
adAuctionHeaders: true
选项,该选项会指示浏览器在稍后调用runAdAuction()
以在浏览器中完成竞价时验证此调用的响应。 - 提取请求的来源必须与向
getInterestGroupAdAuctionData()
和runAdAuction()
调用提供的seller
来源一致。
调用的正文包含:
- SAS 用于运行内容相关竞价的明文内容相关竞价载荷。
- 加密的 Protected Audience 竞价载荷,由 SAS 发送到 SFE 以运行服务器端 B&A 竞价。
如需查看此调用的示例,请参阅本地测试应用的卖方 JavaScript 代码。
Base64 编码和解码
从 getInterestGroupAdAuctionData()
调用返回的加密 request
载荷是 Uint8Array
的实例,这是一种 JSON 无法处理的数据类型。如需以 JSON 格式发送字节数组,您可以对二进制数据应用 base64 编码,将其转换为字符串。
JavaScript 浏览器 API 在 window
上提供了 atob()
和 btoa()
函数,用于在二进制数据和 base64 编码的 ASCII 字符串之间进行转换。(atob
表示 ASCII 转二进制,btoa
表示二进制转 ASCII)。
调用 btoa()
将二进制数据编码为 base64 编码字符串的代码如下所示:
function encodeBinaryData(data) {
return btoa(String.fromCharCode.apply(null, data));
}
此 fetch
调用返回的加密 B&A 竞价结果也采用 base64 编码,因此您需要将其解码回二进制数据。调用 atob()
将 base64 编码的 ASCII 字符串解码为二进制数据:
function decodeBase64String(base64string) {
return new Uint8Array(
atob(base64string)
.split('')
.map((char) => char.charCodeAt(0))
);
}
不过,采用 base64 编码的字符串通常比原始数据大约 33%。如果您想进一步缩短延迟时间,请使用 JSON 以外的格式发送二进制数据。
调用 SFE 的 SelectAd
以运行 B&A 竞价
卖方广告服务从网页收到统一竞价请求后,系统会先运行内容相关竞价,以确定内容相关竞价胜出方,并收集要传入 PA B&A 竞价的买方信号。然后,通过使用请求载荷从 SAS 调用 SFE 的 SelectAd
操作来发起 B&A 竞价。请注意,第 2 步中页面向 SAS 发出的请求中的某些元数据会转发到 SFE。
构建 SelectAdRequest
载荷
SelectAd
调用的请求载荷可以构建如下所示:
const selectAdRequest = {
auction_config: {
seller: 'https://ssp.example',
auction_signals: '{"testKey":"someValue"}',
seller_signals: '{"testKey":"someValue"}',
buyer_list: [
'https://dsp-x.example',
'https://dsp-y.example',
],
per_buyer_config: {
'https://dsp-x.example': { buyer_signals: '{"testKey": "someValue"}' },
'https://dsp-y.example': { buyer_signals: '{"testKey": "someValue"}' },
},
},
client_type: 'CLIENT_TYPE_BROWSER',
protected_auction_ciphertext: decodeBase64string(request)
};
请注意,如果来自浏览器的加密广告竞价数据采用 base64 编码,则在使用 gRPC 向 SFE 发送请求时,需要将其解码回二进制数据。如果使用 HTTP 发送请求,则加密的广告竞价数据可以保持其 base64 编码形式。
如需查看 SelectAd
请求中定义的其他字段,请参阅 SelectAdRequest
的 proto 定义。
为混合模式和组件竞价设置顶级卖方字段
如果卖方正在运行混合模式竞价,或者作为组件卖方参与多卖家竞价,则需要在请求中定义 top_level_seller
字段。
如果您是混合模式卖家,top_level_seller
值为您的来源:
const selectAdRequest = {
auction_config: {
seller: 'https://ssp-mix.example',
top_level_seller: 'https://ssp-mix.example',
}
}
如果您是组件卖家,top_level_seller
值为多卖家竞价的顶级卖家:
const selectAdRequest = {
auction_config: {
seller: 'https://ssp-mix.example',
top_level_seller: 'https://ssp-top.example',
}
}
调用 SFE 的 SelectAd
您可以使用 gRPC 或 HTTP 从 SAS 调用 SFE。
gRPC 调用
使用 Node 中的 Express 和 gRPC 客户端向 SFE 发出的 gRPC 请求如下所示:
import grpc from '@grpc/grpc-js';
// Load proto definition
const packageDefinition = protoLoader.loadSync(protoPath, { keepCase: true, enums: String });
const {
privacy_sandbox: {
bidding_auction_servers: { SellerFrontEnd }
}
} = grpc.loadPackageDefinition(packageDefinition);
// Instantiate the gRPC client
const sfeGrpcClient = new SellerFrontEnd('192.168.84.104:50067', grpc.credentials.createInsecure());
// Send SelectAd request
sfeGrpcClient.selectAd(selectAdRequest,(error, response) => {
// Handle SFE response
});
您可以在 本地测试应用代码库中找到 SFE 客户端的 proto 定义。
对 Envoy 代理的 HTTP 调用
发送给 SFE 的 HTTP POST 请求会发送到 /v1/selectAd
路径,如下所示:
fetch('https://ssp-ba.example/sfe/v1/selectAd', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(selectAdRequest),
});
转发元数据
应将网页对 SAS 的调用中以下元数据添加到 SAS 对 SFE 的 SelectAd
调用中:
Accept-Language
User-Agent
- IP 地址
将元数据发送到 SFE 时,必须使用以下非标准标头,因为 gRPC 可能会更改 User-Agent
标头:
X-Accept-Language
X-User-Agent
X-BnA-Client-IP
以下示例展示了如何在 Node 中使用 gRPC 客户端通过 Express 转发元数据:
sellerAdService.post('/ad-auction', (req, res) => {
// …
const metadata = new grpc.Metadata();
metadata.add('X-Accept-Language', req.header('Accept-Language'));
metadata.add('X-User-Agent', req.header('User-Agent'));
metadata.add('X-BnA-Client-IP', req.ip);
const sfeGrpcClient = createSfeGrpcClient();
sfeGrpcClient.selectAd(selectAdRequest, metadata, callbackFn);
})
以下示例展示了如何使用 HTTP 调用转发元数据:
sellerAdService.post('/ad-auction', (req, res) => {
// …
fetch('https://ssp-ba.example/sfe/v1/selectAd', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Accept-Language': req.header('Accept-Language'),
'X-User-Agent': req.header('User-Agent'),
'X-BnA-Client-IP': req.ip
},
body: JSON.stringify(selectAdRequest)
});
})
服务器协调的多卖家竞价
如果您是运行服务器协调的多卖方竞价的顶级卖方,则系统会先向 SFE 发出 GetComponentAuctionCiphertexts
调用,然后再发出 SelectAd
调用。响应包含发送到组件卖方广告服务的重新加密的组件竞价载荷。返回的组件 B&A 广告竞价结果会提供给顶级卖方的 SFE 的 SelectAd
调用。
如需了解详情,请参阅 GitHub 上的多卖家说明文档。
将 B&A 竞价结果返回到页面
B&A 竞价结束后,系统会将加密的竞价结果返回给 SAS,SAS 会使用加密的竞价结果响应第 2 步中页面中的统一竞价请求。在发送给该网页的 SAS 响应中,Ad-Auction-Result
响应标头中设置了加密竞价结果的 base64url 编码 SHA-256 哈希。浏览器会在客户端完成竞价时使用此哈希来验证载荷。
在 Node 中,使用 base64 编码创建 SHA-256 哈希的代码如下所示:
import { createHash } from 'crypto';
createHash('sha256')
.update(binaryData, 'base64')
.digest('base64url');
在响应标头中附加哈希并将竞价结果返回到页面,如下所示:
sellerAdService.post('/ad-auction', (req, res) => {
// …
sfeGrpcClient.selectAd(selectAdRequest, metadata, (error, response) => {
const { auction_result_ciphertext } = response;
const ciphertextShaHash = createHash('sha256')
.update(auction_result_ciphertext, 'base64')
.digest('base64url');
res.set('Ad-Auction-Result', ciphertextShaHash);
res.json({
protectedAudienceAuctionResult: encodeBinaryData(auction_result_ciphertext),
contextualAuctionResult: getContextualAuctionResult()
});
});
})
由于这是对第 2 步中从网页发出的统一竞价请求的响应,因此响应中还包含内容相关竞价结果。
您可以通过重复标头或分隔哈希来在 Ad-Auction-Result
中添加多个哈希。以下两个响应标头是等效的:
Ad-Auction-Result: ungWv48Bz-pBQUDeXa4iI7ADYaOWF3qctBD_YfIAFa0=,9UTB-u-WshX66Xqz5DNCpEK9z-x5oCS5SXvgyeoRB1k=
Ad-Auction-Result: ungWv48Bz-pBQUDeXa4iI7ADYaOWF3qctBD_YfIAFa0=
Ad-Auction-Result: 9UTB-u-WshX66Xqz5DNCpEK9z-x5oCS5SXvgyeoRB1k=
如需查看此调用的示例,请参阅本地测试应用的卖方服务器代码。
调用 runAdAuction()
以完成竞价
SAS 返回的统一竞价响应包含加密的 B&A 竞价结果。此载荷会传递到 runAdAuction()
调用,以便在浏览器中完成竞价。第 1 步中 getInterestGroupAdAuctionData()
调用的 requestId
值也会传入竞价。
// Get the encrypted ad auction data (Step #1)
const { requestId, request } = navigator.getInterestGroupAdAuctionData(adAuctionDataConfig)
// Send unified auction request (Step #2)
const response = await fetch('https://ssp-ba.example/ad-auction', {
method: 'POST',
body: JSON.stringify({
adAuctionRequest: encodeBinaryData(request),
}),
});
const { protectedAudienceAuctionResult } = await response.json();
// Finish the auction in the browser
await navigator.runAdAuction({
// pass in "requestId" and "protectedAudienceAuctionResult"
// the config structure will differ based on the auction configuration
});
传入 runAdAuction()
调用的竞价配置的结构因卖方选择的竞价配置而异。
单卖家竞价
如需运行单卖方 B&A 竞价,runAdAuction()
调用的竞价配置的构建方式如下所示:
await navigator.runAdAuction({
seller: 'https://ssp-ba.example',
requestId,
serverResponse: protectedAudienceAuctionResult,
});
requestId
字段接受 getInterestGroupAdAuctionData()
调用返回的 requestId
。serverResponse
字段接受在第 3 步中运行的 B&A 竞价的字节数组。
如需查看此调用的示例,请参阅本地测试应用的卖方 JavaScript 代码。
混合模式竞价
如需运行混合模式 B&A 竞价(设备端买方和 B&A 买方都可以参与),runAdAuction()
调用的竞价配置的构建方式如下所示:
await navigator.runAdAuction({
seller: 'https://ssp-mix.example',
decisionLogicURL: 'https://ssp-mix.example/score-ad.js',
componentAuctions: [
// B&A auction result
{
seller: 'https://ssp-mix.example',
requestId,
serverResponse: protectedAudienceAuctionResult,
},
// On-device auction config
{
seller: 'https://ssp-mix.example',
decisionLogicURL: 'https://ssp-mix.example/on-device-score-ad.js',
interestGroupBuyers: [
'https://dsp-a.example', // On-device buyer
'https://dsp-a.example', // On-device buyer
],
},
]
});
为了促进混合模式竞价,B&A 竞价结果和设备端竞价配置会传递到 componentAuctions
字段。在混合模式竞价中,顶级配置和组件配置的 seller
值相同。
如需查看此调用的示例,请参阅本地测试应用的卖方 JavaScript 代码。
多卖家竞价
如果您是运行设备协调的多卖家竞价的顶级卖家,则每个组件卖家都会提交其 B&A 竞价结果和设备端竞价配置。
await navigator.runAdAuction({
seller: 'https://ssp-top.example',
decisionLogicURL: 'https://ssp-top.example/score-ad.js',
componentAuctions: [
// SSP-BA's B&A-only auction result
{
seller: 'https://ssp-ba.example',
requestId: 'g8312cb2-da2d-4e9b-80e6-e13dec2a581c',
serverResponse: Uint8Array(560) [193, 120, 4, …] // Encrypted B&A auction result
},
// SSP-MIX's B&A auction result
{
seller: 'https://ssp-mix.example',
requestId: 'f5135cb2-da2d-4e9b-80e6-e13dec2a581c',
serverResponse: Uint8Array(560) [133, 20, 4, …] // Encrypted B&A auction result
}.
// SSP-MIX's on-device auction config
{
seller: 'https://ssp-mix.example',
interestGroupBuyers: ['https://dsp-a.example', 'https://dsp-b.example'],
decisionLogicURL: 'https://ssp-mix.example/score-ad.js',
}
// SSP-OD's on-device auction config
{
seller: 'https://ssp-od.example',
interestGroupBuyers: ['https://dsp-a.example', 'https://dsp-b.example'],
decisionLogicURL: 'https://ssp-od.example/score-ad.js',
}
]
})
如需查看此调用的示例,请参阅本地测试应用的卖方 JavaScript 代码。
后续步骤
阅读本指南后,您可以执行以下后续步骤:
了解详情
- 如需深入了解,请参阅 GitHub 上的以下说明文档:
- 详细了解适用于网站的 B&A 架构
- 按照端到端本地测试 Codelab 中的说明,使用 B&A 对 Protected Audience 进行实验。
有疑问?
- 如果您对出价和竞价服务有疑问,请在出价和竞价服务代码库中打开问题。
- 如果您对 Privacy Sandbox 有一般性疑问,请在 privacy-sandbox-dev-support 代码库中打开问题。