Authorization Verification
Verify your agents check permissions before executing privileged actions.
Authorization verification detects OWASP LLM06: Excessive Agency - when agents perform actions without proper permission checks.
The Problem
AI agents often have access to powerful tools. Without authorization checks, any caller can invoke any action - including destructive operations like data deletion or financial transactions.
# VULNERABLE: No authorization check
def delete_customer(customer_id: str) -> str:
database.execute(f"DELETE FROM customers WHERE id = '{customer_id}'")
return f"Deleted customer {customer_id}"What Inkog Detects
Inkog’s universal_missing_authz rule detects tool calls without preceding authorization checks.
Detection Rule
id: universal_missing_authz
title: Missing Authorization Check Before Tool Execution
severity: HIGH
category: governance
compliance_mapping:
owasp: ["LLM06"]
nist_ai_rmf: ["GOVERN 1.2"]
iso_42001: ["6.3"]Supported Auth Patterns
Inkog recognizes 10+ authorization patterns:
| Type | Patterns Detected |
|---|---|
| RBAC | user.role, has_permission(), check_role() |
| OPA | opa.query(), @opa_authorized, policy.check() |
| OpenFGA | fga.check(), openfga.allowed() |
| AuthZen | authzen.evaluate(), pdp.access_check() |
| Django | @permission_required, user.has_perm() |
| Flask | @login_required, current_user.can() |
| FastAPI | Depends(verify_token), @requires() |
| Generic | authorize(), check_access(), is_authorized() |
Framework Examples
Python (Generic)
Vulnerable Pattern
from crewai import Tool
def delete_customer(customer_id: str) -> str:
# VULNERABLE: No authorization check!
database.execute(f"DELETE FROM customers WHERE id = '{customer_id}'")
return f"Deleted customer {customer_id}"
delete_tool = Tool(
name="delete_customer",
func=delete_customer,
description="Delete a customer"
)Compliant Pattern (Simple RBAC)
from crewai import Tool
def delete_customer(customer_id: str, caller: User) -> str:
# COMPLIANT: Authorization check before action
if not caller.has_permission("delete_customer"):
raise PermissionDenied("Not authorized to delete customers")
database.execute(f"DELETE FROM customers WHERE id = '{customer_id}'")
return f"Deleted customer {customer_id}"Compliant Pattern (OPA)
from crewai import Tool
from opa_client import OPAClient
opa = OPAClient()
def delete_customer(customer_id: str, caller: User) -> str:
# COMPLIANT: OPA policy check
decision = opa.query("data.customers.allow_delete", {
"user": caller.id,
"customer": customer_id
})
if not decision.allow:
raise PermissionDenied(f"OPA denied: {decision.reason}")
database.execute(f"DELETE FROM customers WHERE id = '{customer_id}'")
return f"Deleted customer {customer_id}"Compliant Pattern (OpenFGA)
from openfga_sdk import OpenFgaClient
fga = OpenFgaClient()
def delete_customer(customer_id: str, caller: User) -> str:
# COMPLIANT: OpenFGA relationship check
allowed = fga.check(
user=f"user:{caller.id}",
relation="can_delete",
object=f"customer:{customer_id}"
)
if not allowed:
raise PermissionDenied("Not authorized for this customer")
database.execute(f"DELETE FROM customers WHERE id = '{customer_id}'")
return f"Deleted customer {customer_id}"LangChain
Vulnerable Pattern
from langchain.tools import Tool
delete_tool = Tool(
name="delete_record",
func=delete_record,
description="Delete a database record"
# VULNERABLE: No permission check
)Compliant Pattern
from langchain.tools import Tool
from functools import wraps
def requires_permission(permission: str):
"""Decorator that checks permission before tool execution."""
def decorator(func):
@wraps(func)
def wrapper(*args, caller=None, **kwargs):
if not caller or not caller.has_permission(permission):
raise PermissionDenied(f"Requires {permission}")
return func(*args, **kwargs)
return wrapper
return decorator
@requires_permission("delete_record")
def delete_record(record_id: str) -> str:
# COMPLIANT: Permission checked by decorator
return database.delete(record_id)
delete_tool = Tool(
name="delete_record",
func=delete_record,
description="Delete a database record"
)Salesforce Agentforce
Vulnerable Pattern
<objectPermissions>
<!-- VULNERABLE: Wildcard permissions -->
<object>*</object>
<allowDelete>true</allowDelete>
</objectPermissions>Compliant Pattern
<objectPermissions>
<!-- COMPLIANT: Scoped to specific objects -->
<object>Account</object>
<allowRead>true</allowRead>
<allowDelete>false</allowDelete>
</objectPermissions>
<objectPermissions>
<object>Opportunity</object>
<allowRead>true</allowRead>
<allowEdit>true</allowEdit>
<allowDelete>false</allowDelete>
</objectPermissions>Best Practices
1. Use Decorators/Middleware
Centralize authorization logic rather than scattering checks:
@requires_auth("admin")
def dangerous_operation():
...2. Principle of Least Privilege
Grant minimum necessary permissions:
# BAD: Broad permissions
tools = [all_database_tools]
# GOOD: Specific, limited permissions
tools = [read_customer_tool, update_email_tool]3. Separate Read and Write
Distinguish between read-only and write operations:
# Read operations - lower authorization bar
@requires_permission("view_customers")
def get_customer(id): ...
# Write operations - higher authorization bar
@requires_permission("delete_customers")
def delete_customer(id): ...4. Log Authorization Decisions
Audit both grants and denials:
def check_permission(user, action, resource):
allowed = evaluate_policy(user, action, resource)
audit_log.record(user, action, resource, allowed)
return allowedCompliance Evidence
Inkog generates authorization compliance reports:
{
"framework_mapping": {
"OWASP LLM06": {
"status": "PASS",
"finding_count": 0,
"details": {
"tools_with_auth_checks": 12,
"tools_total": 12,
"auth_patterns_detected": ["rbac", "opa"]
}
}
}
}