Obexal Docs

Docs/Access control/Conditional access

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.

Note

Country rules rely on a local GeoIP database. Install it and monitor that it stays present so your country rules actually take effect.

Note

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

ActionEffect at sign-in
allowThe sign-in proceeds normally
require_mfaAn MFA step-up is required before any session is opened
denyThe 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 version

Every 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.