prebid-server icon indicating copy to clipboard operation
prebid-server copied to clipboard

Support bidder-specific device data

Open paulborile opened this issue 11 months ago • 13 comments

We observed that when Prebid.js calls a Prebid Server (PBS), some bidder-specific global data (which appears in .ext.prebid.bidderconfig[<bidder>].config.ortb2.device when passed to PBS) is not forwarded to the bidders (it does not appear in the Bidder Request hook). Specifically, the device data added by, for example, the Prebid.js WURFL RTD module in ortb2.device is lost during transit in PBS.

paulborile avatar Jan 13 '25 10:01 paulborile

The same happens on pbs Java

paulborile avatar Jan 22 '25 11:01 paulborile

@paulborile - please provide an ORTB example.

ext.prebid.bidderconfig.BIDDER.config.ortb2.device should get added to the ORTB seen by BIDDER as simply "device"

bretg avatar Jan 24 '25 14:01 bretg

Input request coming from prebid.js + wurfl rtd : some Bidder-Specific global FPD is set

{
    "imp": [
        {
            "ext": {
                "prebid": {
                    "bidder": {
                        "pubmatic": {
                            "publisherId": "156276",
                            "adSlot": "pubmatic_test"
                        },
                        "appnexus": {
                            "placement_id": 13144370
                        },
                        "eplanning": {
                            "ci": "18f66",
                            "t": 1
                        }
                    },
                    "adunitcode": "test-div-1"
                }
            },
            "id": "test-div-1",
            "banner": {
                "topframe": 1,
                "format": [
                    {
                        "w": 300,
                        "h": 250
                    },
                    {
                        "w": 300,
                        "h": 600
                    }
                ]
            },
            "secure": 1
        }
    ],
    "site": {
        "domain": "staging-prebid-js-poc.scientiamobile.com",
        "publisher": {
            "domain": "scientiamobile.com",
            "id": "1"
        },
        "page": "https://staging-prebid-js-poc.scientiamobile.com/static/html/pbs_wurfl.html"
    },
    "device": {
        "w": 635,
        "h": 882,
        "dnt": 0,
        "ua": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Mobile Safari/537.36",
        "language": "en",
        "ext": {
            "vpw": 980,
            "vph": 1362
        },
        "sua": {
            "source": 2,
            "platform": {
                "brand": "Android",
                "version": [
                    "6",
                    "0"
                ]
            },
            "browsers": [
                {
                    "brand": "Not A(Brand",
                    "version": [
                        "8",
                        "0",
                        "0",
                        "0"
                    ]
                },
                {
                    "brand": "Chromium",
                    "version": [
                        "132",
                        "0",
                        "6834",
                        "110"
                    ]
                },
                {
                    "brand": "Google Chrome",
                    "version": [
                        "132",
                        "0",
                        "6834",
                        "110"
                    ]
                }
            ],
            "mobile": 1,
            "model": "Nexus 5",
            "architecture": ""
        }
    },
    "ext": {
        "prebid": {
            "auctiontimestamp": 1737731924490,
            "targeting": {
                "includewinners": true,
                "includebidderkeys": false
            },
            "bidderconfig": [
                {
                    "bidders": [
                        "pubmatic"
                    ],
                    "config": {
                        "ortb2": {
                            "device": {
                                "ext": {
                                    "wurfl": {
                                        "is_mobile": true,
                                        "complete_device_name": "Google Nexus 5",
                                        "form_factor": "Feature Phone",
                                        "model_name": "Nexus 5",
                                        "brand_name": "Google"
                                    }
                                },
                                "make": "Google",
                                "model": "Nexus 5"
                            }
                        }
                    }
                },
                {
                    "bidders": [
                        "appnexus"
                    ],
                    "config": {
                        "ortb2": {
                            "device": {
                                "ext": {
                                    "wurfl": {
                                        "advertised_browser": "Chrome Mobile",
                                        "advertised_browser_version": "132.0.0.0",
                                        "advertised_device_os": "Android",
                                        "advertised_device_os_version": "6.0",
                                        "ajax_support_javascript": true,
                                        "brand_name": "Google",
                                        "complete_device_name": "Google Nexus 5",
                                        "density_class": "3.0",
                                        "form_factor": "Feature Phone",
                                        "is_app_webview": false,
                                        "is_connected_tv": false,
                                        "is_full_desktop": false,
                                        "is_mobile": true,
                                        "is_ott": false,
                                        "is_phone": true,
                                        "is_robot": false,
                                        "is_smartphone": false,
                                        "is_smarttv": false,
                                        "is_tablet": false,
                                        "manufacturer_name": "LG",
                                        "marketing_name": "",
                                        "max_image_height": 640,
                                        "max_image_width": 360,
                                        "model_name": "Nexus 5",
                                        "physical_screen_height": 110,
                                        "physical_screen_width": 62,
                                        "pixel_density": 443,
                                        "pointing_method": "touchscreen",
                                        "resolution_height": 1920,
                                        "resolution_width": 1080
                                    }
                                },
                                "make": "Google",
                                "model": "Nexus 5",
                                "devicetype": 1,
                                "os": "Android",
                                "osv": "6.0",
                                "hwv": "Nexus 5",
                                "ppi": 443,
                                "pxratio": "3.0",
                                "js": true
                            }
                        }
                    }
                },
                {
                    "bidders": [
                        "eplanning"
                    ],
                    "config": {
                        "ortb2": {
                            "device": {
                                "ext": {
                                    "wurfl": {
                                        "advertised_browser": "Chrome Mobile",
                                        "advertised_browser_version": "132.0.0.0",
                                        "advertised_device_os": "Android",
                                        "advertised_device_os_version": "6.0",
                                        "ajax_support_javascript": true,
                                        "brand_name": "Google",
                                        "complete_device_name": "Google Nexus 5",
                                        "density_class": "3.0",
                                        "form_factor": "Feature Phone",
                                        "is_app_webview": false,
                                        "is_connected_tv": false,
                                        "is_full_desktop": false,
                                        "is_mobile": true,
                                        "is_ott": false,
                                        "is_phone": true,
                                        "is_robot": false,
                                        "is_smartphone": false,
                                        "is_smarttv": false,
                                        "is_tablet": false,
                                        "manufacturer_name": "LG",
                                        "marketing_name": "",
                                        "max_image_height": 640,
                                        "max_image_width": 360,
                                        "model_name": "Nexus 5",
                                        "physical_screen_height": 110,
                                        "physical_screen_width": 62,
                                        "pixel_density": 443,
                                        "pointing_method": "touchscreen",
                                        "resolution_height": 1920,
                                        "resolution_width": 1080
                                    }
                                },
                                "make": "Google",
                                "model": "Nexus 5",
                                "devicetype": 1,
                                "os": "Android",
                                "osv": "6.0",
                                "hwv": "Nexus 5",
                                "ppi": 443,
                                "pxratio": "3.0",
                                "js": true
                            }
                        }
                    }
                },
                {
                    "bidders": [
                        "0test"
                    ],
                    "config": {
                        "ortb2": {
                            "device": {
                                "ext": {
                                    "wurfl": {
                                        "is_mobile": true,
                                        "complete_device_name": "Google Nexus 5",
                                        "form_factor": "Feature Phone",
                                        "model_name": "Nexus 5",
                                        "brand_name": "Google"
                                    }
                                },
                                "make": "Google",
                                "model": "Nexus 5"
                            }
                        }
                    }
                }
            ],
            "channel": {
                "name": "pbjs",
                "version": "v9.22.0"
            },
            "createtids": false
        },
        "tmaxmax": 3000
    },
    "id": "0a63b9b8-31c6-4eaf-9f78-cfeb06ddcbcb",
    "test": 0,
    "tmax": 1000
}

