How to convert SUA data into UA-CH, and vice versa
Introduction
As part of Google's User-Agent reduction, the User-Agent request header has been subject to a gradual deprecation in Chromium-based browsers since 2020, and the deprecation should be complete by May 2023.
User-Agent Client Hints (UA-CH) are a mechanism introduced by Google in 2020 to "enable developers to access information about a user's browser in a privacy-preserving and ergonomic way". Essentially, User-Agent Client Hints can be viewed as a replacement for a traditional User-Agent string in Chromium-based browsers.
These changes by Google have knock-on effects for the rest of the industry. This blog will primarily discuss the impact of the changes to the IAB's OpenRTB specification (in which they introduced the Structured User Agent), as well as the importance of the User-Agent Client Hints.
51Degrees already has support for User-Agent Client Hints, however for our ad tech customers, we're providing a method of converting the OpenRTB Structured User Agent into User-Agent Client Hints so the data can be fed into our device detection service.
OpenRTB
The IAB's OpenRTB (a primary communication protocol that is used within advertising and real-time bidding) passes User-Agent information as part of the request to allow the ad supply chain members to identify a user's device.
Now that information within the User-Agent has been deprecated within Chromium browsers (which make up a large portion of the browser market share), IAB in April 2022 introduced a new Object: Device attribute in version 2.6 of the OpenRTB specification.
The Structured User Agent (sua or device.sua), referenced in sections 3.2.18 and 3.2.29 of the specification, is an OpenRTB attribute that can be used when the browser supports User-Agent Client Hints.
The value of the new oRTB request “device.sua” field is an object that contains User-Agent Client Hints information and the older “device.ua” field that contains a traditional User-Agent string. Both of these may be present in the OpenRTB bid request, however device.sua is likely to contain more complete and accurate information given that the User-Agent is reduced.
Structured User Agent components
The following is an example of Structured User Agent information:
{
"device":
{
"sua":
{
"browsers":
[
{
"brand": "Google Chrome",
"version":
[
"111",
"0",
"5563",
"146"
]
},
{
"brand": "Not(A:Brand",
"version":
[
"8",
"0",
"0",
"0"
]
},
{
"brand": "Chromium",
"version":
[
"111",
"0",
"5563",
"146"
]
}
],
"platform":
{
"brand": "macOS",
"version":
[
"13",
"3"
]
},
"mobile": 0,
"architecture": "x86",
"bitness": "64",
"model": "",
"source": 2
}
}
}
This Structured User Agent object corresponds to this set of User-Agent Client Hints:
sec-ch-ua-arch: "x86"
sec-ch-ua-bitness: "64"
sec-ch-ua-full-version-list: "Google Chrome";v="111.0.5563.146", "Not(A:Brand";v="8.0.0.0", "Chromium";v="111.0.5563.146"
sec-ch-ua-mobile: ?0
sec-ch-ua-model
sec-ch-ua-platform: "macOS"
sec-ch-ua-platform-version: "13.3.0"
Please note the subtlety of how the string data is deliberately quoted in the HTTP headers to specify that this is a string type data. The question mark in the sec-ch-ua-mobile
header specifies that this is a Boolean type data.
When you compare the two examples, you can see that the Structured User Agent contains the information provided by the User-Agent Client Hints.
Structured User Agent information can be gathered either from UA-CH header values or from their equivalent available through the NavigatorUAData API. Below is a table detailing the comparison between the OpenRTB request path, the UA-CH header (according to the OpenRTB specification section 3.2.29), and the NavigatorUAData API:
OpenRTB Request Path | UA-CH HTTP Header | navigator.userAgentData API |
---|---|---|
device.sua.browsers | Sec-CH-UA-Full-Version-List | uaData.getHighEntropyValues(['fullVersionList']) |
device.sua.platform | Sec-CH-UA-Platform + Sec-CH-UA-PlatformVersion | uaData.platform + uaData.getHighEntropyValues(['platformVersion']) |
device.sua.mobile | Sec-CH-UA-Mobile | uaData.mobile |
device.sua.architecture | Sec-CH-UA-Arch | uaData.getHighEntropyValues(['architecture'] |
device.sua.bitness | Sec-CH-UA-Bitness | CuaData.getHighEntropyValues(['bitness']) |
device.sua.model | Sec-CH-UA-Model | uaData.getHighEntropyValues(['model']) |
Besides the above, there are two more OpenRTB request fields: device.sua.ext and device.sua.source. The device.sua.ext field is optional and is used to pass various non-standardized information (as long as it is a valid JSON-encoded representation) – it is likely to be ignored by most parts of the supply-chain and read only by the interested parties.
The device.sua.source explains how the original data for Structured User Agent forming has been obtained and can take one of the following values according to the AdCom 1.0 spec:
Value | Definition |
---|---|
0 | Unspecified/unknown |
1 | User-Agent Client Hints (only low-entropy headers were available) |
2 | User-Agent Client Hints (with high-entropy headers available) |
3 | Parsed from User-Agent header (the same string carried by the ua field) |
Converting Structured User Agent to User-Agent Client Hints
Most device detection services (including 51Degrees) expect to receive evidence in the form of HTTP headers that contain a User-Agent and/or User-Agent Client Hints. Given the field correspondence table above, it is easy to come up with the routines to convert the information from the Structured User Agent format into the UA-CH header map format.
This received header map can be fed as evidence into one of the 51Degrees services such as our Pipeline, cloud API, or UAParser to perform an accurate device detection.
Device data can then be used to enrich the device object as part of the OpenRTB request to pass further to other members of the supply chain. See our documentation on how to map OpenRTB attributes to 51Degrees properties.
The conversion routines are hosted as part of this repository. We also have a demo page to test the Structured User Agent to UA-CH conversion and we encourage you to try the routines and use them in your code.