CreateIT
CreateIT
BLOG

Get User Fingerprint using UAParser.js

Ninja standing in front of a laptop with a fingerprint on screen

Get User Fingerprint using UAParser.js

SHARE

Challenge:
detect a website visitor without using cookies
Solution:
create a user fingerprint from User-Agent data

How to identify a website visitor? How to do it even in incognito (private) mode? The old solution was to save the cookie in the browser, which would store the visitor’s unique user identifier. However, once saved, the cookie belongs to one domain and can’t be transferred between different browsers.

Every browser provides general information about the browser name, browser version, platform and user language. We can use it to create a user fingerprint, which will be used as UID (Unique Identifier). It stays the same, even if browser cache or settings are purged. Identification will also be working in the Incognito Mode of the browser.

Black text on white background with a green button in the middle

Fingerprint usage

Have you ever wondered how the same adverts are displayed when you’re navigating the internet? Companies that are running ad networks use a similar technique: a user fingerprint is assigned to a user profile. Targeted ads are displayed basing on this data.

User privacy

It’s important to ask users for permission to use their data. Sensitive information, such as user IP address or user fingerprint, can be used to identify a single visitor. With the introduction of privacy laws, including GDPR (Europe) and CCPA (CA, USA), it’s crucial to get a user permission before requesting sensitive information.

IpAddress should be enough?

We can ask the following question: we already have userIP, so why should we use additional User-Agent data? One IP address can be assigned to multiple computers in the network. We need more information to target a single user. We can distinguish a single visitor using browser data.

Fingerprint demo

As a demonstration, let’s say we have a database of visitor names. Every unique user fingerprint is assigned to his first name. Our goal will be to display a greeting: Hello [user_name]! To simplify the example, we’re going to hardcode data in javascript, but the recommended way of storing such data will be a database or an API.

We’re going to use UAParser.js to get information about the Browser, Engine, OS, CPU, and Device type/model. It’s a javascript library, also available as an NPM package. Fingerprint value is hash generated from 5 values: the IP address and browser details.

Hello John!

Here is the entire application code written using React. On user visit, we’re asking for permission and then displaying greetings. The IP address is fetched using https://ip.nf/me.json. Execute the following command to install the required dependency:

npm install ua-parser-js

The main application:

// App.jsx
import React, { useState } from "react";
import './App.css';
import {Greetings} from "./Greetings";
function App() {
  const [permissionGranted, setPermissionGranted] = useState(false);
  return (
    <div className="App">
        <div>
          <h3>Website wants to use your IP address and User-Agent details.<br />Click below to consent:</h3>
          <button className={'myButton'} onClick={() => {setPermissionGranted(true)}}>I'm granting permission {permissionGranted ? '✓' : ''}</button>
        </div>
        {permissionGranted ? <Greetings /> : ''}
    </div>
  );
}
export default App;

The component for calculating the fingerprint and displaying greetings:

// Greetings.jsx
import React, {useState, useEffect} from 'react';
import { UAParser } from 'ua-parser-js';
export const Greetings = () => {
    const [ipAddress, setIpAddress] = useState(false);
    const [userFingerprint, setUserFingerPrint] = useState(false);
    const [firstName, setFirstName] = useState('Anonymous');
    const setIP = () => {
        fetch('https://ip.nf/me.json'
            , {
                headers: {
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                }
            }
        )
            .then(function (response) {
                return response.json();
            })
            .then(function (myJson) {
                if(myJson.ip.ip){
                    setIpAddress(myJson.ip.ip);
                    // setIpAddress("127.0.0.1");
                }
            });
    }
   const generateFingerprint = (ip, browserName, browserVersion, operatingSystem, language) =>{
        return generateHash(ip) + generateHash(browserName) + generateHash(browserVersion)
            + generateHash(operatingSystem) + generateHash(language);
    }
    const generateHash = (input) => {
        if(input){
            return input.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
        }
        return 0;
    }
    const setFingerprint = () => {
        const userAgentInfo = new UAParser();
        const browserName = userAgentInfo.getBrowser().name;
        const browserVersion = userAgentInfo.getBrowser().version;
        const platform = userAgentInfo.getOS().name;
        const userLang = (navigator.language) ? navigator.language : '';
        const temp1 = generateFingerprint(ipAddress, browserName, browserVersion, platform, userLang);
        if(temp1){
            setUserFingerPrint(temp1);
        }
    }
    const setNameFromDatabase = (fingerprint) => {
        // for demo purpose – data is hardcoded
        // for production application: use database storage or API
        // user fingerprint assigned to name
        const myDatabase = {
            3112300576: 'John',
            3112300577: 'Michael',
            3112300578: 'Anna'
        };
        if(myDatabase[fingerprint]) {
            setFirstName(myDatabase[fingerprint]);
        }
    }
    useEffect(() => {
        setIP();
    }, [])
    useEffect(() => {
        if(ipAddress){
            setFingerprint();
        }
        // eslint-disable-next-line
    }, [ipAddress])
    useEffect(() => {
        if(userFingerprint){
            setNameFromDatabase(userFingerprint);
        }
        // eslint-disable-next-line
    }, [userFingerprint])
    return (
        <>
            <h2>Hello {firstName}!</h2>
            { ipAddress && <p><strong>Your IP address: </strong> {ipAddress}</p> }
            <p><strong>Your Fingerprint: </strong> {userFingerprint}</p>
        </>
    );
}

The content of the CSS style:

body {
  background-color: #fff;
  background-image:
          linear-gradient(90deg, transparent 79px, #abced4 79px, #abced4 81px, transparent 81px),
          linear-gradient(#eee .1em, transparent .1em);
  background-size: 100% 1.2em;
}
.App {
  text-align: center;
  max-width: 350px;
  margin: 50px auto;
}
.myButton {
  background-color:#44c767;
  border-radius:28px;
  border:1px solid #18ab29;
  display:inline-block;
  cursor:pointer;
  color:#ffffff;
  font-size:17px;
  padding:16px 31px;
  text-decoration:none;
  text-shadow:0px 1px 0px #2f6627;
}
.myButton:hover {
  background-color:#5cbf2a;
}
.myButton:active {
  position:relative;
  top:1px;
}

Black text and green button on white background

More accurate targeting

We can add additional parameters for calculating fingerprint value to have more accurate results, e.g.: Geo Location, screen resolution, time of visit patterns, URL changes, a picture rendered with the HTML Canvas or WebGL, adBlock addon installed, a list of fonts in the system or some other advanced fingerprint signals. Instead of doing it manually, we can also use a ready-to-use NPM library: https://www.npmjs.com/package/@fingerprintjs/fingerprintjs.

That’s it for today’s tutorial. Make sure to follow us for other useful tips and guidelines and don’t forget to sign up for our newsletter.

 

Need help?

  • Looking for support from experienced programmers?

  • Need to fix a bug in the code?

  • Want to customize your webste/application?

ADD COMMENT

Your email address will not be published.

createIT Contact