Authorization
Control access to resources and fields.
Overview
DataFn enforces field-level authorization on every query and mutation. Access control is defined per-resource in your schema using DatafnPermissionsPolicy, and enforced server-side before any data is read or written.
By default, resources without a permissions policy are denied (FORBIDDEN). This deny-by-default behavior ensures that new resources are not accidentally exposed.
Permissions Policy
Define read and write field lists on each resource in your schema:
const schema = {
resources: [
{
name: "tasks",
version: 1,
fields: [
{ name: "title", type: "string" },
{ name: "status", type: "string" },
{ name: "assigneeId", type: "string" },
{ name: "internalNotes", type: "string" },
],
permissions: {
read: {
fields: ["title", "status", "assigneeId", "createdAt", "updatedAt"],
},
write: {
fields: ["title", "status", "assigneeId"],
},
},
},
],
};In this example, internalNotes is excluded from both read and write policies. Any query selecting or filtering on internalNotes, or any mutation writing to it, will be rejected with FORBIDDEN.
The id field is always allowed for both read and write operations.
Query Authorization
When a query is executed, the server checks:
- Select fields: Every selected field must appear in
read.fields. - Filter fields: Every field referenced in
filtersmust appear inread.fields. - Sort fields: Every field referenced in
sortmust appear inread.fields. - Aggregation fields: Every field referenced in
aggregationsmust appear inread.fields. - GroupBy fields: Every field in
groupBymust appear inread.fields. - Having fields: Every field in
havingmust appear inread.fields. - Search fields: Every field in
search.fieldsmust appear inread.fields.
// This query is REJECTED because "internalNotes" is not in read.fields
{
resource: "tasks",
select: ["id", "title", "internalNotes"],
}
// This query is REJECTED because filtering on "internalNotes" is not allowed
{
resource: "tasks",
filters: { internalNotes: { $like: "%secret%" } },
}Mutation Authorization
When a mutation is executed, the server checks:
- Record fields: Every field in the
recordobject must appear inwrite.fields. - Relation operations: For
relate,modifyRelation, andunrelate, the relation name must appear inwrite.fields.
// This mutation is REJECTED because "internalNotes" is not in write.fields
{
resource: "tasks",
operation: "merge",
id: "task_1",
record: { internalNotes: "secret data" },
}Request-Level Authorization
The authorize callback runs on every request after JSON parsing but before execution. Use it to implement custom per-request authorization logic:
const server = await createDatafnServer({
schema,
db,
authorize: async (ctx, action, payload) => {
// Reject unauthenticated requests
if (!ctx.session?.userId) return false;
// Only admins can seed
if (action === "seed" && !ctx.session.isAdmin) return false;
return true;
},
});The action parameter is one of: "status", "query", "mutation", "transact", "seed", "clone", "pull", "push", "reconcile".
Returning false produces an HTTP 403 response:
{
"ok": false,
"error": {
"code": "FORBIDDEN",
"message": "Authorization denied",
"details": { "path": "$" }
}
}JSON parsing always happens before the authorize callback. This ensures that malformed JSON returns DFQL_INVALID (400), not FORBIDDEN (403).
allowUnknownResources
By default, any resource without a permissions policy is denied:
// Server config
{
allowUnknownResources: false, // default
}Set allowUnknownResources: true to bypass this check. This is intended only for development and testing:
const server = await createDatafnServer({
schema,
db,
allowUnknownResources: true, // allow all resources in development
});Debug Mode
In debug mode (the default in non-production environments), authorization errors include the resource name and a hint:
No authorization policy found for resource 'tasks'. Define a policy or use allowUnknownResources: true to allow.In production (debug: false), the same error produces a generic message:
Authorization deniedThis prevents leaking schema details to clients.