Fraud
With Fraud, you can detect GPS spoofing, proxy and VPN usage, and device tampering.
Along with Regions, you can also detect a user's country and state and mark specific regions as allowed or denied to comply with regulations.
Together, Fraud and Regions provide the following user context:
{ "fraud": { "verified": true, "passed": false, "bypassed": false, "blocked": false, "mocked": true, "jumped": false, "compromised": false, "inaccurate": false, "proxy": false, "sharing": false, "lastMockedAt": "2023-07-27T17:18:28.536Z", "lastJumpedAt": "2023-07-27T17:18:28.536Z", "lastCompromisedAt": null, "lastInaccurateAt": null, "lastProxyAt": null, "lastSharingAt": null }, "country": { "code": "US", "name": "United States", "flag": "🇺🇸", "passed": true, "allowed": true }, "state": { "code": "RI", "name": "Rhode Island", "passed": false, "allowed": true, "distanceToBorder": 192.3, "inBufferZone": true, "inExclusionZone": false }}
#
QuickstartFirst, sign up for Radar and get an API key.
Then, enable Fraud on the Settings page.
From there, integrate the SDK, complete the steps below, and call Radar.trackVerified()
or Radar.trackVerifiedToken()
. Radar will perform fraud and jurisdiction checks as described below.
#
How it worksYou can call Radar.trackOnce()
to accurately detect a user's current geofences, current place, or current country and state.
However, users can spoof a device's location. For example, a gaming app user may spoof their location to access sports betting features only available in specific states. Or, a retail app user may spoof their location to access offers only available inside a store geofence.
To ensure you can trust a user's location, you can call Radar.trackVerified()
or Radar.trackVerifiedToken()
instead. Radar will collect a variety of fraud signals and perform fraud and jurisdiction checks, calculating flags that you can use for fraud detection and geo-compliance in your app.
#
Fraud flagsIf you call Radar.trackVerified()
or Radar.trackVerifiedToken()
, Radar exposes the following flags in user.fraud
:
mocked
: Indicates whether a user's location is being mocked, such as in a simulator or using a location spoofing app (e.g., Fake GPS location).jumped
: Indicates whether the user moved too far too fast (e.g., "jumped" across the country in only a few seconds).compromised
: Indicates whether the user's device or app has been compromised according to the Play Integrity API on Android or App Attest service on iOS.inaccurate
: Indicates whether the user's location accuracy is too low to pass verification.sharing
: Indicates whether the user is screen sharing (e.g., using TeamViewer).proxy
: Indicates whether the user's IP address is a known proxy or VPN.verified
: Indicates whether the request was made with SSL pinning configured successfully.
While you can work with individual flags, Radar also exposes a single user.fraud.passed
flag that indicates whether all fraud checks passed.
Additionally, the lastMockedAt
, lastJumpedAt
, lastCompromisedAt
, lastInaccurateAt
, lastProxyAt
, and lastSharingAt
timestamps indicate the last time that the user failed each fraud check.
#
Bypassing checks for testingIf desired, you can bypass fraud checks for individual users using the Mark as Bypassed button on the user detail page.
If a user is marked as bypassed, user.fraud.bypassed = true
and user.fraud.passed = true
, regardless of whether the user passes fraud checks.
#
Blocking usersYou can also manually block a user using the Mark as Blocked button on the user detail page.
If a user is blocked, user.fraud.blocked = true
and user.fraud.passed = false
, regardless of whether the user passes fraud checks.
#
Allowed states and countriesWith Regions, gaming customers can mark specific jurisdictions (i.e., countries and states) as allowed or denied to comply with regulations. For example, a sportsbook or daily fantasy sports app may only be allowed to operate in specific US states.
If you call Radar.trackVerified()
or Radar.trackVerifiedToken()
, Radar exposes your settings in user.country.allowed
and user.state.allowed
.
Additionally, you can enable buffer zones and exclusion zones for different states. If buffer zones and exclusion zones are enabled, user.state.inBufferZone
and user.state.inExclusionZone
indicates whether the user is within a buffer zone or exclusion zone.
While you can work with individual flags, Radar also exposes user.state.passed
and user.country.passed
flags that indicate whether all jurisdiction checks passed.
#
Platform-specific configuration#
Android#
Initialize SDKTo support the sharing
flag on Android, pass fraud = true
to Radar.initialize()
.
Radar.initialize( context = this, publishableKey = "prj_test_pk...", fraud = true)
#
Play Integrity APITo support the compromised
flag on Android, enable the Play Integrity API on the Settings page.
If the user's device or app does not meet basic integrity checks, user.fraud.compromised = true
.
You must add the Play Integrity API dependency before calling Radar.trackVerified()
.
Add the dependency to the dependencies
section of your app's build.gradle
file:
dependencies { implementation 'com.google.android.play:integrity:1.2.0'}
If Radar.trackVerified()
returns ERROR_FORBIDDEN
, check the logs. The Play Services version on the device may be out of date.
#
SSL pinningTo enable SSL pinning and prevent man-in-the-middle attacks, add a res/xml/network_security_config.xml
file:
<?xml version="1.0" encoding="utf-8"?><network-security-config> <!-- for React Native --> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">localhost</domain> </domain-config>
<!-- for SSL pinning --> <domain-config cleartextTrafficPermitted="false"> <domain includeSubdomains="true">api-verified.radar.io</domain> <pin-set> <pin digest="SHA-256">15ktYXSSU2llpy7YyCgeqUKDBkjcimK/weUcec960sI=</pin> <pin digest="SHA-256">15ktYXSSU2llpy7YyCgeqUKDBkjcimK/weUcec960sI=</pin> </pin-set> </domain-config></network-security-config>
Learn more about Network Security Configuration on Android.
#
iOS#
App AttestTo support the compromised
flag on iOS, enable App Attest and configure a list of valid App IDs (e.g., A1B2C3D4E5.com.yourapp.app1,A1B2C3D4E5.com.yourapp.app2
) on the Settings page.
If App Attest indicates that the user's device or app has been compromised, user.fraud.compromised = true
.
App Attest requires iOS 14 and above. If Radar.trackVerified()
returns ERROR_FORBIDDEN
, check the logs. The iOS version on the device may not support App Attest.
#
SSL pinningTo enable SSL pinning and prevent man-in-the-middle attacks, add the following to your Info.plist
file:
<key>NSAppTransportSecurity</key><dict> <key>NSAllowsArbitraryLoads</key> <false/> <key>NSPinnedDomains</key> <dict> <key>api-verified.radar.io</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSPinnedLeafIdentities</key> <array> <dict> <key>SPKI-SHA256-BASE64</key> <string>15ktYXSSU2llpy7YyCgeqUKDBkjcimK/weUcec960sI=</string> </dict> <dict> <key>SPKI-SHA256-BASE64</key> <string>15ktYXSSU2llpy7YyCgeqUKDBkjcimK/weUcec960sI=</string> </dict> </array> </dict> </dict></dict>
Learn more about SSL pinning on iOS.
#
Web and desktopThe web SDK supports the mocked
, proxy
, and jumped
flags with Radar.trackOnce()
.
On desktop, if the end user installs the Radar Verify Mac or Windows app, the web SDK also supports the sharing
flag with Radar.trackVerified()
. If Radar.trackVerified()
returns ERROR_DESKTOP_APP
, it means the Radar Verify app is not running.
#
Verifying user locationsOnce the above configuration is complete, you can verify a user's location with just a few lines of code.
#
Manual tracking#
Standard callbackCall Radar.trackVerified()
on app open, periodically, or before sensitive transactions. Then, check user.fraud.passed
, user.state.allowed
, and/or user.country.allowed
to allow or deny access.
- Swift
- Kotlin
- Web
- React Native
- Capacitor
- Flutter
Radar.trackVerified { (status, location, events, user) in if user?.fraud?.passed == true && user?.country?.allowed == true && user?.state?.allowed == true { // allow access to feature } else { // deny access to feature, show error message }}
Radar.trackVerified { status, location, events, user -> if (user?.fraud?.passed == true && user.country?.allowed == true && user.state?.allowed == true) { // allow access to feature } else { // deny access to feature, show error message }}
Radar.trackVerified().then((result) => { const { user } = result; if (user.fraud.passed && user.country.allowed && user.state.allowed) { // allow access to feature } else { // deny access to feature, show error message }}).catch((err) => { // deny access to feature, show error message});
Radar.trackVerified().then((result) => { const { user } = result; if (user.fraud.passed && user.country.allowed && user.state.allowed) { // allow access to feature } else { // deny access to feature, show error message }}).catch((err) => { // deny access to feature, show error message});
Radar.trackVerified().then((result) => { const { user } = result; if (user?.fraud?.passed && user?.country?.allowed && user?.state?.allowed) { // allow access to feature } else { // deny access to feature, show error message }}).catch((err) => { // deny access to feature, show error message});
var res = await Radar.trackVerified();// do something with res['status'], res['location'], res['events'], res['user']
Then, look up the user and check fraud flags server-side for an additional layer of verification.
curl "https://api.radar.io/v1/users/56db1f4613012711002229f4" \ -H "Authorization: prj_live_sk_..."
#
Token callbackAlternatively, set a JSON Web Token (JWT) secret key on the Settings page.
Then, call Radar.trackVerifiedToken()
to get a signed JWT containing a payload with user.fraud
, user.state
, and user.country
instead. Send the JWT to your server to verify and decode it.
- Swift
- Kotlin
- Web
- React Native
- Capacitor
- Flutter
Radar.trackVerifiedToken { (status, token) in // send token to server}
Radar.trackVerifiedToken { status, token -> // send token to server}
Radar.trackVerified().then((result) => { const { token } = result; // send token to server}).catch((err) => { // deny access to feature, show error message});
Radar.trackVerified().then((result) => { const { token } = result; // send token to server}).catch((err) => { // deny access to feature, show error message});
Radar.trackVerifiedToken().then((result) => { const { token } = result; // send token to server}).catch((err) => { // deny access to feature, show error message});
var res = await Radar.trackVerifiedToken();// do something with res['status'], res['token']
Finally, verify and decode the JWT server-side using your secret key and a JWT library. If the token is valid, check user.fraud.passed
, user.state.passed
, and/or user.country.passed
in the payload to allow or deny access.
For example, in JavaScript:
const jwt = require('jsonwebtoken');
try { const decoded = jwt.verify(token, SECRET_KEY); const { user } = decoded; // token is valid, check user.fraud, user.state, user.country to allow or deny access} catch(err) { // token is invalid, deny access to feature, show error message}
#
Automatic trackingIn addition to manually calling Radar.trackVerified()
or Radar.trackVerifiedToken()
, you can also call Radar.startTrackingVerified()
and Radar will automatically verify the user's location at regular intervals or on network connection changes.
For example, to automatically verify the user's location every 5 minutes (300 seconds), without ranging beacons, and with token callbacks:
- Swift
- Kotlin
- Capacitor
Radar.setVerifiedDelegate(delegate)
Radar.startTrackingVerified(token: true, interval: 300, beacons: false)
// in delegate
func didUpdateToken(_ token: String) { // send token to server}
Radar.setVerifiedReceiver(receiver)
Radar.startTrackingVerified(token = true, interval = 300, beacons = false)
// in receiver
override fun onTokenUpdated(context: Context, token: String) { // send token to server}
Radar.addListener('token', (result) => { const { token } = result; // send token to server});
Radar.startTrackingVerified({ token: true, interval: 300, beacons: false,});
#
Error handlingOn errors or failed fraud checks, you may want to display a message to the end user. For example:
- On
ERROR_PERMISSIONS
: Unable to determine your location. Please make sure you've granted location permissions and try again. - On
ERROR_LOCATION
: Unable to determine your location. Please make sure location services and wi-fi are enabled and try again. - On
ERROR_NETWORK
: Unable to determine your location. Please make sure you're connected to the Internet and try again. - On
country.allowed == false
orstate.allowed == false
: Unable to verify your location. Please make sure you're in an allowed area and try again. - On
fraud.proxy == true
: Unable to verify your location. Please disconnect from any VPNs or proxy servers you may be using and try again. - On other error cases, or as a fallback: Unable to verify your location. Please contact support.