Coder Social home page Coder Social logo

fingerprintjs / fingerprintjs-pro-react Goto Github PK

View Code? Open in Web Editor NEW
45.0 7.0 7.0 3.26 MB

Fingerprint Pro Wrapper for React Single Page Applications (SPA)

License: MIT License

JavaScript 11.65% TypeScript 86.15% Shell 2.20%
fingerprintjs react wrapper integration spa single-page-app single-page-application identification fingerprinting fraud-detection

fingerprintjs-pro-react's Introduction

Fingerprint logo

CI badge coverage Current NPM version Monthly downloads from NPM MIT license Discord server Discord server

Fingerprint Pro React

Fingerprint is a device intelligence platform offering 99.5% accurate visitor identification. Fingerprint Pro React SDK is an easy way to integrate Fingerprint Pro into your React application. It's also compatible with Next.js and Preact. See application demos in the examples folder.

Table of contents

Requirements

  • React 18 or higher
  • For Preact users: Preact 10.3 or higher
  • For Next.js users: Next.js 13.1 or higher
  • For Typescript users: Typescript 4.8 or higher

Note

This package assumes you have a Fingerprint Pro subscription or trial, it is not compatible with the source-available FingerprintJS. See our documentation to learn more about the differences between Fingerprint Pro and FingerprintJS.

Installation

Using npm:

npm install @fingerprintjs/fingerprintjs-pro-react

Using yarn:

yarn add @fingerprintjs/fingerprintjs-pro-react

Using pnpm:

pnpm add @fingerprintjs/fingerprintjs-pro-react

Getting started

In order to identify visitors, you'll need a Fingerprint Pro account (you can sign up for free). To get your API key and get started, see the Fingerprint Pro Quick Start Guide.

1. Wrap your application (or component) in <FpjsProvider>.

// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import { FpjsProvider /*, FingerprintJSPro */ } from '@fingerprintjs/fingerprintjs-pro-react'
import App from './App'

const root = ReactDOM.createRoot(document.getElementById('app'))

root.render(
  <FpjsProvider
    loadOptions={{
      apiKey: 'your-public-api-key',
      // region: 'eu',
      // endpoint: ['metrics.yourwebsite.com', FingerprintJSPro.defaultEndpoint],
      // scriptUrlPattern: ['metrics.yourwebsite.com/agent-path', FingerprintJSPro.defaultScriptUrlPattern],
    }}
  >
    <App />
  </FpjsProvider>
)

2. Use the useVisitorData() hook in your components to identify visitors

// src/App.js
import React from 'react'
import { useVisitorData } from '@fingerprintjs/fingerprintjs-pro-react'

function App() {
  const { isLoading, error, data } = useVisitorData()

  if (isLoading) {
    return <div>Loading...</div>
  }
  if (error) {
    return <div>An error occured: {error.message}</div>
  }

  if (data) {
    // Perform some logic based on the visitor data
    return (
      <div>
        Welcome {data.visitorFound ? 'back' : ''}, {data.visitorId}!
      </div>
    )
  } else {
    return null
  }
}

export default App

The useVisitorData hook also returns a getData method you can use to make an API call on command.

// src/App.js
import React, { useState } from 'react'
import { useVisitorData } from '@fingerprintjs/fingerprintjs-pro-react'

function App() {
  const { isLoading, error, getData } = useVisitorData({ tag: 'subscription-form' }, { immediate: false })
  const [email, setEmail] = useState('')

  if (isLoading) {
    return <div>Loading...</div>
  }
  if (error) {
    return <div>An error occurred: {error.message}</div>
  }

  return (
    <div>
      <form
        onSubmit={(e) => {
          e.preventDefault()
          getData()
            .then((data) => {
              // Do something with the visitor data, for example,
              // append visitor data to the form data to send to your server
              console.log(data)
            })
            .catch((error) => {
              // Handle error
            })
        }}
      >
        <label htmlFor='email'>Email:</label>
        <input type='email' value={email} onChange={(e) => setEmail(e.currentTarget.value)} />
        <button type='submit'>Subscribe</button>
      </form>
    </div>
  )
}

export default App
  • See the full code example in the examples folder.
  • See our Use cases page for open-source real-world examples of using Fingerprint to detect fraud and streamline user experiences.

Linking and tagging information

