Overview
This tutorial shows how to implement Fingerprint to prevent loan application fraud, where applicants try to game the system by submitting multiple applications using slightly altered personal details, fake identities, or devices that show signs of automation or tampering. You’ll begin with a starter app that includes a mock loan application page and a basic review flow. From there, you’ll add the Fingerprint JavaScript agent to identify each visitor and use server-side logic with Fingerprint data to check if they’ve already submitted an application. Additionally, you’ll see how to use Smart Signals to block or flag suspicious devices submitting applications. This way, you can flag or block repeat submissions from the same device or suspicious sources. By the end, you’ll have a sample app that detects duplicate or high-risk loan applications per visitor and can be customized to fit your use case and business rules. This tutorial uses just plain JavaScript and a Node server with SQLite on the back end. For language- or framework-specific setups, see our quickstarts.Estimated time: < 15 minutes
Prerequisites
Before you begin, make sure you have the following:- A copy of the starter repository (clone with Git or download as a ZIP)
- Node.js (v20 or later) and npm installed
- Your favorite code editor
- Basic knowledge of JavaScript
1. Create a Fingerprint account and get your API keys
- Sign up for a free Fingerprint trial, or log in if you already have an account.
- After signing in, go to the API keys page in the dashboard.
- Save your public API key, which you’ll use to initialize the Fingerprint JavaScript agent.
- Create and securely store a secret API key for your server. Never expose it on the client side. You’ll use this key on the backend to retrieve full visitor information through the Fingerprint Server API.
2. Set up your project
- Clone or download the starter repository and open it in your editor.
Terminal
- This tutorial will be using the
loan-riskfolder. The project is organized as follows:
Project structure
- Install dependencies:
Terminal
- Copy or rename
.env.exampleto.env, then add your Fingerprint API keys:
Terminal
- Start the server:
Terminal
- Visit http://localhost:3000 to view the mock loan application page from the starter app. You can test out the basic coupon flow by changing the inputs and clicking Request loan. You’ll notice you can apply for loans with new personal details an unlimited amount of times.
3. Add Fingerprint to the front end
In this step, you’ll load the Fingerprint client when the page loads and trigger identification when the user clicks Request loan. The client returns both avisitorId and a requestId. Instead of relying on the visitorId from the browser, you’ll send the requestId to your server along with the loan data. The server will then call the Fingerprint Events API to securely retrieve the full identification details, including the verified visitorId and risk signals such as browser tampering or bot activity.
- At the top of
public/index.js, load the Fingerprint JavaScript agent:
public/index.js
- Make sure to change
regionto match your workspace region (e.g.,eufor Europe,apfor Asia,usfor Global (default)). - Near the bottom of
public/index.js, the Request loan button already has an event handler set up for submitting a loan application. Inside this handler, request visitor identification from Fingerprint using theget()method and include the returnedrequestIdwhen sending the loan data to the server:
public/index.js
get() method sends signals collected from the browser to Fingerprint servers, where they are analyzed to identify the visitor. The returned requestId acts as a reference to this specific identification event, which your server can later use to fetch the full visitor details.
For lower latency in production, check out our documentation on using Sealed Client Results to return full identification details as an encrypted payload from the get() method.
4. Receive and use the request ID to get visitor insights
Next, pass therequestId through to your loan application logic, initialize the Fingerprint Server API client, and fetch the full visitor identification event so you can access the trusted visitorId and Smart Signals.
- In the back end, the
server/server.jsfile already defines API routes for the app. Notice that the/api/loan-riskroute simply passes the request body to arequestLoanfunction that is defined in theserver/loans.jsfile.
server/server.js
- The
server/loans.jsfile contains the logic for handling loan applications and will contain the rest of the tutorial. Start by importing and initializing the Fingerprint Server API client there, loading your environment variables withdotenv, and importing thecryptopackage for use later:
server/loans.js
- Make sure to change
regionto match your workspace region (e.g.,EUfor Europe,APfor Asia,Globalfor Global (default)). - Update the
requestLoanfunction to also extract therequestIdfrom the passed request body and use it to fetch the full identification event details from Fingerprint:
server/loans.js
requestId, the Fingerprint server client will retrieve the full data for the visitor identification request. The returned object will contain the visitor ID, IP address, device, and browser details, and Smart Signals like bot detection, browser tampering detection, VPN detection, and more.
You can see a full example of the event structure and test it with your own device in our demo playground.
For additional checks to ensure the validity of the data coming from your front end, view how to protect from client-side tampering and replay attacks in our documentation.
5. Block bots and suspicious devices
This optional step uses the Bot Detection and Suspect Score Smart Signals,
which are only available on paid plans.
event object includes the Bot Detection Smart Signal that flags automated activity, making it easy to reject bot traffic.
This signal returns good for known bots like search engines, bad for automation tools, headless browsers, or other signs of automation, and notDetected when no bot activity is found.
- Continuing in the
requestLoanfunction inserver/loans.js, after the existingloanDataobject, check the bot signal returned in theeventobject:
server/loans.js
- Below the bot detection check, add a condition that reads the Suspect Score from the
eventobject and flags the loan application for review if it exceeds a chosen threshold (for example, 20):
server/loans.js
6. Detect inconsistent loan applications per visitor
Next, we’ll use the trustedvisitorId from the event object to check for past loan applications submitted from the same device. When a new application is submitted, we will check if a record exists for the current visitorId. If it does, we’ll compare the stored personal details to the new ones. If they don’t match, it indicates the applicant is trying to change their personal details to start a new loan, and the application is denied. This makes fraud detection enforceable at the device level, not just by account or identity details. Even if someone uses new personal information, fake identities, or alternate emails, their applications are still linked to the same device, helping you catch attempts to game the system.
Note: The starter app includes a SQLite database with the following table already created for you to store loan applications:
SQLite database tables
- First, within the
requestLoanfunction, retrieve thevisitorIdfrom theeventobject and update theloanDatavariable to include it:
server/loans.js
- Next, we’ll add some helper functions to the bottom of the
server/loans.jsfile. The first one will allow us to generate a hash based on personal details. This is just a simple way to quickly compare details for this demo:
server/loans.js
- The next helper function retrieves the first application submitted by the visitor (if it exists) and compares the personal details from that application to the current one:
server/loans.js
- Additionally, we can limit the total number of applications allowed by a visitor, regardless of the details entered. Add another helper function to get the current number of applications made by the visitor:
server/loans.js
- Also update the existing
recordLoanApplicationhelper function to include thevisitorIdwhen recording loan applications:
server/loans.js
- Return to the
requestLoanfunction and use the helper functions to enforce consistent personal details per visitor and limit the number of applications:
server/loans.js
This is a minimal example to show how to implement Fingerprint. In a real
application, make sure to implement proper security practices, error checking,
and data handling that align with your production standards.
7. Test your implementation
Now that everything is wired up, you can test the full protected loan application flow using the loan application page.- Start your server if it isn’t already running and open http://localhost:3000:
Terminal
- Reset the database by clicking “Reset demo DB” at the bottom of the loan application page.
- Keep the default values in the form and click Request loan. The application should be approved.
- Change some personal details, like the name, and submit again. The second application will be rejected because the same visitor has already applied with different personal details.
- Refresh the page and try submitting a few more applications using the default details. After several attempts, new submissions will be blocked.
- Open the page in incognito and try to submit a new loan application, your visitor ID will remain the same and the application is still blocked.
- Bonus: Test the flow using a headless browser or automation tool to see bot detection in action. A sample script is available in
test-bot.js. While your app is running, run the script withnode test-bot.jsin your terminal and observe that the automated applications are denied.