ua-parser-box

Introducing the 51Degrees UAParser

51Degrees & Postindustria

2/20/2023 4:30 PM

User Agent Client Hints Device Detection News Development

We've expanded the 51Degrees product suite

The 51Degrees UAParser provides comprehensive device data based on User-Agent Client Hints and the User-Agent, and can reliably detect Apple iPhone models. The package can be used both in the browser and Node.js environment.

Download the 51Degrees UAParser from one of the following sources:

A live demonstration of the product was shown during our UAParser webinar: catch up on the replay here.

In the meantime, we have written a guide detailing how to migrate from the existing UAParser.js to the new 51Degrees UAParser.

Table of contents

Background

UAParser.js is a popular npm package allowing device detection from the User-Agent. It runs locally and parses the User-Agent using regular expressions to extract device information.

Unfortunately, the User-Agent is changing. Google is gradually removing device information from Chromium-based User-Agents, and the information reduction is due to complete in May 2023.

For an example of the User-Agent reduction, let's look at this User-Agent sent by Chrome Canary 112 running on Samsung Galaxy S8:

Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36

Now let's compare this User-Agent, which uses Chrome 109 on the same device:

Mozilla/5.0 (Linux; Android 9; SM-G950F<) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36

Chrome 112 is already missing the platform version and model information, while the browser version is reduced in both User-Agents.

The newly released 51Degrees UAParser package:

  • Does not rely solely on a deprecated User-Agent HTTP header. Instead, it uses all available evidence including the User-Agent and User-Agent Client Hints.
  • Does not require "offline" processing as it uses the 51Degrees Cloud service to process the evidence data, producing accurate and up-to-date results.
  • Uses a familiar programming model similar to the original UAParser.js and can work either in the browser or Node.js environment.

Let's see how to easily migrate from UAParser.js to the 51Degrees UAParser.

Migrating from UAParser.js to 51Degrees UAParser

Creating a Resource Key

Before integrating 51Degrees UAParser, you need to register and configure a 51Degrees Resource Key using the Configurator. The Configurator tool allows you to choose the device properties you would like to get as a result of the call to UAParser.

We recommend following this link to create a Resource Key, as it contains the minimum recommended properties that you will need for the UAParser to work. One of such properties is JavascriptHardwareProfile which is needed for a reliable Apple device detection in the browser.

There are a few small steps to follow creating your Resource Key (you can refer to our Configurator guide for more information):

  • Choose your properties.
  • Enter your License Key (if you have chosen paid-for properties, you will be asked to buy a pricing plan). Alternatively, click “free product is enough for me”.
  • Implement your Resource Key on the left and copy it to use in the integration later.

User-Agent Client Hints delegation

Before jumping in to migrating the client-side code, there is one more thing that we need to mention: User-Agent Client Hints delegation. There are articles on the web discussing the matter in detail, both human readable and written by WICG.

There are two distinct scenarios of Client Hints processing:

  1. When the browser has to send Client Hints to the first party - that is the domain from which the current page (or frame) is served.
  2. When the browser has to send Client Hints to the third party - that is the other domain the currently served page makes a request to, different from the one the page is served from. Notice that even if it is a subdomain of the origin, it will still be considered a third-party domain.

Delegation needs to happen in the second scenario. This is needed because 51Degrees UAParser running within the page delegates Client Hints to a third-party cloud.51degrees.com domain.

There are two ways for the owner of the web page to tell the browser that it is allowed to pass the Client Hints to the third-party domain:

  1. Setting the Accept-CH and Permissions-Policy HTTP response headers when serving the page. This works if you have access to the server and can modify the response headers.
  2. Adding the <meta http-equiv="Delegate-CH"> tag in the page head. This is appropriate when you can't modify response headers on the server.