The visitorId provided by Fingerprint Identification is especially useful when combined with information you already know about your users, for example, account IDs, order IDs, etc. To learn more about various applications of the linkedId and tag, see Linking and tagging information.

Associate the visitor ID with your data using the linkedId or tag parameter of the options object passed into the useVisitorData() hook or the getData function:

// ...

function App() {
  const {
    isLoading,
    error,
    getData
  } = useVisitorData({
    linkedId: "user_1234",
    tag: {
      userAction: "login",
      analyticsId: "UA-5555-1111-1"
    }
  });

// ...

Caching strategy

Fingerprint Pro usage is billed per API call. To avoid unnecessary API calls, it is a good practice to cache identification results. By default, the SDK uses sessionStorage to cache results.

  • Specify the cacheLocation prop on <FpjsProvider> to instead store results in memory or localStorage. Use none to disable caching completely.
  • Specify the cache prop on <FpjsProvider> to use your custom cache implementation instead. For more details, see Creating a custom cache in the Fingerprint Pro SPA repository (a lower-level Fingerprint library used by this SDK).
  • Pass {ignoreCache: true} to the getData() function to ignore cached results for that specific API call.

Note

If you use data from extendedResult, pay additional attention to your caching strategy. Some fields, for example, ip or lastSeenAt, might change over time for the same visitor. Use getData({ ignoreCache: true }) to fetch the latest identification results.

Error handling

The getData function throws errors directly from the JS Agent without changing them. See JS Agent error handling for more details.

API Reference

See the full generated API reference.

Support and feedback

To ask questions or provide feedback, use Issues. If you need private support, please email us at [email protected]. If you'd like to have a similar React wrapper for the open-source FingerprintJS, consider creating an issue in the main FingerprintJS repository.

License

This project is licensed under the MIT license. See the LICENSE file for more info.

fingerprintjs-pro-react's People

Contributors

borislobanov92 avatar dependabot[bot] avatar finesse avatar ilfa avatar jurouhlar avatar lisenish avatar makma avatar molefrog avatar natapg avatar necipallef avatar semantic-release-bot avatar sshelomentsev avatar theunderscorer avatar valve avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

fingerprintjs-pro-react's Issues

Race Condition

From Sergey M.:

What happens under the hood:
await clientPromise.current is called by the immediate data request. The current value is undefined at this moment of time, so the await continues the execution very quickly.
clientPromise.current = client.init() is called. The clientPromise.current value is filled with the JS agent loading promise.
return client.getVisitorData() is called very soon after. The agent loading promise was not awaited, so the agent hasn’t loaded and the promise is rejected with a FPJSAgent hasn't loaded yet error.
The step 1 is executed before the step 2 (expected to be vice-versa I suppose) because React executes useEffect in order of element parenthood: from children to parents. The useVisitorData hook (that starts loading the data immediately) is a child of a FpjsProvider element (that initializes the client).

I did a quick fix to test my hypothesis. I replaced this useEffect with a useLayoutEffect to make the step 2 go before the step 1, and the error went away. This is a dirty solution, I don’t recommend it.
The error may not happened with the old version of JS agent because it loads almost instantly.
I’d implement a more robust solution that works no matter when getVisitorData or getData were called, even during an initial rendering.
Also I recommend handling JS agent loading errors because they cause the error too.

https://fpjs.myjetbrains.com/youtrack/issue/DASH-1039

PR: #8

Dont working in Firefox

I installed on NextJS, the latest version, it works great for google Chmmo, Vivaldi, Brave and Opera, but on FireFox of the following error:

error.message FPJSAgentError: Error: Network connection error
And:
Requisição cross-origin bloqueada: A diretiva de mesma origem (same origin policy) impede a leitura do recurso remoto em https://api.fpjs.io/?ci=js/3.6.1 (motivo: falha na requisição CORS). Código de status: (null).

image

Add useful example for the `getData` method

In the example below we show how to call the getData method but don't afford any useful scenarios of what we can do with that data.
We need to add a better scenario to the documentation.

<form
  onSubmit={(e) => {
    e.preventDefault()
    getData().then((data) => {
      if (data) {
        // do something with the visitor data
        // for example, append visitor data to the form data to send to your server
        console.log(data)
      }
    })
  }}
>

Installing fingerprintjs on next.js app router

Hi there!

When looking at the tutorial for installing fingerprintjs on next, it looks like the example is for Next page router:

// pages/_app.tsx
import {
  FpjsProvider
} from '@fingerprintjs/fingerprintjs-pro-react'
import {AppProps} from 'next/app'

export default function MyApp({Component, pageProps}: AppProps) {
  return (
    <FpjsProvider
      loadOptions={{
        apiKey: "<api key>"
      }}
    >
      <Component {...pageProps} />
    </FpjsProvider>
  )
}

I was wondering how this can be done with the new app router? Thank you!

I tried putting it in app/layout.tsx

<body className={inter.className}>
      <FpjsProvider
      loadOptions={{
        apiKey: "<api key>"
      }}>
      </FpjsProvider>
      {children}  
      </body>

and then calling it from a page:

import Image from "next/image";
import { useVisitorData } from '@fingerprintjs/fingerprintjs-pro-react'

export default function Home() {
  const {isLoading, error, data, getData} = useVisitorData(
    {extendedResult: true},
    {immediate: true}
  )

  return (
    <div>
      <button onClick={() => getData({ignoreCache: true})}>
        Reload data
      </button>
      <p>VisitorId: {isLoading ? 'Loading...' : data?.visitorId}</p>
      <p>Full visitor data:</p>
      <pre>{error ? error.message : JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

but I get "Failed to load the JS script of the agent".

Thank you for looking at this!

Some of options supported in SPA library are ignored

Because we memoize our getOptions:

const { extendedResult, timeout, tag, linkedId } = getOptions ?? {}
const memoizedOptions = useMemo(
  () => ({ extendedResult, timeout, tag, linkedId }),
  [extendedResult, timeout, tag, linkedId]
)

we skip the new products field recently introduced in our JavaScript Agent.

Module not found: Error: Package path . is not exported from package

I get this error locally:

ERROR in ./app/containers/SupportPage/index.tsx 43:34-83
Module not found: Error: Package path . is not exported from package /Users/sr/Documents/r2/node_modules/@fingerprintjs/fingerprintjs-pro-react (see exports field in /Users/sr/Documents/r2/node_modules/@fingerprintjs/fingerprintjs-pro-react/package.json)
 @ ./app/containers/Home/routes.tsx 54:38-71
 @ ./app/containers/Home/index.js 58:0-30 541:4-18 558:14-20 617:14-20 652:54-67
 @ ./app/containers/App/index.js 21:9-34
 @ ./app/app.js 45:0-33 101:38-41 109:40-112:3 109:2-112:4
node -v && npm -v
v18.14.2
9.6.0

Looking at this as the culprit: 4b0fe82

Particularly package.json:

  "exports": {
    "import": "dist/fp-pro-react.esm.js"
  },

The comment for the commit is fixes "appDir" problem in nextjs. I am not using nextjs, I am using webpack 5. Webpack is not liking this at all and I don't have the time to dive into a solution for y'all so letting you know here the issue it is giving.

The temporary solution for me was to downgrade to 2.2.0 via:

npm rm @fingerprintjs/fingerprintjs-pro-react && npm install --save @fingerprintjs/[email protected]

Posting this for anyone else who runs into this so it also does not take too many seconds of your precious existence.

Updating state in `useVisitorData` might cause a warning in React < 18

Hi! Prior to React 18 there was a warning that the library would show if you tried to set state in already unmounted component. Luckily, in the newest version they made this less strict so that application developers don't have to guard state updates with any additional checks.

There are three places where we update the state in an asynchronous function inside the useVisitor hook. Even though it's not the case anymore, since this is a library, there could be clients who still use React 17 or earlier, it would be nice if we could prevent any warnings from happening.

The fix can be really straight forward like:

const isComponentUnmounted = useRef<boolean>(false)

useEffect(() => {
  return () => isComponentUnmounted.current = true
}, [])

// inside an async func
if(isComponentUnmounted.current) return
setState(...)

`getData` dependency inside `useEffect`

As a developer I expect getData function to be immutable, so I can use it as a dependency inside useEffect. However it seems like getData function is re-created on each render.

Simple example

function Component({ changingPropName }) {
  const { getData } = useVisitorData()
  
  useEffect(() => {
    console.log('effect triggered', getData)
  }, [getData])

  return <div>{changingPropName}</div>
}

function Emulate() {
  const [counter, setCounter] = useState(1);

  useEffect(() => {
    setInterval(() => setCounter((state) => state + 1), 1000)
  }, [setCounter])

  return <Component changingPropName={counter} />
}

ReactDOM.render(<Emulate />, document.body)

Add option to ignore cache if "immediate" is set to true

Right now it you use the immediate flag to get data:

  const { getData, data, isLoading, error } = useVisitorData(
    { extendedResult: true },
    { immediate: true }
  );

there is no option to disable cache. It would be good if user could do something like this:

  const { getData, data, isLoading, error } = useVisitorData(
    { extendedResult: true, ignoreCache: true },
    { immediate: true }
  );

We can still keep the option to ignore cache in getData as well, and it should overwrite the option set in useVisitorData if it was passed there.

Can i get useVisitorData hook outside of react components?

I have a NextJS app but need to get access to the visitor data outside react components for networking purposes. What is the recommendation to get visitor data in an app using this SDK but needing to get this info outside of a react context?

Put extra arguments for the getData callback

I have an intention to use getData function like this:

const customParams = undefined;
const { getData } = useVisitorData(customParams, { immediate: false });
...

const onSubmit = async () => {
  const response = await axios.get(...);
  await getData({ ignoreCache: true }, { linkedId: response.id, tag: { action: 'EXAMPLE_ACTION' } })
}

I cannot do that as getData doesn't take extra arguments.
Which means I have to define custom arguments statically, when I initialize useVisitorData hook.

Could you please correct me if I am wrong with the usage or provide the solution for this specific case.
Thank you!

neither data, error, or isLoading from useVisitorData is set.

Hello folks!

I am building a next.js app and I have a code similar to the following example:

"use client";

import { useVisitorData } from "@fingerprintjs/fingerprintjs-pro-react";

export default function FingerprintComponent() {
  const { data, error, isLoading } = useVisitorData({ extendedResult: false });

  if (isLoading) {
    return <LoadingComponent />;
  }

  if (error) {
    return <ErrorComponent />;
  }

  if (data) {
    return <DataComponent fingerprint={data.visitorId} />;
  }

  return null;
}

Of course, the whole app is wrapped in the FpjsProvider.

Would you happen to know when can I expect this code to return null? My expectation is that this should never happen, as the data are either loading, loaded, or an error occured.

However, on some iOS devices, I noticed a behaviour where null is being returned for a couple of seconds and then the data is loaded and the output switches to DataComponent. It behaves as if isLoading was not set to true while the data is loading, but instead the code falls through all branches and returns null until data is present.

What is a good pattern to handle these kind of cases?

A simpler way to detect Preact

Hi! I was doing some digging into the source code and I noticed that the current approach of detecting Preact environment creates some constraints in the other parts of the library. If we just had some simpler, ideally synchronous way of Preact detection we would have:

  1. Avoided having an invisible span rendered by <FpjsProvider />. While it doesn't seem like a big deal, there could be some clients that expect the library doesn't produce any html, and could rely on some exotic CSS in their apps like body > div.
  2. Simplified the client initialization and loading logic and potentially avoided extra rendering at the startup.

I don't have a definitive answer yet, however I have compiled a list of tricks that might work here:

  1. A more hacky and less stable option: ctx = createContext({}); isPreact = ctx._id || ctx.__c (the property name is _id in the source code, but it becomes __c after mangling). Pros: works for Preact starting from 10.0.0 (the first release where hooks became generally available), doesn't require any additional component, completely synchronous. Cons: it's implementation specific and might break in the future.
  2. Check the number of arguments passed down to class component's render(). Preact always has two agruments. Pros: works in both 8.x and 10.x, it's an official documented difference and it is less likely to be changed in the current release Cons: it is not completely synchronous, however it doesn't output an html element.

Let me know what you think,
Cheers.

Breaks navigation in Next.js

I noticed that adding this library to an existing Next.js project according to the docs breaks the navigation (particularily next/link) on my entire site.

While trying to find out if this was due to my project, I extended the Next.js example in examples/next with a new test.tsx page and a link in index.tsx and could successfully reproduce the issue.

Clicking on the link changes the URL but doesn't navigate. This seems to have to do with the fact that FPJS is integrated in the _app.tsx layout. When I tried adding the provider on a single page only, things worked as expected.

How to reproduce?

Add test.tsx to examples/next/pages:

import type { NextPage } from 'next'

const Test: NextPage = () => <h1>test</h1>

export default Test

Add link to examples/next/pages/index.tsx:

import Link from 'next/link'

<Link href='/test'>
  <a>test</a>
</Link>

Click new link.

Allow `getData` to throw errors, allow multiple failed requests, error messages doesn't match library constants

Hello I've been getting some Client timeout validations for some users that try to log in from a mobile with low signal where in those cases I've setted up fallback to other types of security validations the problem is that the only way to handle those errors is from the error coming from useVisitorData I want to be able to handle those errors on a try/catch clause using getData instead.

Another issue that I've found is that the errors defined in @fingerprintjs/fingerprintjs-pro do not match exactly the errors coming from the API

import { ERROR_CLIENT_TIMEOUT } from '@fingerprintjs/fingerprintjs-pro';

The error message that is returned from the your service has this Error: prefixed on every error so its not possble to handle those errors like you mention in your documentation

ERROR_CLIENT_TIMEOUT !== "Error: Clear timeout"

Finally for my use case where I use getData when the user tries to log in once I get an invalid response it won't let me fetch another response unless I unmount the hook or refresh the page, I should be able to use getData and get responses from it on demand, not only once per component mounted.

Here is an screencast of the issue:

allow.multiple.req.mov

Failed to load the JS script of the agent

Hello. We're constantly getting this error

Error: Failed to load the JS script of the agent

On Microsoft Edge.

We're on versions

"@fingerprintjs/fingerprintjs-pro": "3.7.1",
"@fingerprintjs/fingerprintjs-pro-react": "1.3.1",

Javascript Agent Error Passing

In the documentation here, it states that getData throws errors directly from our Pro Agent without changing them. However, when calling getData, it occasionally errors with the full error object being:
{"name":"FPJSAgentError"}

I have not yet experienced receiving an error response other than this one and I would estimate that over 95% of my getData requests result in a successful response object. As there isn't much that I can do with this error response to figure out what went wrong, I am posting here in hopes that you have any pointers, answers, or suggestions.

What I would expect to happen is to receive one of the errors from here which this repo's documentation points to here.

Unfortunately, I have not been able to reproduce it myself.

Thanks in advance!

"@fingerprintjs/fingerprintjs-pro-react": "^2.3.3",
"react": "^16.13.1",

Using useVisitorData in NextJs '12.1.5' don't response data in useEffect

We are using fingerprintjs in NextJs but always in first load don't get data

How can I get the method information?

import { useVisitorData } from '@fingerprintjs/fingerprintjs-pro-react'
export default function Home() {
  const { isLoading: isLoadingFingerprint, error, data, getData } = useVisitorData({ extendedResult: true }, { immediate: true })
  useEffect(() => {
       getData({ ignoreCache: false })
        console.log(window.location.href)
        console.log(data)
  }, [])

Screenshot 2024-01-19 at 10 52 59 AM

Content Security Policy Issue in next js example repo

Hi there, I have been trying to implement the react fingerprint js npm package into my next js project and come up against CSP issues blocking requests to your servers.

I just cloned your next js example package and can confirm the same CSP issues occur here. Please could you help me understand how I can work around this please?

I have tried to update the CSP in the next.config.js based on these docs but still the requests are being blocked:
https://nextjs.org/docs/advanced-features/security-headers
https://dev.fingerprint.com/docs/js-agent-csp

Any help resolving this issue would be greatly appreciated. Please let me know if I can provide any further information

image

// next.config.js

/** @type {import('next').NextConfig} */


const ContentSecurityPolicy = `
	base-uri 'self';
	default-src 'self' 'unsafe-eval' 'unsafe-inline' https://*.fptls.com https://*.fptls2.com https://eu.api.fpjs.io https://fpnpmcdn.net;
	style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
`;

const securityHeaders = [
  {
    key: 'Content-Security-Policy',
    value: ContentSecurityPolicy.replace(/\s{2,}/g, ' ').trim(),
  },
];


const nextConfig = {
  reactStrictMode: true,
	async headers() {
		return [
			{
				source: '/(.*)',
				headers: securityHeaders,
			},
		]
	}
}

module.exports = nextConfig

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.