Invisible captcha
In this guide we'll walk through how to use Stytch Device Fingerprinting to implement "invisible captcha" bot protection for your application.
In a traditional captcha, every user needs to complete an interactive task, which adds friction while still being vulnerable to bot attacks. Invisible captcha solutions only prompt suspicious-looking users to complete a task, providing a seamless experience for most real users.
With Stytch Device Fingerprinting, you can easily implement invisible captcha protection. We use this ourselves in our auth products to protect email magic links from being consumed by corporate email scanners, and you can use it in any flow where you don't want automated traffic.
Goals
The goal of invisible captcha is to protect your application from automated abuse while maintaining a smooth user experience for legitimate users. This means our approach is to:
- Identify and block automated requests transparently
- Allow legitimate users to proceed without interruption
- Prompt for additional verification only when necessary
Stytch Device Fingerprinting is hardened against reverse engineering and spoofing, so it provides more reliable bot detection than other invisible captcha providers.
High level flow
To implement invisible captcha with Stytch Device Fingerprinting, you will first get the fingerprint and verdict:
- The frontend client will call GetTelemetryID() and send the result to the backend for processing.
- The backend server will call the Fingerprint Lookup API to fetch the results from Stytch.
- In the Lookup response, Stytch will recommend a verdict action of ALLOW, BLOCK, or CHALLENGE.
Then, you will act on the recommended verdict action:
- If the verdict is ALLOW, you can let the user proceed.
- If the verdict is BLOCK or CHALLENGE, you can serve an interactive challenge or reject the client entirely.
Getting a fingerprint and verdict
To get a fingerprint and Stytch's recommended verdict, you'll call GetTelemetryID() on the frontend and Fingerprint Lookup on the backend.
For a step-by-step tutorial, see Getting started with the Device Fingerprinting API.
Once you look up the fingerprint, you will be able to take action.
Act on the recommended verdict action
ALLOW response
The Fingerprint Lookup response may return an ALLOW verdict, like this:
{
"verdict": {
"action": "ALLOW",
"detected_device_type": "...",
"is_authentic_device": true,
"reasons": [...]
}
// other fields omitted
}
The verdict.action of ALLOW indicates that this device looks like a normal user on a normal device. You can immediately allow the user to carry out the protected action without any additional friction. In pseudocode:
lookupResponse = client.fraud.fingerprint.lookup({telemetry_id})
if (lookupResponse.verdict.action == "ALLOW") {
return doProtectedAction()
}
BLOCK response
The Fingerprint Lookup response may return an BLOCK verdict, like this:
{
"verdict": {
"action": "BLOCK",
"detected_device_type": "UNKNOWN",
"is_authentic_device": false,
"reasons": ["HEADLESS_BROWSER_AUTOMATION"]
}
// other fields omitted
}
The verdict.action of BLOCK indicates that this device looks automated or malicious. Usually this is due to known automation warning flags detected by Stytch, or due to banned device signatures from known past abuse across Stytch's network.
You should prevent the user from completing the action.
lookupResponse = client.fraud.fingerprint.lookup({telemetry_id})
if (lookupResponse.verdict.action == "BLOCK") {
return genericErrorResponse()
}
We recommend returning opaque error messages to avoid giving information to the attacker. For example, if the protected action is a login, you can return a standard "Username or password is incorrect" message instead of telling the user they are blocked by Device Fingerprinting. See Enforcement with Device Fingerprinting for more guidance.
CHALLENGE response
The Fingerprint Lookup response may return an CHALLENGE verdict, like this:
{
"verdict": {
"action": "CHALLENGE",
"detected_device_type": "UNKNOWN",
"is_authentic_device": false,
"reasons": ["VIRTUAL_MACHINE"]
}
// other fields omitted
}
The verdict.action of CHALLENGE indicates that this request is unusual in a way that may or may not be malicious. For example, virtual machines are often used in automated attacks, but they can also be used in legitimate enterprise browser environments.
Depending on how sensitive your action is, you have several options:
- Treat CHALLENGE like ALLOW, which is more permissive
- Treat CHALLENGE like BLOCK, which is more strict
- On CHALLENGE, prompt the user for additional verification. For example:
- Complete a traditional captcha-style interactive task
- Answer a simple question about your service
- Step-up with multi-factor authentication (MFA)
lookupResponse = client.fraud.fingerprint.lookup({telemetry_id})
if (lookupResponse.verdict.action == "CHALLENGE") {
return simpleChallenge("What is the name of this site?")
}
The amount of friction to add for a CHALLENGE depends on your application and how sensitive the action is.
Modifying decisioning logic
Fraud and abuse prevention is an adversarial problem, so you may find instances where the recommended action is not correct. In traditional captcha systems, you might have trouble tuning a risk threshold with an acceptable false positive and false negative rate. Since Stytch provides rich contextual data, you can modify the decisioning logic precisely based on many dimensions.
- You can set Rules to allow or block specific fingerprints or IP geolocation features.
- You can override verdict reasons if the default action is not ideal for your typical users.
- You can write custom code using any of the attributes of the Fingerprint Lookup response or your own application context
What's next?
Learn how to get started with Device Fingerprinting in just a few minutes. Then, check out the go-live checklist for production readiness.
Access to Device Fingerprinting is gated to help protect our users from reverse engineering. If you are interested in invisible captcha use cases, please contact us to request access.
Contact sales