Skip to content

Policy Evaluation

When an identity attempts an action, VeraID evaluates all applicable policies and returns a deterministic allow or deny decision. This page explains the evaluation algorithm, conflict resolution rules, and how to test policy evaluation via the API.

Evaluation Order

VeraID evaluates policies using a strict four-step algorithm:

Step 1: Explicit DENY Always Wins

If any active DENY policy matches the request (subject, resource, action, and all conditions pass), access is immediately denied. No further evaluation occurs.

DENY match found → ACCESS DENIED (stop)

Step 2: At Least One ALLOW Required

If no DENY policies match, VeraID checks for matching ALLOW policies. At least one active ALLOW policy must match (subject, resource, action, and all conditions pass) for access to be granted.

No DENY match + ALLOW match found → ACCESS GRANTED

Step 3: All Conditions Must Pass

For a policy to “match,” all of its conditions must pass. A policy with three conditions requires all three to be satisfied. If any condition fails, the policy is skipped as if it does not exist.

Policy conditions: [TIME_WINDOW ✓, IP_RANGE ✓, RATE_LIMIT ✗] → Policy does not match

Step 4: Default Implicit Deny

If no policies match at all — no DENY and no ALLOW — access is denied by default. This is the implicit deny principle: anything not explicitly allowed is forbidden.

No matching policies → ACCESS DENIED (implicit deny)

Priority-Based Evaluation

Policies are evaluated in priority order, with higher-priority policies evaluated first. Priority is an integer value; the default is 100.

PriorityUse Case
1000+Emergency lockdown policies
500–999Security overrides and compliance rules
100–499Standard operational policies
1–99Low-priority fallback policies

Example: Priority in action

Consider two policies for the same identity:

// Policy A: Priority 200 (evaluated first)
{
"name": "allow-read-all",
"effect": "ALLOW",
"priority": 200,
"subjects": ["identity:svc-data-pipeline"],
"resources": ["*"],
"actions": ["read:*"],
"conditions": []
}
// Policy B: Priority 500 (evaluated first due to higher priority)
{
"name": "deny-secrets-read",
"effect": "DENY",
"priority": 500,
"subjects": ["identity:svc-data-pipeline"],
"resources": ["secrets:*"],
"actions": ["read:*"],
"conditions": []
}

For a request to read:secrets/db-password:

  • Policy B (DENY, priority 500) matches first and denies access.
  • Policy A (ALLOW, priority 200) is never reached.

Even if the priorities were reversed, the result would be the same — DENY always wins regardless of priority.

Policy Evaluation API

Test policy evaluation without making actual access requests using the evaluation endpoint.

Request

Terminal window
curl -X POST \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"identityId": "id_abc123def456",
"resource": "prod:database:users",
"action": "write:records",
"context": {
"ipAddress": "10.0.1.50",
"timestamp": "2026-03-19T14:30:00Z",
"geoLocation": {
"country": "US",
"region": "us-east-1"
}
}
}' \
https://app.veraid.io/api/v1/policies/evaluate
FieldTypeRequiredDescription
identityIdstringYesThe identity attempting the action.
resourcestringYesThe target resource identifier.
actionstringYesThe action being attempted.
contextobjectNoRuntime context for condition evaluation.
context.ipAddressstringNoSource IP address for IP range conditions.
context.timestampstringNoISO 8601 timestamp for time window conditions. Defaults to current time.
context.geoLocationobjectNoGeographic location for geo-fencing conditions.

Response

{
"allowed": false,
"matchedPolicies": [
{
"id": "pol_deny_prod_writes",
"name": "deny-prod-writes-after-hours",
"effect": "DENY",
"priority": 500,
"conditionsEvaluated": [
{
"type": "TIME_WINDOW",
"passed": true,
"detail": "Current time 14:30 UTC is within window"
}
]
},
{
"id": "pol_allow_db_access",
"name": "allow-db-access",
"effect": "ALLOW",
"priority": 100,
"conditionsEvaluated": [
{
"type": "IP_RANGE",
"passed": true,
"detail": "10.0.1.50 is within 10.0.0.0/8"
}
]
}
],
"reason": "Denied by policy 'deny-prod-writes-after-hours' (explicit DENY)"
}
FieldTypeDescription
allowedbooleanWhether the action is permitted.
matchedPoliciesobject[]All policies that matched the request, regardless of effect.
matchedPolicies[].conditionsEvaluatedobject[]Result of each condition check for the policy.
reasonstringHuman-readable explanation of the decision.

