Attribute-based access control (ABAC) makes decisions based on attributes of the user, the resource, and the request context. Authdog evaluates Rego policies at the edge, in process, with no external policy server.
When to use ABAC
ABAC is the right choice when:
- Access depends on data, not a fixed job title
- Rules reference resource properties (amount, owner, department)
- Context matters (time, IP, location)
- You want to express complex conditions that would explode into hundreds of RBAC roles
Typical ABAC rules:
- "Managers can approve expenses under $5,000 in their own department"
- "Users can edit resources they own"
- "Access only during business hours from approved IPs"
- "Confidential documents require clearance level 3+"
Public API
ABAC policies are managed via the environment-scoped REST API at https://api.authdog.com/v1/tenants/{tenantId}/environments/{environmentId}, authenticated with a tenant or environment API secret.
| Operation | Endpoint | Method |
|---|---|---|
| List policies | /abac-policies |
GET |
| Create or update policy | /abac-policies |
POST |
| Validate policy | /abac-policies/validate |
POST |
| Delete policy | /abac-policies/{policyId} |
DELETE |
List policies
curl https://api.authdog.com/v1/tenants/{tenantId}/environments/{envId}/abac-policies \
-H "Authorization: Bearer $AUTHDOG_API_SECRET"Response:
{
"policies": [
{
"id": "pol_abc123",
"name": "Department approval",
"slug": "dept-approval",
"description": "Managers approve in their department",
"regoSource": "package authdog.authz\n\ndefault allow = false\n\nallow {\n input.action == \"approve\"\n input.user.department == input.resource.department\n}",
"enabled": true,
"createdAt": "2026-01-15T10:00:00Z",
"updatedAt": "2026-01-15T10:00:00Z"
}
]
}Create or update policy
Include id to update an existing policy.
curl -X POST https://api.authdog.com/v1/tenants/{tenantId}/environments/{envId}/abac-policies \
-H "Authorization: Bearer $AUTHDOG_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"name": "Owner can edit",
"slug": "owner-edit",
"description": "Resource owners can edit their own resources",
"regoSource": "package authdog.authz\n\ndefault allow = false\n\nallow {\n input.action == \"edit\"\n input.resource.owner == input.user.id\n}",
"enabled": true
}'Response:
{
"policy": {
"id": "pol_def456",
"name": "Owner can edit",
"slug": "owner-edit",
"enabled": true
}
}Validate policy
Stateless syntax and semantic validation. Nothing is persisted.
curl -X POST https://api.authdog.com/v1/tenants/{tenantId}/environments/{envId}/abac-policies/validate \
-H "Authorization: Bearer $AUTHDOG_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"regoSource": "package authdog.authz\n\ndefault allow = false\n\nallow { input.action == \"read\" }",
"slug": "read-policy"
}'Response:
{
"valid": true
}On error:
{
"valid": false,
"errors": [
{"line": 3, "column": 1, "message": "unexpected token"}
]
}Delete policy
curl -X DELETE https://api.authdog.com/v1/tenants/{tenantId}/environments/{envId}/abac-policies/{policyId} \
-H "Authorization: Bearer $AUTHDOG_API_SECRET"Policy input schema
Every policy receives an input object. Your application supplies resource, action, and custom context fields.
{
"user": {
"id": "user_abc123",
"email": "[email protected]",
"department": "engineering",
"clearanceLevel": 2,
"roles": ["editor"],
"groups": ["engineering-team"],
"metadata": {}
},
"resource": {
"type": "expense",
"id": "exp_456",
"department": "engineering",
"amount": 3500,
"owner": "user_abc123",
"metadata": {}
},
"action": "approve",
"context": {
"ip": "192.168.1.100",
"timestamp": "2026-11-09T14:30:00Z",
"userAgent": "Mozilla/5.0 ...",
"environment": "production"
}
}User attributes
Populated from the authenticated session:
| Attribute | Type | Description |
|---|---|---|
id |
string | User ID |
email |
string | Primary email |
department |
string | From user profile or IdP |
clearanceLevel |
number | Custom attribute |
roles |
array | RBAC roles (if also configured) |
groups |
array | Group memberships |
metadata |
object | Custom user metadata |
Resource attributes
Supplied by your application in the check request:
| Attribute | Type | Description |
|---|---|---|
type |
string | Resource type (expense, document, etc.) |
id |
string | Resource identifier |
| Custom fields | any | Your domain attributes (amount, owner, department) |
Context attributes
Supplied by your application or inferred:
| Attribute | Type | Description |
|---|---|---|
ip |
string | Client IP address |
timestamp |
string | ISO 8601 request time |
userAgent |
string | User agent header |
environment |
string | Authdog environment name |
Making authorization checks
Use the AuthZEN decision API to evaluate policies.
curl -X POST https://api.authdog.com/access/v1/evaluation \
-H "Authorization: Bearer $AUTHDOG_ENV_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"subject": {"type": "user", "id": "user_abc123"},
"action": {"name": "approve"},
"resource": {
"type": "expense",
"id": "exp_456",
"properties": {
"department": "engineering",
"amount": 3500,
"owner": "user_abc123"
}
},
"context": {
"ip": "192.168.1.100"
}
}'Response:
{
"decision": true
}Example policies
Department-scoped approval
package authdog.authz
default allow = false
allow {
input.action == "approve"
input.user.department == input.resource.department
input.resource.amount < 5000
}Owner can edit
package authdog.authz
default allow = false
allow {
input.action == "edit"
input.resource.owner == input.user.id
}Business hours only
package authdog.authz
default allow = false
allow {
hour := time.clock(time.parse_rfc3339_ns(input.context.timestamp))[0]
hour >= 9
hour < 17
}Clearance level check
package authdog.authz
default allow = false
allow {
input.action == "view"
input.resource.classification == "confidential"
input.user.clearanceLevel >= 3
}IP allowlist
package authdog.authz
default allow = false
allowed_networks = {"10.0.0.0/8", "192.168.0.0/16"}
allow {
some network
allowed_networks[network]
net.cidr_contains(network, input.context.ip)
}Combining multiple conditions
package authdog.authz
default allow = false
allow {
input.action == "transfer"
input.resource.amount < 10000
input.user.department == "finance"
is_business_hours
is_approved_ip
}
is_business_hours {
hour := time.clock(time.parse_rfc3339_ns(input.context.timestamp))[0]
hour >= 9
hour < 17
}
is_approved_ip {
startswith(input.context.ip, "10.")
}CLI usage
CLI support for ABAC commands is coming soon. For now, use the generic API command:
# List policies
authdog api GET /v1/tenants/{tenantId}/environments/{envId}/abac-policies
# Create policy
authdog api POST /v1/tenants/{tenantId}/environments/{envId}/abac-policies \
--data '{"name":"My policy","slug":"my-policy","regoSource":"package authdog.authz\n\ndefault allow = false\n\nallow { input.action == \"read\" }","enabled":true}'
# Validate policy
authdog api POST /v1/tenants/{tenantId}/environments/{envId}/abac-policies/validate \
--data '{"regoSource":"package authdog.authz\n\ndefault allow = false\n\nallow { true }","slug":"test"}'
# Delete policy
authdog api DELETE /v1/tenants/{tenantId}/environments/{envId}/abac-policies/{policyId}Console
Authorization → ABAC (when ABAC model is selected):
- Policies: list, create, edit, enable/disable
- Policy Editor: Monaco editor with Rego syntax highlighting
- Validation: real-time syntax checking
Related
- Authorization concepts — compare ABAC with RBAC and FGA
- ABAC quickstart — step-by-step setup
- AuthZEN API — vendor-neutral decision endpoint
- Rego language reference — full Rego documentation