Browse the library
Documentation librarySecurity
Access control
Access control lets you block, or allow only, requests from your software by IP address, network range or country. In Advanced Protection, rules are checked on every SDK call, before the license is even looked at, so a blocked network gets nowhere.
7 min read
When to use it
- Shut out an IP or a range that is guessing keys or sending odd traffic.
- Serve customers only in the countries where you distribute your product.
- Keep internal software inside the company network by allowing only the office range.
To keep one machine out, block it on the Devices tab of End users › Devices & sessions, see Devices, HWID and transfers.
Access rules only run in Advanced Protection, which is on every plan. In Standard Protection your rules stay saved but do nothing, and the form to add one is hidden. With no rules, anyone holding a valid license can sign in. See The two protection modes.
The three rule types
| Type | Value | Matches when |
|---|---|---|
| IP address | 203.0.113.7 | The request IP is exactly this string |
| Network (CIDR) | 203.0.113.0/24, 2001:db8::/32 | The request IP falls inside the range |
| Country | VN, JP | The request's country code is this value |
IP addresses and ranges
An IP address rule is an exact string match, so enter the bare address with no /prefix. A Network (CIDR) rule must carry a prefix such as /24. Both IPv4 and IPv6 work. For IPv6, prefer CIDR (even /128 for a single address): ranges compare by value, so the way an address is abbreviated does not matter.
The IP compared is the one the server sees. When the API sits behind a proxy, the server takes the real client IP from a header set by the edge (the TRUSTED_IP_HEADER environment variable, either cf-connecting-ip or x-real-ip).
Country
Enter the two-letter code in capitals, such as VN or US. The dashboard capitalises lower-case input for you.
The Country type only appears when the API runs behind Cloudflare with TRUSTED_IP_HEADER=cf-connecting-ip; the code comes from the cf-ipcountry header Cloudflare adds. With any other setup the type is hidden.
Adding and removing rules
- Open the app and go to App security › Protection, tab Access Rules. In Standard Protection, turn on Advanced Protection first.
- Under Type, pick IP address, Network (CIDR) or Country. The Value field shows a sample of the expected format.
- Under Action, pick Block or Allow only. With Allow only selected, the dashboard reminds you: "When any allow rule exists for a type, everything else of that type is blocked".
- Fill in Value and, optionally, a Note of up to 200 characters, such as "key guessing on Nov 2" or "office VPN".
- Click Add rule. It applies from the next request; there is nothing to ship.
The table below lists the newest rules first, with Action, Type, Value, Note and Created columns. Click the trash icon (Delete) at the end of a row and confirm to remove a rule.
There is no edit button. To change a rule's action or note, add the same Type and Value again: the server updates the existing rule instead of creating a duplicate, and the new note (even an empty one) replaces the old one.
Only Admin and Owner can add or remove rules; a Member can view them. See workspace roles.
How block and allow work together
- Block: a request matching any block rule is refused.
- Allow only: turns that type into an allow list. Once a type has at least one allow rule, a request must match one of that type's allowed values. Types without allow rules are not restricted.
- Allow lists of different types stack: a request must pass every one of them.
- Block always beats allow.
Say an app behind Cloudflare has three rules: Allow only country VN, Allow only country JP, Block range 203.0.113.0/24.
| Request | Result |
|---|---|
From VN, IP 198.51.100.4 | Allowed |
From JP, IP 198.51.100.9 | Allowed |
From VN, IP 203.0.113.50 | access_blocked, matches the block rule |
From US, IP 198.51.100.4 | access_blocked, not an allowed country |
Add Allow only range 198.51.100.0/24 and a customer in VN outside that range is refused too, because requests now have to pass both the country list and the network list.
Order of checks
Each request from the SDK goes through these steps and stops at the first one that fails:
- Signature, clock and nonce (
bad_signature,clock_skew,replayed_request). - Whether the app accepts sign-ins (
app_paused). - Whether Protect 24/7 has blocked the IP for now (
access_blocked), in both protection modes. - In Advanced Protection, Block rules (
access_blocked). - In Advanced Protection, each type's Allow only list (
access_blocked). - Only then sign-in mode, maintenance, minimum version and the license, as described in Core concepts.
What follows from this:
- Rules cover every client call:
init(),activate(),trial(),login(),heartbeat(),variables(),checkUpdates(),logout(). Software that is already running is refused on its next call, usuallyheartbeat(). - Rules do not end sessions; they refuse each request. Remove the rule and a session that has not expired carries on.
- The key is never examined for a blocked request, so a right key and a wrong one get the same answer.
- Rules do not apply to the dashboard, the customer portal or the Seller API. Seller API keys have their own Allowed IPs list under Business › Seller API keys.
The access_blocked error
A blocked request gets HTTP 403 with the code access_blocked. The message ends in "is blocked" when a block rule or a Protect IP block matched, and "is not allowed" when the request fell outside an allow list. The answer is signed like any other, so the SDK trusts it and throws a ClavisError:
import { ClavisError } from "@clavisauh/sdk";
try {
await clavis.init();
await clavis.activate(licenseKey);
} catch (err) {
if (err instanceof ClavisError && err.code === "access_blocked") {
// Do not retry in a loop: the answer stays the same until the rules change
showMessage("This network or device cannot connect. Please contact the seller.");
}
}
Protect 24/7 records requests refused by a rule as Blocked by an access rule signals (low severity), with IP and country. Because they stop before the licensing step, they do not appear in App security › Activity log.
Temporary IP blocks from Protect 24/7
Alongside your rules, Protect 24/7 keeps its own list of temporarily blocked IPs. These blocks apply in both protection modes, and the Access Rules tab lists them under Blocked IP addresses:
- Automatically: when Graduated Enforcement is set to Act on its own (this runs on Premium; Basic only alerts), an IP that reaches 20 wrong keys or passwords within 15 minutes (the Key brute-forcing signal) is blocked for 24 hours.
- By hand: in App security › Protection, under Needs you, click Block IP next to an IP subject and enter a reason. The incident drawer of an IP has the same button. The block lasts 24 hours and then lifts on its own.
- Lifting it early: on the Access Rules tab, click Lift block next to the IP and give a reason. Undo next to the IP blocked line in an incident does the same.
A Protect block covers exactly one IP, is checked before every rule, and wins even over Allow only. A blocked IP is tagged Blocked in the Most suspicious IPs part of the Sources panel. Changes take effect within about 15 seconds.
Managing rules with the Seller API
The Seller API has three endpoints: GET /apps/{appId}/rules (scope rules:read), POST /apps/{appId}/rules and DELETE /rules/{id} (scope rules:write). The body takes kind (ip, cidr, country), value, action (block or allow, block by default) and note. Country codes must already be in capitals; a value that does not fit its kind is refused with validation_failed. Rules added this way also run only in Advanced Protection.
curl -X POST https://api.example.com/seller/v1/apps/app_…/rules \
-H "Authorization: Bearer <seller_api_key>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: block-203-0-113-0" \
-d '{"kind":"cidr","value":"203.0.113.0/24","action":"block","note":"key guessing"}'
The server answers HTTP 201 with { "data": { "id": "rule_…", ... } }. Authentication and error codes are covered in the Seller API reference.