Complete Policy Example

The following example demonstrates a production-grade policy that combines multiple condition types to secure a deployment pipeline.

{
"name": "secure-production-deploy",
"description": "Allow production deployments only during business hours, from the corporate network, with rate limiting and MFA verification",
"priority": 300,
"subjects": [
"group:deploy-bots",
"identity:svc-github-actions"
],
"resources": [
"prod:deployments:*",
"prod:releases:*"
],
"actions": [
"deploy:create",
"deploy:rollback",
"release:publish"
],
"conditions": [
{
"type": "TIME_WINDOW",
"timezone": "America/New_York",
"windows": [
{
"days": ["MON", "TUE", "WED", "THU", "FRI"],
"startTime": "09:00",
"endTime": "17:00"
}
]
},
{
"type": "IP_RANGE",
"allowedRanges": [
"10.0.0.0/8",
"172.16.0.0/12"
],
"deniedRanges": [
"10.0.99.0/24"
]
},
{
"type": "RATE_LIMIT",
"maxRequests": 20,
"windowSeconds": 3600
}
],
"effect": "ALLOW",
"isActive": true
}

How this policy is evaluated:

  1. Subject match — The request must come from an identity in the deploy-bots group or the specific svc-github-actions identity.
  2. Resource match — The target resource must match prod:deployments:* or prod:releases:*.
  3. Action match — The action must be one of deploy:create, deploy:rollback, or release:publish.
  4. Time window — The request must occur on a weekday between 9 AM and 5 PM Eastern.
  5. IP range — The request must originate from 10.0.0.0/8 or 172.16.0.0/12, but not from 10.0.99.0/24.
  6. Rate limit — The identity must not have exceeded 20 requests in the last hour.

If all six checks pass, access is granted. If any check fails, this policy does not match and the evaluator continues to the next policy.

Evaluation Flow Diagram

The complete evaluation flow for a single request:

Request received
├─ Collect all active policies matching subject + resource + action
├─ Sort by priority (highest first)
├─ For each matching DENY policy:
│ └─ Evaluate all conditions
│ ├─ All conditions pass → ACCESS DENIED (stop)
│ └─ Any condition fails → Skip this policy
├─ For each matching ALLOW policy:
│ └─ Evaluate all conditions
│ ├─ All conditions pass → ACCESS GRANTED (stop)
│ └─ Any condition fails → Skip this policy
└─ No policies matched → ACCESS DENIED (implicit deny)

Debugging Policy Decisions

When access is unexpectedly denied, use these strategies to diagnose the issue:

  1. Check the evaluation API response — The matchedPolicies array shows which policies matched and how each condition was evaluated.
  2. Verify policy is active — Ensure isActive is true on the relevant policy.
  3. Check subject membership — Confirm the identity belongs to the expected groups.
  4. Review condition details — The conditionsEvaluated array includes a detail field explaining why each condition passed or failed.
  5. Look for DENY policies — A DENY policy at any priority level overrides all ALLOW policies.
Terminal window
# Evaluate with full context for debugging
curl -X POST \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"identityId": "id_abc123def456",
"resource": "prod:database:users",
"action": "write:records",
"context": {
"ipAddress": "10.0.1.50",
"timestamp": "2026-03-19T14:30:00Z",
"geoLocation": {
"country": "US",
"region": "us-east-1"
}
}
}' \
https://app.veraid.io/api/v1/policies/evaluate | jq .

Next Steps

  • Policy Overview — Review the full PBAC model and policy structure.
  • Policy Conditions — Configure time windows, IP ranges, rate limits, and geo-fencing.
  • Alerts — Set up alerts for policy violations and unusual access patterns.