fingerprintjs / fingerprintjs-pro-ios

Fingerprint Identification SDK for accurate iOS device identification.
https://fingerprint.com
Other
37 stars 11 forks source link

WebView approach not working #6

Closed makma closed 3 years ago

makma commented 3 years ago

Whem I follow the guidelines for the webview approach, vendorId tag is not reflected.

Steps to reproduce:

  1. Create view in swiftUI:
    
    import SwiftUI
    import WebKit

struct ContentView: View { var body: some View { Webview(url: URL(string: "https://eager-hermann-4ea017.netlify.app")!) } }

struct Webview: UIViewRepresentable { let url: URL

func makeUIView(context: UIViewRepresentableContext<Webview>) -> WKWebView {
    let webview = WKWebView()

    let vendorId = UIDevice.current.identifierForVendor?.uuidString ?? "undefined"

    let script = WKUserScript(source: "window.fingerprintjs.vendorId = \(vendorId)", injectionTime: .atDocumentStart, forMainFrameOnly: false)

    webview.configuration.userContentController.addUserScript(script)

    let request = URLRequest(url: self.url, cachePolicy: .returnCacheDataElseLoad)
    webview.load(request)

    return webview
}

func updateUIView(_ webview: WKWebView, context: UIViewRepresentableContext<Webview>) {
    let request = URLRequest(url: self.url, cachePolicy: .returnCacheDataElseLoad)
    webview.load(request)
}

}


2. Create hmlt page with FPJS on the URL specified in the `Webview(url: URL(string: "https://eager-hermann-4ea017.netlify.app")!)`
``` html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta name="description" content="Webpage description goes here" />
    <meta charset="utf-8" />
    <title>Change_me</title>
  </head>
  <body>
    <div class="container"></div>
    <script>
      const vendorId =
        window.fingerprintjs && window.fingerprintjs.vendorId
          ? window.fingerprintjs.vendorId
          : 'not-in-ios-context'

      function initFingerprintJS() {
        // Initialize an agent at application startup.
        const fpPromise = FingerprintJS.load({
          token: 'tQUwQQOuG9TNwqc6F4I2',
          region: 'eu',
          endpoint: 'https://fp.martinmakarsky.com',
        })

        // Get the visitor identifier when you need it.
        fpPromise
          .then((fp) =>
            fp.get({
              tag: {
                deviceId: vendorId,
                deviceType: 'ios',
              },
              linkedId: 'makma',
              extendedResult: true,
            })
          )
          .then((result) => {
            console.log(result.visitorId);
            var el = document.createElement("h1");
            el.innerText = result.visitorId;
            document.body.appendChild(el);
          })
      }
    </script>
    <script
      async
      src="https://cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs-pro@3/dist/fp.min.js"
      onload="initFingerprintJS()"
    ></script>
  </body>
</html>
  1. Run iOS app, get visitorId and check tag on server API Expected output:
    ...
            "linkedId": "makma",
            "timestamp": 1629209018949,
            "time": "2021-08-17T14:03:38Z",
            "url": "https://eager-hermann-4ea017.netlify.app/",
            "tag": {
                "deviceId": "vendor-id-from-ios-context",
                "deviceType": "ios"
            }
    ...

    Actual output:

    ...
            "linkedId": "makma",
            "timestamp": 1629209018949,
            "time": "2021-08-17T14:03:38Z",
            "url": "https://eager-hermann-4ea017.netlify.app/",
            "tag": {
                "deviceId": "not-in-ios-context",
                "deviceType": "ios"
            }
    ...

Note: I've also tried to set window object after webview.load(request)

Questions and suggestions:

pvels commented 3 years ago

@makma Yep, its documentation issue

This is happen because the fingerprintjs object undefined, as well as vendorId string must be escaped

Here is right solution:

let vendorId = UIDevice.current.identifierForVendor.flatMap { "'\($0.uuidString)'" } ?? "undefined"

WKUserScript(source: "window.fingerprintjs = { 'vendorId' : \(vendorId) }",
             injectionTime: .atDocumentStart,
             forMainFrameOnly: false)

@spalt08 can you please suggest better solution for script source and keypath? And also maybe fingerprintjs object should be avoided as unnecessary?

makma commented 3 years ago

Yep, @spalt08 has already written to me, I've checked this solution and it really works, gonna update readme with full swiftUI example in a minute, thx