Here are the HTTP response headers we'll need to set to ensure delegation:

    
        Accept-CH: Sec-CH-UA, Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Full-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-WoW64 
        Permissions-Policy: ch-ua=("https://cloud.51degrees.com"), ch-ua-arch=("https://cloud.51degrees.com"), ch-ua-bitness=("https://cloud.51degrees.com"), ch-ua-mobile=("https://cloud.51degrees.com"), ch-ua-model=("https://cloud.51degrees.com"), ch-ua-platform=("https://cloud.51degrees.com"), ch-ua-platform-version=("https://cloud.51degrees.com"), ch-ua-full-version=("https://cloud.51degrees.com"), ch-ua-full-version-list=("https://cloud.51degrees.com"), ch-ua-wow64=("https://cloud.51degrees.com")
    

Alternatively you can add this <meta http-equiv="Delegate-CH"> tag within the page <head>:

    
        <meta http-equiv="Delegate-CH" content="Sec-CH-UA-Model https://cloud.51degrees.com; Sec-CH-UA https://cloud.51degrees.com; Sec-CH-UA-Arch https://cloud.51degrees.com; Sec-CH-UA-Full-Version https://cloud.51degrees.com; Sec-CH-UA-Mobile https://cloud.51degrees.com; Sec-CH-UA-Platform https://cloud.51degrees.com; Sec-CH-UA-Platform-Version https://cloud.51degrees.com" />
     

Please note: The major prerequisite for the Client Hints to be sent by the browser is a secure HTTPS connection. This is the default nowadays but is still worth mentioning in case you are testing on some staging server with HTTP and are not seeing Client Hints sent. For test setups, a self-signed SSL certificate is enough.

Migrating client-side code

Equipped with the Resource Key and knowledge about the Client Hint delegation, we are ready to migrate the UAParser.js integration to 51Degrees UAParser.

You probably had the page code similar to the below. When used without explicitly passing a User-Agent as a parameter, the original UAParser.js would detect your current device on which the browser is running:


    <!doctype html>
    <html>
        <head>
        <script src="ua-parser.min.js"></script>
        <script>
            let result = UAParser();
            console.log(result);
        </script>
        </head>
        <body>
        </body> 
    </html>

Here are the migration steps we need to take to migrate this code to using 51Degrees UAParser:

  1. Download the package by running npm i @51degrees/ua-parser-js. The script you need resides in the node_modules/@51degrees/ua-parser-js/lib/ua-parser.min.js. Alternatively use the script from the CDN: https://cdn.jsdelivr.net/npm/@51degrees/ua-parser-js.
  2. We need to either use Accept-CH and Permissions-Policy headers or add a Delegate-CH meta tag within the <head> element. We chose the latter in the below migrated code snippet example.
  3. Change the script import statement to use the path to the new package in the src attribute or use a CDN link.
  4. The new 51Degrees UAParser is inherently asynchronous, thus UAParser is a promise. We prefer resolving promises using the await keyword, thus, to use the top-level await we must set the script type="module".
  5. Here is the code to obtain the result object, notice the await keyword and the Resource Key: let result = await UAParser("<your resource key>");
  6. The code below obtaining the result can stay the same, but if you had additional properties configured for this Resource Key, you can access them directly (using lowercase key names) inside the result.device object.

The migrated code snippet looks like this:

    
        <!doctype html> 
        <html> 
            <head> 
            <meta http-equiv="Delegate-CH" content="Sec-CH-UA-Model https://cloud.51degrees.com; Sec-CH-UA https://cloud.51degrees.com; Sec-CH-UA-Arch https://cloud.51degrees.com; Sec-CH-UA-Full-Version https://cloud.51degrees.com; Sec-CH-UA-Mobile https://cloud.51degrees.com; Sec-CH-UA-Platform https://cloud.51degrees.com; Sec-CH-UA-Platform-Version https://cloud.51degrees.com" /> 
            <script src="ua-parser.min.js"></script> 
            <script type="module"> 
               let result = await UAParser("resource key");  
               console.log(result); 
            </script> 
            </head> 
            <body> 
            </body> 
        </html>  
    

Alternatively, if the server adds Accept-CH and Permissions-Policy response headers, the snippet becomes even simpler as you can omit the Delegate-CH meta tag.

    
        <!doctype html> 
        <html>
            <head> 
            <script src="https://cdn.jsdelivr.net/npm/@51degrees/ua-parser-js"></script> 
            <script type="module"> 
                let result = await UAParser("resource key");  
                console.log(result); 
            </script> 
            </head> 
            <body> 
            </body> 
        </html> 
    

