Delegate User-Agent Client-Hints to 3rd Parties

‼️ Note: there is an updated version of this article here.

User-Agent Client Hints (UA-CH) has been hot in the Chrome camp lately. Motivated by the sudden urge to deprecate the User-Agent request header due to privacy concerns. With Chrome version 84 and higher, the plumbing with delegation through feature- or permission policies is implemented.

Delegating UA-CH to a third party server is similar to how delegating other client hints work, but with some caveats. 

Stragely, the JavaScript API to access the hints client side navigator.userAgentData.getHighEntropyValues() is not protected by any opt-in, permission policy or user interaction…

To delegate UA-CH to third parties the steps are as follows:

1. Opt-in to Receive UA-CH

The server has to announce support for client hints. For UA-CH this is not any different than for other hints. The server simply responds with an Accept-CH header listing the hints supported:

Accept-CH: ua-platform,ua-arch,ua-model,ua-platform-version,ua-full-version

This will ask the user-agent to send the requested hints to all subsequent requests to the same origin who made the request. 

But this is not what we want… If the origin is foo.com, we want the hints to be sent to bar.foo.comtoo…

In order to do that, we need to make sure the browser sends the hints to bar.foo.com too. This is done through a feature policy header:

2. Delegate Hints Using Feature Policy

At the time of writing, the mechanism for enabling or disabling features in browsers is called Feature Policy. This will at some point in the future change name to Permission Policy

Note that the hints ua-mobile and ch-ua does not need to be delegated through feature policies. These headers are available by default and are meant to make an alternative to the User-Agentheader.

To delegate the User-Agent Client Hints to a 3rd party we need to add the Feature-Policy header to the response along with the Accept-CH header:

Feature-Policy: ch-ua-arch https://bar.foo.com;ch-ua-model https://bar.foo.com;ch-ua-platform https://bar.foo.com;ch-ua-platform-version https://bar.foo.com;ch-ua-full-version https://bar.foo.com

Here’s the first caveat. Note that the hints are prefixed with ch-. Even if the hint is named ua-arch in the Accept-CH header, it is delegated to the third party as ch-ua-arch

The 3rd party does not have to announce support for hints or opt in to receiving hints. All hints are sent regardless.

The second caveat is that the hints specified in the policy are case sensitive and must be lowercase! This is true for all client hints, not only UA-CH.

3. Read the Request Headers at the 3rd Party

By now the 3rd party should receive the hints specified in the policy as request headers.

Here’s another caveat. The UA-CH header names are now prefixed yet another time. Now with sec-. The hint ua-arch we opted in for in step 1 reveals itself as sec-ch-ua-arch. Yea, I know… Here’s the reason.

The request headers to the 3rd party may now look something like this:

sec-ch-ua-full-version: "85.0.4156.0"
sec-ch-ua-arch: ""
sec-ch-ua-platform: "Mac OS X"
sec-ch-ua-platform-version: "10_13_6"
sec-ch-ua-model: ""
sec-ch-ua-mobile: ?0
sec-ch-ua: "\\Not;A\"Brand";v="99", "Google Chrome";v="85", "Chromium";v="85"

How does UA-CH work?

I put together this demo on glitch.com: https://glen-wistful-protoceratops.glitch.me/

Make sure to test in a supporting browser, like Chrome v 84 or higher.

You’ll notice that hints are sent on the first request to the 3rd party iframe.

☝🏻 make sure to open this demo in a separate window https://glen-wistful-protoceratops.glitch.me/

Comments are closed.