Skip to main content

Permission Checks

These endpoints answer RBAC-style (role-based access control) questions: "does the authenticated user have permission X?". All variants take a permission slug (or a list) and an optional context object. The optional context lets the server bring ABAC (attribute-based access control: rules over properties of the user, the resource and the request) policies into the decision — for example, checking document.edit with {document_owner_id: 123} in context can evaluate ownership conditions alongside the role check.

Single Permission Check​

POST /api/v1/authz/check

Return whether the authenticated user has a single permission.

Request Body​

FieldTypeRequiredDescription
permissionstringYesPermission slug (e.g., document.edit)
contextobjectNoContextual attributes for ABAC evaluation

Simple Check​

curl -X POST https://api.example.com/api/v1/authz/check \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"permission": "document.edit"
}'
{
"allowed": true,
"permission": "document.edit",
"user_id": "019d6190-9bba-7b5f-a0a5-07a6d10184d2"
}

Check with Context (ABAC)​

Include attributes about the target resource, request, or environment so ABAC policies can evaluate dynamic conditions.

curl -X POST https://api.example.com/api/v1/authz/check \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"permission": "document.edit",
"context": {
"document_id": 456,
"document_owner_id": 123,
"department": "engineering"
}
}'
Context Use Cases

Context is how you pass per-request facts — resource IDs, ownership, IP, device type, timestamps — that policies can reference. Attributes stored on the user or resource are merged with context at evaluation time.

Checking Another Subject​

All four check endpoints accept an optional subject object. Without it, the check is about the caller. With it, you can ask about another user or agent in the same organization — but only if the caller holds the authz.check permission or presents a token / API key with the authz:check scope. See Who May Ask About Whom.

FieldTypeRequiredDescription
subject.typestringYes (when subject is present)user or agent
subject.idstringYes (when subject is present)User UUID or agent id (agt_…)
curl -X POST https://api.example.com/api/v1/authz/check \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"permission": "document.edit",
"subject": {"type": "user", "id": "019d6190-9bba-7b5f-a0a5-07a6d10184d2"}
}'

The response's subject_id / user_id then identify the requested subject, not the caller.

StatusMeaning
403 insufficient_permissionsThe subject is not the caller and the caller lacks authz.check / authz:check
404 SUBJECT_NOT_FOUNDNo such user/agent in the caller's organization (only reachable with the privilege)
400 INVALID_SUBJECTsubject is present but not {type: "user" | "agent", id}

Bulk Permission Check​

POST /api/v1/authz/check-bulk

Check many permissions in one request. The response contains an individual result for each permission. This is the efficient way to populate a page that conditionally shows several actions.

Request Body​

FieldTypeRequiredDescription
permissionsarrayYesArray of permission slugs to check
contextobjectNoShared context for all checks

Example​

curl -X POST https://api.example.com/api/v1/authz/check-bulk \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"permissions": ["document.view", "document.edit", "document.delete", "document.share"],
"context": {
"document_id": 456
}
}'
{
"results": {
"document.view": true,
"document.edit": true,
"document.delete": false,
"document.share": true
},
"user_id": "019d6190-9bba-7b5f-a0a5-07a6d10184d2",
"context": {
"document_id": 456
}
}

Check Any (OR)​

POST /api/v1/authz/check-any

Return allowed: true if the user has at least one of the listed permissions. Useful when multiple permissions can unlock the same feature.

curl -X POST https://api.example.com/api/v1/authz/check-any \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"permissions": ["document.owner", "document.editor", "admin.all"]
}'
{
"allowed": true,
"permissions": ["document.owner", "document.editor", "admin.all"],
"user_id": "019d6190-9bba-7b5f-a0a5-07a6d10184d2"
}

Check All (AND)​

POST /api/v1/authz/check-all

Return allowed: true only if the user has every listed permission. Use for actions that require compound authority.

curl -X POST https://api.example.com/api/v1/authz/check-all \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"permissions": ["document.edit", "document.publish"]
}'
{
"allowed": false,
"permissions": ["document.edit", "document.publish"],
"user_id": "019d6190-9bba-7b5f-a0a5-07a6d10184d2"
}

Response Fields​

FieldTypeDescription
allowedbooleanWhether the permission check passed
permissionstringThe permission that was checked (single check)
permissionsarrayThe permissions that were checked (any/all)
resultsobjectMap of permission → result (bulk check)
subject_type / subject_idstringThe subject the decision is about (user or agent) — the caller unless a subject was supplied
user_idstring (UUID)Same as subject_id for user subjects (kept for backwards compatibility)
contextobjectThe context that was provided (empty object {} if none)

Common Patterns​

UI Button Visibility​

// Fetch permissions when loading a document editor
async function loadDocumentPermissions(documentId) {
const response = await fetch('/api/v1/authz/check-bulk', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
permissions: ['document.edit', 'document.delete', 'document.share'],
context: { document_id: documentId }
})
});

const { results } = await response.json();

// Show/hide UI elements based on permissions
document.getElementById('edit-btn').hidden = !results['document.edit'];
document.getElementById('delete-btn').hidden = !results['document.delete'];
document.getElementById('share-btn').hidden = !results['document.share'];
}

Server-Side Guard​

# Check permission before processing request
def require_permission(permission):
def decorator(func):
def wrapper(*args, **kwargs):
response = requests.post(
f'{LUMOAUTH_URL}/api/v1/authz/check',
headers={'Authorization': f'Bearer {get_token()}'},
json={'permission': permission}
)

if not response.json().get('allowed'):
raise PermissionDenied(f'Missing permission: {permission}')

return func(*args, **kwargs)
return wrapper
return decorator

@require_permission('document.delete')
def delete_document(document_id):
# User has permission, proceed with deletion
...