Guides

Add device fingerprinting to a Vue or Nuxt app

Add device fingerprinting to a Vue 3 or Nuxt app: install the plugin or module, identify with a composable, and verify the result in a server route.

This tutorial adds device fingerprinting to a Vue 3 app or a Nuxt app. A plugin or module gives every component one shared identification as readonly refs, a composable asks for it when the visitor acts, and your server reads the stored result before it decides.

Before you start

  • Vue 3.3 or newer, or Nuxt 3 or 4. In Nuxt, use the module: it registers the Vue plugin and auto-imports the composables.
  • A public key with your app's origin in its allowed origins, and a secret key for your server.
  • A decision about consent: the SDK collects nothing until consent is granted.

Get your keys

  1. Step 1: Create your keys

    In the Fingerly dashboard, open Integration > SDK keys and create a public key in the development environment. Add the origin your app runs on as an allowed origin. A public key with no allowed origins refuses every browser request. Then create a secret key in the same environment for your server.

    Development keys run the same detection as production and cost nothing. See API keys and environments.

Add it to a Vue 3 app

  1. Step 1: Install the plugin

    npm install @fingerly/vue
    
    main.ts
    import { createApp } from 'vue'
    import { createFingerly } from '@fingerly/vue'
    import App from './App.vue'
    
    createApp(App)
      // consent: 'granted' only once you have consent (or another lawful basis)
      .use(createFingerly({ apiKey: 'fly_pk_development_…', consent: 'granted' }))
      .mount('#app')
    

    Installing collects nothing. consent defaults to pending, and you can change it at runtime with useFingerly().setConsent().

  2. Step 2: Identify when the visitor acts

    SignUpForm.vue
    <script setup lang="ts">
    import { ref } from 'vue'
    import { useIdentify } from '@fingerly/vue'
    
    const email = ref('')
    const { identify, isLoading, error } = useIdentify({ tag: 'signup' })
    
    async function onSubmit() {
      let requestId: string | undefined
      try {
        ;({ requestId } = await identify())
      } catch {
        // Carry on: your server treats a missing request ID as missing evidence.
      }
      await fetch('/api/signup', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email: email.value, requestId }),
      })
    }
    </script>
    
    <template>
      <form @submit.prevent="onSubmit">
        <input v-model="email" type="email" required />
        <button :disabled="isLoading">{{ isLoading ? 'Checking…' : 'Create account' }}</button>
        <p v-if="error">{{ error.message }}</p>
      </form>
    </template>
    

    Every returned field is a readonly ref, and isReady and isSuspicious are computed refs. A call while an identification is in flight gets that identification, and a finished result is reused until you call refresh().

Add it to a Nuxt app

  1. Step 1: Install the module

    npm install @fingerly/nuxt
    
    nuxt.config.ts
    export default defineNuxtConfig({
      modules: ['@fingerly/nuxt'],
      fingerly: {
        apiKey: 'fly_pk_development_…',
        // 'granted' only once you have consent (or another lawful basis)
        consent: 'granted',
      },
    })
    

    Or leave the key out of the config and set NUXT_PUBLIC_FINGERLY_API_KEY in the environment. The environment wins, and Nuxt reads it when the server starts, so one build can move through staging and production with a different key in each.

  2. Step 2: Identify in any component

    useIdentify and useFingerly are auto-imported. The plugin runs on the server and the client, so the composables work during server rendering, where they return the unidentified state. Identification itself only happens in the browser.

    components/SignUpForm.vue
    <script setup lang="ts">
    const { identify, isLoading } = useIdentify({ tag: 'signup' })
    
    async function onSubmit(form: { email: string }) {
      const { requestId } = await identify()
      await $fetch('/api/signup', { method: 'POST', body: { ...form, requestId } })
    }
    </script>
    

Verify in a server route

Whichever front end you use, decide on the server. In Nuxt, a Nitro route reads the stored event with your secret key and checks it belongs to the action. In a plain Vue app, put the same code in whichever backend you run.

server/api/signup.post.ts
import { load } from '@fingerly/node'

const fingerly = load({ secretKey: process.env.FINGERLY_SECRET_KEY! })

export default defineEventHandler(async (event) => {
  const { email, requestId } = await readBody(event)
  const result = await fingerly.events.get(requestId)
  if (result.tag !== 'signup') throw createError({ statusCode: 400 })
  // decide with result.suspect_level
})

Check the event is recent and has not been used before as well, and answer a pending event with a retry rather than a refusal. Server-side verification lists the four checks.

Get past content blockers

Mount a first-party proxy at the path the dashboard issues and give the plugin or module that path as endpoints. The Nuxt SDK page has the Nitro route, and proxy integrations explains the model.

Common questions

How do I get a visitor ID in Vue?

Install the plugin, call identify() from useIdentify() when the visitor acts, and read visitorId from the result. Send the requestId to your server and read the stored event there.

Does it work with the Options API?

Yes. The plugin adds this.$fingerly, so await this.$fingerly.identify() works in mounted() or a method. The Vue SDK page has an example.

Does it work with server rendering?

Yes. immediate runs in onMounted, which never runs on the server, and calling identify() during server rendering rejects with FingerlyServerError. In a custom SSR setup, create the plugin per app instance rather than at module scope.

How do I set the key per environment in Nuxt?

Set NUXT_PUBLIC_FINGERLY_API_KEY. The options are published to runtimeConfig.public.fingerly, so the key can differ between staging and production without a rebuild.

What if the key is missing?

The Nuxt build still succeeds because the key can arrive from the environment later. The plugin warns once per process, the app renders, and identify() fails with a missing-key error until a key is set.