Conditional access
Per-tenant rules that allow, require MFA or deny a sign-in based on network, time of day and country, evaluated at a single chokepoint.
Conditional access is a policy you define per organization: an ordered list of rules plus a default action, applied to every sign-in before any session is opened. Each rule combines up to three dimensions (network, time window, country) and resolves to one action: allow, require_mfa or deny.
How a sign-in is evaluated
Rules are evaluated in order, with firewall semantics: the first enabled rule whose constrained dimensions all match wins. If no rule matches, the default action applies (allow when unset). An empty dimension means no constraint on it, so a rule with no network, no window and no country matches every sign-in.
Evaluation happens at a single chokepoint in the login flow, whatever the primary factor: password, social login, SAML or LDAP. A deny is additionally enforced at session opening, so passwordless and passkey flows cannot bypass it either.
The posture is availability first: if the policy store is unreachable, the sign-in proceeds as allow rather than locking everyone out.
Rule dimensions
Networks
A list of CIDR blocks or bare IP addresses (IPv4 or IPv6). An empty list matches any network.
Time window
Days of week (ISO, 1 is Monday through 7 is Sunday), a start time "HH:MM" (inclusive) and an end time (exclusive), interpreted in an IANA timezone such as Europe/Paris (UTC when empty). Empty fields mean no constraint on that part.
Countries
A list of ISO 3166-1 alpha-2 codes (for example FR). The country of the sign-in IP is resolved entirely locally, from a .mmdb database on the platform itself: no external geolocation API is ever called, and the user's IP never leaves the platform. When self-hosting, the database is mounted via GEOIP_DB_PATH, see Configuration.
Country rules rely on a local GeoIP database. Install it and monitor that it stays present so your country rules actually take effect.
The device is not a rule dimension. A new device is a risk signal, handled by risk-based access, which combines with this static policy.
Actions
| Action | Effect at sign-in |
|---|---|
allow | The sign-in proceeds normally |
require_mfa | An MFA step-up is required before any session is opened |
deny | The sign-in is refused with a generic error |
If require_mfa applies to a user who has no enrolled factor, the sign-in is refused: the user must enroll a factor from a trusted network first. See Multi-factor authentication.
The IP denylist
Separate from the rules, each tenant has an IP denylist. A blocked IP is refused before the password is even verified, and before any rate-limit or lockout counter is touched, with a generic error that leaks nothing. It is managed with GET /v1/admin/ip-blocks, POST /v1/admin/ip-blocks and DELETE /v1/admin/ip-blocks/{ip}.
You cannot lock yourself out
Every application of the policy is checked against your own current connection. A policy is refused if it would deny your current IP, or if it would require_mfa while your account has no enrolled second factor (the step-up would be impossible, so you would be locked out at your next sign-in). The error message tells you to add an allow rule for your network or enroll a factor first. This guard also runs when restoring a previous version.
Manage the policy over the API
Both endpoints require the tenant:manage permission, with an Admin API token (Authorization: Bearer obx_...). If your organization uses a custom domain, it replaces accounts.obexal.com.
curl -sS https://accounts.obexal.com/v1/admin/access-policy \
-H "Authorization: Bearer $OBEXAL_API_TOKEN"{
"policy": {
"rules": [
{
"name": "Office and VPN",
"networks": ["203.0.113.0/24", "198.51.100.7"],
"action": "allow",
"enabled": true
}
],
"defaultAction": "require_mfa"
},
"knownActions": ["allow", "require_mfa", "deny"]
}curl -sS -X PUT https://accounts.obexal.com/v1/admin/access-policy \
-H "Authorization: Bearer $OBEXAL_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"rules": [
{"name": "Office and VPN", "networks": ["203.0.113.0/24"], "action": "allow", "enabled": true},
{"name": "Business hours", "networks": [], "action": "allow", "enabled": true,
"window": {"days": [1, 2, 3, 4, 5], "start": "07:00", "end": "20:00", "tz": "Europe/Paris"}}
],
"defaultAction": "deny"
}'
# 204 No Content: applied, audited, archived as a new versionEvery application is written to the audit log and archived as an immutable version: see Policy versioning and simulation. The access policy is also part of the declarative configuration bundle, see Policy as code.
Limits
- At most 100 rules per policy, 256 networks and 250 countries per rule.
- Rule names are capped at 120 characters.
- Country rules take effect only when a local GeoIP database is installed and readable.