Migrating server-side code

A common scenario on the server is to receive a User-Agent as a request header and pass it to the UAParser to recognize the device. The Node.js server code could look something like this:

    
        var http = require('http'); 
        var parser = require('ua-parser-js'); 
 
        http.createServer(function (req, res) { 
            // parse user-agent header 
            var result = parser(req.headers['user-agent']); 
            res.end(JSON.stringify(result, null, ' ')); 
        }) 
        .listen(80, '0.0.0.0');
    

Here is what we need to change to migrate this to use 51Degrees UAParser:

  1. We want to use a HTTPS server instead of HTTP so that the browser would send us User-Agent Client Hints.
  2. The package name in the require statement should be changed to @51degrees/ua-parser-js.
  3. The function passed to the server should now be marked async, as we are going to use await inside it.
  4. The parser call should be prepended with await.
  5. The parser call now takes the Resource Key and the request header map as a parameter.
    
        // the self-signed certificate for the test environment 
            var fs = require('fs'); 
            var options = { 
                key: fs.readFileSync('.cert/key.pem'), 
                cert: fs.readFileSync('.cert/cert.pem') 
            };

        // the new package name 
            const UAParser = require("@51degrees/ua-parser-js"); 
            var https = require('https');

        // note the async function 
            https.createServer(options, async function (req, res) { 
            // note the await keyword and parameters 
                let result = await UAParser("your resource key", req.headers); 
                res.end("
51Degrees:
" + JSON.stringify(result, null, ' ') + "
"); }) .listen(443, '0.0.0.0');

Now we assume that this would be a secondary call to the server from within the page that has already been rendered and had an Accept-CH header in the response that listed all the necessary User-Agent Client Hints. The Accept-CH header in the original page would make it send the Client Hints in all subsequent requests to the same origin. This feature is called the Accept-CH cache.

Here is a quick and dirty example where we'll show how to ask the browser to immediately send the User-Agent Client Hints by setting two response headers: Accept-CH and Critical-CH. The latter tells the browser to immediately come back with the Client Hints if it did not send them on the first attempt, so it starts another request before loading the response body.

     
        // the self-signed certificate for the test environment 
        var fs = require('fs'); 
        var options = { 
            key: fs.readFileSync('.cert/key.pem'), 
            cert: fs.readFileSync('.cert/cert.pem') 
        }; 
 
        // the new package name 
        const UAParser = require("@51degrees/ua-parser-js"); 
        var https = require('https'); 
 
        // note the async function 
        https.createServer(options, async function (req, res) { 
        // note the await keyword and parameters 
        let result = await UAParser("your resource key", req.headers); 
 
        ch = 'Sec-CH-UA, Sec-CH-UA-Arch, Sec-CH-Bitness, Sec-CH-UA-Full-Version, \ 
        Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, \ 
        Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-WoW64' 
 
        res.setHeader('Critical-CH',ch) 
        res.setHeader('Accept-CH', ch) 
 
        res.end("
51Degrees:
" + JSON.stringify(result, null, ' ') + "
"); }) .listen(443, '0.0.0.0');

Conclusion

We've shown you how to get started with the 51Degrees UAParser, an easy-to-use alternative to the original UAParser.js.

Download the 51Degrees UAParser from one of the following sources:

The 51Degrees UAParser addresses a major limitation of the User-Agent deprecation imposed by the modern Chromium browser ecosystem. Because the data is in the cloud and constantly updated by a professional data analyst team, the device detection provided by 51Degress UAParser spans longer and wider with many more devices and much more data properties for each device.

Additionally, 51Degrees UAParser has a clear and concise programming model that allows it to be universally deployed. It runs in any modern browser including Apple and Android mobile browsers, and in the Node.js server environment.

If you still have questions on getting started with the 51Degrees UAParser, please do get in touch.

We also hosted a webinar on the 51Degrees UAParser. Catch up on the recording here.

Thanks to Postindustria for their work on this project!