(0test being our test Adapter)

The following data is from MakeRequest of pubmatic adapter ( https://docs.prebid.org/prebid-server/developers/add-new-bidder-go.html#makerequests )

(*openrtb2.BidRequest)(0xc000bcfe00)({
 ID: (string) (len=36) "0a63b9b8-31c6-4eaf-9f78-cfeb06ddcbcb",
 Imp: ([]openrtb2.Imp) (len=1 cap=1) {
  (openrtb2.Imp) {
   ID: (string) (len=10) "test-div-1",
   Metric: ([]openrtb2.Metric) <nil>,
   Banner: (*openrtb2.Banner)(0xc0002ddd00)({
    Format: ([]openrtb2.Format) (len=2 cap=2) {
     (openrtb2.Format) {
      W: (int64) 300,
      H: (int64) 250,
      WRatio: (int64) 0,
      HRatio: (int64) 0,
      WMin: (int64) 0,
      Ext: (json.RawMessage) <nil>
     },
     (openrtb2.Format) {
      W: (int64) 300,
      H: (int64) 600,
      WRatio: (int64) 0,
      HRatio: (int64) 0,
      WMin: (int64) 0,
      Ext: (json.RawMessage) <nil>
     }
    },
    W: (*int64)(<nil>),
    H: (*int64)(<nil>),
    WMax: (int64) 0,
    HMax: (int64) 0,
    WMin: (int64) 0,
    HMin: (int64) 0,
    BType: ([]openrtb2.BannerAdType) <nil>,
    BAttr: ([]adcom1.CreativeAttribute) <nil>,
    Pos: (*adcom1.PlacementPosition)(<nil>),
    MIMEs: ([]string) <nil>,
    TopFrame: (int8) 1,
    ExpDir: ([]adcom1.ExpandableDirection) <nil>,
    API: ([]adcom1.APIFramework) <nil>,
    ID: (string) "",
    Vcm: (*int8)(<nil>),
    Ext: (json.RawMessage) <nil>
   }),
   Video: (*openrtb2.Video)(<nil>),
   Audio: (*openrtb2.Audio)(<nil>),
   Native: (*openrtb2.Native)(<nil>),
   PMP: (*openrtb2.PMP)(<nil>),
   DisplayManager: (string) "",
   DisplayManagerVer: (string) "",
   Instl: (int8) 0,
   TagID: (string) "",
   BidFloor: (float64) 0,
   BidFloorCur: (string) "",
   ClickBrowser: (*int8)(<nil>),
   Secure: (*int8)(0xc000779ad6)(1),
   IframeBuster: ([]string) <nil>,
   Rwdd: (int8) 0,
   SSAI: (openrtb2.AdInsertion) 0,
   Exp: (int64) 0,
   Qty: (*openrtb2.Qty)(<nil>),
   DT: (float64) 0,
   Refresh: (*openrtb2.Refresh)(<nil>),
   Ext: (json.RawMessage) (len=105 cap=105) {
    00000000  7b 22 62 69 64 64 65 72  22 3a 7b 22 70 75 62 6c  |{"bidder":{"publ|
    00000010  69 73 68 65 72 49 64 22  3a 22 31 35 36 32 37 36  |isherId":"156276|
    00000020  22 2c 22 61 64 53 6c 6f  74 22 3a 22 70 75 62 6d  |","adSlot":"pubm|
    00000030  61 74 69 63 5f 74 65 73  74 22 7d 2c 22 74 69 64  |atic_test"},"tid|
    00000040  22 3a 22 39 64 35 33 65  66 66 36 2d 31 34 37 62  |":"9d53eff6-147b|
    00000050  2d 34 37 37 31 2d 62 30  36 31 2d 66 38 32 63 30  |-4771-b061-f82c0|
    00000060  63 38 32 37 35 30 61 22  7d                       |c82750a"}|
   }
  }
 },
 Site: (*openrtb2.Site)(0xc0007d3c20)({
  ID: (string) "",
  Name: (string) "",
  Domain: (string) (len=40) "staging-prebid-js-poc.scientiamobile.com",
  CatTax: (adcom1.CategoryTaxonomy) 0,
  Cat: ([]string) <nil>,
  SectionCat: ([]string) <nil>,
  PageCat: ([]string) <nil>,
  Page: (string) (len=75) "https://staging-prebid-js-poc.scientiamobile.com/static/html/pbs_wurfl.html",
  Ref: (string) "",
  Search: (string) "",
  Mobile: (*int8)(<nil>),
  PrivacyPolicy: (*int8)(<nil>),
  Publisher: (*openrtb2.Publisher)(0xc00023ebd0)({
   ID: (string) (len=1) "1",
   Name: (string) "",
   CatTax: (adcom1.CategoryTaxonomy) 0,
   Cat: ([]string) <nil>,
   Domain: (string) (len=18) "scientiamobile.com",
   Ext: (json.RawMessage) <nil>
  }),
  Content: (*openrtb2.Content)(<nil>),
  Keywords: (string) "",
  KwArray: ([]string) <nil>,
  InventoryPartnerDomain: (string) "",
  Ext: (json.RawMessage) <nil>
 }),
 App: (*openrtb2.App)(<nil>),
 DOOH: (*openrtb2.DOOH)(<nil>),
 Device: (*openrtb2.Device)(0xc000604c40)({
  Geo: (*openrtb2.Geo)(<nil>),
  DNT: (*int8)(0xc0009ef9eb)(0),
  Lmt: (*int8)(<nil>),
  UA: (string) (len=131) "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Mobile Safari/537.36",
  SUA: (*openrtb2.UserAgent)(0xc000a69e00)({
   Browsers: ([]openrtb2.BrandVersion) (len=3 cap=3) {
    (openrtb2.BrandVersion) {
     Brand: (string) (len=11) "Not A(Brand",
     Version: ([]string) (len=4 cap=4) {
      (string) (len=1) "8",
      (string) (len=1) "0",
      (string) (len=1) "0",
      (string) (len=1) "0"
     },
     Ext: (json.RawMessage) <nil>
    },
    (openrtb2.BrandVersion) {
     Brand: (string) (len=8) "Chromium",
     Version: ([]string) (len=4 cap=4) {
      (string) (len=3) "132",
      (string) (len=1) "0",
      (string) (len=4) "6834",
      (string) (len=3) "110"
     },
     Ext: (json.RawMessage) <nil>
    },
    (openrtb2.BrandVersion) {
     Brand: (string) (len=13) "Google Chrome",
     Version: ([]string) (len=4 cap=4) {
      (string) (len=3) "132",
      (string) (len=1) "0",
      (string) (len=4) "6834",
      (string) (len=3) "110"
     },
     Ext: (json.RawMessage) <nil>
    }
   },
   Platform: (*openrtb2.BrandVersion)(0xc0009e0d00)({
    Brand: (string) (len=7) "Android",
    Version: ([]string) (len=2 cap=2) {
     (string) (len=1) "6",
     (string) (len=1) "0"
    },
    Ext: (json.RawMessage) <nil>
   }),
   Mobile: (*int8)(0xc0009ef9ec)(1),
   Architecture: (string) "",
   Bitness: (string) "",
   Model: (string) (len=7) "Nexus 5",
   Source: (adcom1.UserAgentSource) 2,
   Ext: (json.RawMessage) <nil>
  }),
  IP: (string) "",
  IPv6: (string) "",
  DeviceType: (adcom1.DeviceType) 0,
  Make: (string) "",
  Model: (string) "",
  OS: (string) "",
  OSV: (string) "",
  HWV: (string) "",
  H: (int64) 882,
  W: (int64) 635,
  PPI: (int64) 0,
  PxRatio: (float64) 0,
  JS: (*int8)(<nil>),
  GeoFetch: (*int8)(<nil>),
  FlashVer: (string) "",
  Language: (string) (len=2) "en",
  LangB: (string) "",
  Carrier: (string) "",
  MCCMNC: (string) "",
  ConnectionType: (*adcom1.ConnectionType)(<nil>),
  IFA: (string) "",
  DIDSHA1: (string) "",
  DIDMD5: (string) "",
  DPIDSHA1: (string) "",
  DPIDMD5: (string) "",
  MACSHA1: (string) "",
  MACMD5: (string) "",
  Ext: (json.RawMessage) (len=56 cap=64) {
   00000000  7b 20 20 20 20 20 20 20  20 20 20 20 20 22 76 70  |{            "vp|
   00000010  77 22 3a 20 39 38 30 2c  20 20 20 20 20 20 20 20  |w": 980,        |
   00000020  20 20 20 20 22 76 70 68  22 3a 20 31 33 36 32 20  |    "vph": 1362 |
   00000030  20 20 20 20 20 20 20 7d                           |       }|
  }
 }),
 User: (*openrtb2.User)(<nil>),
 Test: (int8) 0,
 AT: (int64) 1,
 TMax: (int64) 1000,
 WSeat: ([]string) <nil>,
 BSeat: ([]string) <nil>,
 AllImps: (int8) 0,
 Cur: ([]string) <nil>,
 WLang: ([]string) <nil>,
 WLangB: ([]string) <nil>,
 ACat: ([]string) <nil>,
 BCat: ([]string) <nil>,
 CatTax: (adcom1.CategoryTaxonomy) 0,
 BAdv: ([]string) <nil>,
 BApp: ([]string) <nil>,
 Source: (*openrtb2.Source)(0xc0009f05a0)({
  FD: (*int8)(<nil>),
  TID: (string) (len=36) "9bcd7279-d616-4229-a37a-1e4c52fef7cc",
  PChain: (string) "",
  SChain: (*openrtb2.SupplyChain)(<nil>),
  Ext: (json.RawMessage) <nil>
 }),
 Regs: (*openrtb2.Regs)(<nil>),
 Ext: (json.RawMessage) (len=148 cap=148) {
  00000000  7b 22 70 72 65 62 69 64  22 3a 7b 22 63 68 61 6e  |{"prebid":{"chan|
  00000010  6e 65 6c 22 3a 7b 22 6e  61 6d 65 22 3a 22 70 62  |nel":{"name":"pb|
  00000020  6a 73 22 2c 22 76 65 72  73 69 6f 6e 22 3a 22 76  |js","version":"v|
  00000030  39 2e 32 32 2e 30 22 7d  2c 22 73 65 72 76 65 72  |9.22.0"},"server|
  00000040  22 3a 7b 22 65 78 74 65  72 6e 61 6c 75 72 6c 22  |":{"externalurl"|
  00000050  3a 22 68 74 74 70 3a 2f  2f 6c 6f 63 61 6c 68 6f  |:"http://localho|
  00000060  73 74 3a 38 30 30 30 22  2c 22 67 76 6c 69 64 22  |st:8000","gvlid"|
  00000070  3a 30 2c 22 64 61 74 61  63 65 6e 74 65 72 22 3a  |:0,"datacenter":|
  00000080  22 22 7d 7d 2c 22 74 6d  61 78 6d 61 78 22 3a 33  |""}},"tmaxmax":3|
  00000090  30 30 30 7d                                       |000}|
 }
})

While device.make and device.model had values in input request bidder-specific global fpd, here they are empty :

  Make: (string) "",
  Model: (string) "",

(In a similar way for the other device fields devicetype, os, osv, hwv, ppi, pxratio and js which are filled by prebid.js wurfl rtd) Is this the intended behavior for pbs i.e. stripping down every bidder request to standard fpd regardless of input Bidder-Specific global fpd ? Is there a way to enable this via data permission or does this needs to be modified at the server level ? (tested with pbs 3.7.0 and default configuration)

paulborile avatar Jan 24 '25 16:01 paulborile

Actually, I do seem to recall that bidderconfig only supports merging ORTB for site, app, and user top-level objects.

What's the use case for bidder-specific device data? This would be an enhancement request that's not going to be high priority.

bretg avatar Jan 24 '25 17:01 bretg

When a publisher uses prebid.js with WURFL RTD for device detection, bid requests routed through PBS lose the device enrichment provided by WURFL RTD. Bid requests sent directly to bidders retain this enrichment.

paulborile avatar Jan 24 '25 18:01 paulborile

The question is "why is it bidder-specific"?

i.e. why can't the device enhancement be global to all bidders?

pbjs.setConfig({
   ortb2: {
      device: {
         ... data ...

bretg avatar Jan 24 '25 18:01 bretg

Our current business model involves sending different sets of information to specific bidders (similar to enabling device enrichment for specific publishers).

To address the issue with bidder-specific data in Prebid Server, we are thinking at a new configuration option in the Prebid.js-WURFL RTD module: "Add device data to global ortb object (set true for Prebid Server)" [true/false].

When enabled, this option will ensure that device data is added to ortb2Fragments.global instead of ortb2Fragments.[bidder].data, specifically for use with Prebid Server.

paulborile avatar Jan 28 '25 10:01 paulborile

Very well, I've updated the title to reflect this is an enhancement request. I'm limiting it to just 'device' and not the entire ORTB object at this point. Will bring it up in a future PMC meeting where perhaps we'll decide to throw the doors wide open.

bretg avatar Jan 28 '25 14:01 bretg

Discussed in committee. Agreed to add device as a top-level object supported in bidderconfig with the same behavior: merge into the main request with bidderconfig.ortb2.device fields taking precedence.

bretg avatar Jan 29 '25 15:01 bretg

wrt the fixes necessary to pbs-go, these can be managed by Luca Corbo (@lucor), currently working on our prebid developments. cc @bretg

paulborile avatar Feb 03 '25 16:02 paulborile

It doesn't appear to be relevant to the use case described here, but do we also want to support global device first party data so the behavior for top-level device is consistent with how we handle other top-level fields we support (user, site, app)?

bsardo avatar Feb 19 '25 18:02 bsardo

@bretg the PBS-Go team believes that we should support global device first party data in addition to bidder-specific device first party data so that device is treated in the same way as other supported top-level fields (user, site, app). We'd like to see this functionality added in the same PR as the bidder-specific functionality. Thoughts?

bsardo avatar Mar 12 '25 17:03 bsardo

What is "global device first party data"? What's the real problem here -- validation?

"First Party Data" in my mind is stuff that the publisher supplies as extra-credit targeting signals to the ad tech ecosystem:

  • {site,app,user}.ext.data
  • {site,app}.content.data
  • user.data

That's it. It doesn't make sense to me to call device data "FPD" because it it's not from the publisher. Device data is actually 0th-party-data. (Sometimes helpful that Computer Science people can start counting from 0 and make it seem normal. :-)

{
  ext.prebid.bidderconfig: [{
    bidders: ["bidderA"],
    config: {
      ortb2: {
         // really ought to support all ORTB here, but to minimize validation headaches,
         // we've limited this to a few top level objects
      }
    }
  }]
}

bretg avatar Mar 12 '25 17:03 bretg