Skip to content

Recipe: Deny Tor and VPN logins

The platform can identify Tor exit nodes and commercial VPN services from the incoming IP address before a single credential check runs. This recipe turns that signal into a hard block — users on those networks cannot complete a login at all.

Deny any login attempt that originates from a Tor exit node or a known commercial VPN. The user sees a generic access-denied message; the audit log records the specific reason. No second factor is offered; the session is never issued.

This pattern is appropriate for high-value B2B platforms and regulated industries — banking, financial services, healthcare, government — where the compliance posture explicitly prohibits anonymous-network access. If your goal is to add friction for suspicious logins without a hard block, use the Step-up MFA on risk score recipe instead. That pattern preserves access for legitimate users on VPNs while still challenging elevated-risk sessions.

A Login flow where the Post-Auth stage reads two flags from event.request.asn and routes any request where either is true to a Deny terminal. Requests that pass go straight to Issue Session.

Login flow
Stage: Pre-Login
Block: Identity Lookup
Block: Password Check
Stage: Post-Auth
Block: Decision <-- reads event.request.asn.*
Branch: blocked <-- is_tor OR is_vpn == true
Block: Deny <-- terminates the flow
Branch: proceed (default) <-- neither flag set
Stage: Post-Login
Block: Issue Session

This recipe has two variants: a plain Decision-block version for the common case, and an optional custom action variant for tenants that need to allowlist specific corporate ASNs before applying the VPN block.

Go to Flows, click Login, then Edit.

Step 2 — Add a Decision block to Post-Auth

Section titled “Step 2 — Add a Decision block to Post-Auth”

In the Post-Auth stage, open the block picker and drag a Decision block onto the canvas.

Open the Decision block's settings. Add two branches:

Branch labelWhen expressionRoute to
blockedevent.request.asn.is_tor == true || event.request.asn.is_vpn == trueDeny block
proceed(default)Issue Session

On the blocked branch, drag a Deny block. Set the Reason field to network-blocked. This string appears in the audit log so you can trace the denial; it is never shown to the user. The user sees whatever message your tenant's denial template is configured to display.

Click Save, review the diff, and click Publish.

Configure it — custom action variant (allowlist corporate ASNs)

Section titled “Configure it — custom action variant (allowlist corporate ASNs)”

If your users connect through a corporate VPN whose ASN you know, you can allowlist it before applying the block. This prevents legitimate employees from being denied access.

Go to Custom Actions, click + New Action, and name it network-policy-check. Set the trigger to Login. Paste the following code:

// network-policy-check
// Allow traffic from known corporate ASNs even when VPN flag is set.
// Deny everything else on Tor or commercial VPN.
const KNOWN_CORPORATE_ASNS = new Set([
// Replace these with your organization's ASN numbers.
// Find your ASN at: https://bgp.he.net/
64512,
64513,
]);
const asn = event.request.asn;
// Corporate VPN: allow through even if the VPN flag is set.
if (KNOWN_CORPORATE_ASNS.has(asn.number)) {
api.log('info', `Corporate ASN allowed: ${asn.number} (${asn.org})`);
return;
}
// Tor exit node: always deny, no exceptions.
if (asn.is_tor) {
api.deny('network-blocked:tor');
return;
}
// Commercial VPN not on the corporate allowlist: deny.
if (asn.is_vpn) {
api.deny('network-blocked:vpn');
return;
}

Click Save, then enable the action.

Step 2 — Wire the action into the Login flow

Section titled “Step 2 — Wire the action into the Login flow”

Go to Flows, click Login, then Edit. In the Post-Auth stage, drag a Run Custom Action block and select network-policy-check. The action calls api.deny() directly, so no downstream Decision or Deny block is needed — the flow halts as soon as api.deny() is called.

Click Save and Publish.

Open the Test panel on the Decision block (block-only variant) or use the Test tab on your custom action (action variant).

For the block-only variant, use these test payloads:

// Payload 1: Tor exit node — should hit the Deny block
{
"event.request.asn.is_tor": true,
"event.request.asn.is_vpn": false,
"event.request.asn.number": 0
}
// Payload 2: commercial VPN — should hit the Deny block
{
"event.request.asn.is_tor": false,
"event.request.asn.is_vpn": true,
"event.request.asn.number": 0
}
// Payload 3: clean residential connection — should reach Issue Session
{
"event.request.asn.is_tor": false,
"event.request.asn.is_vpn": false
}

For the custom action variant, paste each payload into the Test tab in the Custom Actions editor. Confirm that the API calls panel shows api.deny(...) for payloads 1 and 2, and no API calls for payload 3.

Connect to an actual Tor Browser and sign in to your tenant. The login should fail with your tenant's generic denial message. Disconnect from Tor and sign in from your normal network — the login should complete.

Tor blocking has real costs. Privacy-conscious users, journalists in high-censorship regions, and security researchers are legitimate users. Blocking Tor is a policy decision — not a best practice — and it should be documented and reviewed regularly.

VPN classification is heuristic. The platform's ASN classification identifies commercial VPN providers, but the dataset is not exhaustive. A VPN that launched after the last threat-feed update will not be flagged. Conversely, some legitimate corporate or university networks may share ASN prefixes with flagged ranges. The custom action variant's allowlist mechanism is the correct mitigation for the latter case.

Do not surface the blocking reason to the user. The Deny block's reason field is audit-log only. If your error template says something like "Tor access is not permitted," you are telling attackers exactly what signal to evade. Use a generic message such as "Access from your current network is not permitted."

Consider step-up as the default. For most tenants, the Step-up MFA on risk score recipe is the better starting point. It challenges Tor and VPN sessions with MFA rather than denying them, which keeps the door open for legitimate users while still protecting against account takeover.