Skip to content

Authorization

EvidentSource uses a claims-based authorization model. Permissions are encoded in the evs:grants JWT claim and evaluated at request time.

┌─────────────────────────────────────────────────────────────────────────────┐
│ 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 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
RoleScopePermissionsUse Case
database_creatorGlobalCreate new databasesAdmin, provisioning systems
readerDatabaseQuery events, render state viewsRead-only access, reporting
writerDatabaseReader + append transactions, execute state changesApplication services
deployerDatabasePublish state changes and state viewsCI/CD pipelines
database_deleterDatabaseDelete databaseAdmin, cleanup automation
PermissionOperations
QUERY_EVENTSQuery events by stream, subject, type
RENDER_STATE_VIEWSExecute state view renderers
APPEND_TRANSACTIONSAppend events via transactions
EXECUTE_STATE_CHANGESExecute state change handlers
PUBLISH_STATE_CHANGESDeploy state change WASM modules
PUBLISH_STATE_VIEWSDeploy state view WASM modules
CREATE_DATABASECreate new databases
DELETE_DATABASEDelete existing databases

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"]
}
}
FieldTypeDescription
globalstring[]Global roles (currently only database_creator)
databasesobjectMap of database ID to role array
all_databasesstring[]Roles applied to all databases (superuser/operator)

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

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 event

Option 2: Custom Attribute

  1. Add custom:evs_grants attribute to user pool
  2. Store JSON-encoded grants in user profiles
  3. Include attribute in token generation
  1. Navigate to Security > API > Authorization Servers
  2. Select your authorization server
  3. Go to Claims tab
  4. Add claim:
    • Name: evs:grants
    • Include in: Access Token and ID Token
    • Value type: Expression
    • Value: user.evsGrants (or use Groups to derive)

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);
};
  1. Create an app registration
  2. Go to Token configuration
  3. Add optional claim for access token
  4. Use a custom claim provider or Graph API extension

For IdPs that cannot add custom claims directly, use a Token Vending Service.

For backend services that need full access to specific databases:

{
"evs:grants": {
"databases": {
"orders_db": ["reader", "writer"],
"inventory_db": ["reader", "writer"],
"analytics_db": ["reader"]
}
}
}

For deployment pipelines that need to publish WASM modules:

{
"evs:grants": {
"databases": {
"staging": ["deployer"],
"production": ["deployer"]
}
}
}

For administrators who need full access:

{
"evs:grants": {
"global": ["database_creator"],
"all_databases": ["reader", "writer", "deployer", "database_deleter"]
}
}

For users who only need to query data:

{
"evs:grants": {
"all_databases": ["reader"]
}
}

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"
}
}
}

Every event recorded by EvidentSource includes CloudEvents extensions for comprehensive audit trails.

ExtensionDescriptionExample
authtypePrincipal typeuser, agent, service, unauthenticated
authidPrincipal subject identifieruser:alice@example.com
authdelegatorDelegator subject (agents only)user:alice@example.com
authdelegatornameDelegator name (agents only)Alice Chen

You can filter events by the authenticated principal who created them:

-- Events by a specific human user
SELECT * FROM events WHERE authtype = 'user' AND authid = 'alice@example.com'
-- Events by agents acting for Alice
SELECT * FROM events WHERE authtype = 'agent' AND authdelegator = 'alice@example.com'
-- Events by a service account
SELECT * FROM events WHERE authtype = 'service' AND authid = 'service:order-api'
-- All events by authenticated principals (exclude anonymous)
SELECT * FROM events WHERE authtype != 'unauthenticated'

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

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 CaseWhere to Store
Who authenticated and made the requestAuth extensions (authtype, authid, authdelegator)
Which customer/tenant the action was forEvent data or correlation extension
Session or request tracingEvent data or correlation extension
MessageCauseSolution
Permission QUERY_EVENTS requiredMissing reader roleAdd reader to database grants
Permission APPEND_TRANSACTIONS requiredMissing writer roleAdd writer to database grants
Permission CREATE_DATABASE requiredMissing database_creatorAdd to global grants

Enable debug logging to see parsed grants:

Terminal window
RUST_LOG=evidentsource_server::auth=debug ./evidentsource server ...

If the evs:grants claim is missing, the user will have no permissions. Check:

  1. IdP is configured to include the claim
  2. Token type (access vs ID token) matches your configuration
  3. Claim name is exactly evs:grants (case-sensitive)