Authorization
EvidentSource uses a claims-based authorization model. Permissions are encoded in the evs:grants JWT claim and evaluated at request time.
Role Hierarchy
Section titled “Role Hierarchy”┌─────────────────────────────────────────────────────────────────────────────┐│ GLOBAL SCOPE ││ ││ database_creator ─────────────────────────► CREATE_DATABASE ││ │└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐│ DATABASE SCOPE ││ ││ ┌─────────────────────────────────────────────────────────────────┐ ││ │ reader │ ││ │ QUERY_EVENTS + RENDER_STATE_VIEWS │ ││ └─────────────────────────────────────────────────────────────────┘ ││ │ ││ ▼ ││ ┌─────────────────────────────────────────────────────────────────┐ ││ │ writer │ ││ │ reader + APPEND_TRANSACTIONS + EXECUTE_STATE_CHANGES │ ││ └─────────────────────────────────────────────────────────────────┘ ││ ││ ┌─────────────────────────────────────────────────────────────────┐ ││ │ deployer │ ││ │ PUBLISH_STATE_CHANGES + PUBLISH_STATE_VIEWS │ ││ └─────────────────────────────────────────────────────────────────┘ ││ ││ ┌─────────────────────────────────────────────────────────────────┐ ││ │ database_deleter │ ││ │ DELETE_DATABASE │ ││ └─────────────────────────────────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────────────────────┘Roles Reference
Section titled “Roles Reference”| Role | Scope | Permissions | Use Case |
|---|---|---|---|
database_creator | Global | Create new databases | Admin, provisioning systems |
reader | Database | Query events, render state views | Read-only access, reporting |
writer | Database | Reader + append transactions, execute state changes | Application services |
deployer | Database | Publish state changes and state views | CI/CD pipelines |
database_deleter | Database | Delete database | Admin, cleanup automation |
Permission Details
Section titled “Permission Details”| Permission | Operations |
|---|---|
QUERY_EVENTS | Query events by stream, subject, type |
RENDER_STATE_VIEWS | Execute state view renderers |
APPEND_TRANSACTIONS | Append events via transactions |
EXECUTE_STATE_CHANGES | Execute state change handlers |
PUBLISH_STATE_CHANGES | Deploy state change WASM modules |
PUBLISH_STATE_VIEWS | Deploy state view WASM modules |
CREATE_DATABASE | Create new databases |
DELETE_DATABASE | Delete existing databases |
evs:grants Claim Format
Section titled “evs:grants Claim Format”The evs:grants claim is a JSON object with three optional fields:
{ "evs:grants": { "global": ["database_creator"], "databases": { "production": ["reader", "writer"], "staging": ["reader", "writer", "deployer"], "development": ["reader", "writer", "deployer", "database_deleter"] }, "all_databases": ["reader"] }}Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
global | string[] | Global roles (currently only database_creator) |
databases | object | Map of database ID to role array |
all_databases | string[] | Roles applied to all databases (superuser/operator) |
Combining Roles
Section titled “Combining Roles”Roles are additive. A user with both reader and deployer on a database has all permissions from both roles.
{ "evs:grants": { "databases": { "production": ["reader", "deployer"] } }}This grants: QUERY_EVENTS, RENDER_STATE_VIEWS, PUBLISH_STATE_CHANGES, PUBLISH_STATE_VIEWS
IdP Configuration
Section titled “IdP Configuration”Amazon Cognito
Section titled “Amazon Cognito”Option 1: Pre-token Generation Lambda
def handler(event, context): # Lookup grants from your database/service grants = get_grants_for_user(event['userName'])
event['response']['claimsAndScopeOverrideDetails'] = { 'accessTokenGeneration': { 'claimsToAddOrOverride': { 'evs:grants': json.dumps(grants) } }, 'idTokenGeneration': { 'claimsToAddOrOverride': { 'evs:grants': json.dumps(grants) } } } return eventOption 2: Custom Attribute
- Add
custom:evs_grantsattribute to user pool - Store JSON-encoded grants in user profiles
- Include attribute in token generation
- Navigate to Security > API > Authorization Servers
- Select your authorization server
- Go to Claims tab
- Add claim:
- Name:
evs:grants - Include in:
Access TokenandID Token - Value type:
Expression - Value:
user.evsGrants(or use Groups to derive)
- Name:
Using Actions:
exports.onExecutePostLogin = async (event, api) => { // Get grants from user metadata or external service const grants = event.user.app_metadata.evs_grants || { databases: {}, global: [] };
api.accessToken.setCustomClaim('evs:grants', grants); api.idToken.setCustomClaim('evs:grants', grants);};Azure AD
Section titled “Azure AD”- Create an app registration
- Go to Token configuration
- Add optional claim for access token
- Use a custom claim provider or Graph API extension
Generic OIDC
Section titled “Generic OIDC”For IdPs that cannot add custom claims directly, use a Token Vending Service.
Common Patterns
Section titled “Common Patterns”Service Account
Section titled “Service Account”For backend services that need full access to specific databases:
{ "evs:grants": { "databases": { "orders_db": ["reader", "writer"], "inventory_db": ["reader", "writer"], "analytics_db": ["reader"] } }}CI/CD Pipeline
Section titled “CI/CD Pipeline”For deployment pipelines that need to publish WASM modules:
{ "evs:grants": { "databases": { "staging": ["deployer"], "production": ["deployer"] } }}Admin User
Section titled “Admin User”For administrators who need full access:
{ "evs:grants": { "global": ["database_creator"], "all_databases": ["reader", "writer", "deployer", "database_deleter"] }}Read-Only Analyst
Section titled “Read-Only Analyst”For users who only need to query data:
{ "evs:grants": { "all_databases": ["reader"] }}AI Agent
Section titled “AI Agent”For AI agents that need database access with delegated authority:
{ "evs:grants": { "databases": { "development": ["reader", "writer"] } }, "evs:principal": { "type": "agent", "name": "Development Assistant", "delegator": { "subject": "user:developer@example.com", "name": "Developer Name" } }}Audit Trail Extensions
Section titled “Audit Trail Extensions”Every event recorded by EvidentSource includes CloudEvents extensions for comprehensive audit trails.
Extension Reference
Section titled “Extension Reference”| Extension | Description | Example |
|---|---|---|
authtype | Principal type | user, agent, service, unauthenticated |
authid | Principal subject identifier | user:alice@example.com |
authdelegator | Delegator subject (agents only) | user:alice@example.com |
authdelegatorname | Delegator name (agents only) | Alice Chen |
Querying by Principal
Section titled “Querying by Principal”You can filter events by the authenticated principal who created them:
-- Events by a specific human userSELECT * FROM events WHERE authtype = 'user' AND authid = 'alice@example.com'
-- Events by agents acting for AliceSELECT * FROM events WHERE authtype = 'agent' AND authdelegator = 'alice@example.com'
-- Events by a service accountSELECT * FROM events WHERE authtype = 'service' AND authid = 'service:order-api'
-- All events by authenticated principals (exclude anonymous)SELECT * FROM events WHERE authtype != 'unauthenticated'Agent Accountability
Section titled “Agent Accountability”When an AI agent creates events, both the agent identity and the delegating human are recorded:
{ "specversion": "1.0", "id": "event-123", "type": "order.created", "source": "/databases/orders/streams/orders", "data": { ... }, "authtype": "agent", "authid": "agent:claude-assistant-alice", "authdelegator": "user:alice@example.com", "authdelegatorname": "Alice Chen"}This provides complete traceability:
- Who made the request: The agent (
authid) - On whose authority: The delegating human (
authdelegator) - When it happened: Event timestamp
- What was done: Event type and data
Business Context vs Auth Context
Section titled “Business Context vs Auth Context”The auth extensions track authentication context (WHO performed the action). For business context (e.g., which customer an action was for), use the CloudEvents correlation extension in your event data.
| Use Case | Where to Store |
|---|---|
| Who authenticated and made the request | Auth extensions (authtype, authid, authdelegator) |
| Which customer/tenant the action was for | Event data or correlation extension |
| Session or request tracing | Event data or correlation extension |
Troubleshooting
Section titled “Troubleshooting”403 Forbidden
Section titled “403 Forbidden”| Message | Cause | Solution |
|---|---|---|
Permission QUERY_EVENTS required | Missing reader role | Add reader to database grants |
Permission APPEND_TRANSACTIONS required | Missing writer role | Add writer to database grants |
Permission CREATE_DATABASE required | Missing database_creator | Add to global grants |
Checking Current Grants
Section titled “Checking Current Grants”Enable debug logging to see parsed grants:
RUST_LOG=evidentsource_server::auth=debug ./evidentsource server ...No evs:grants Claim
Section titled “No evs:grants Claim”If the evs:grants claim is missing, the user will have no permissions. Check:
- IdP is configured to include the claim
- Token type (access vs ID token) matches your configuration
- Claim name is exactly
evs:grants(case-sensitive)