LakeQL
Overview
  • Introduction
Server Setup
  • createApiServer
  • defineConfig
  • Yoga Configuration
Authentication
  • JWT Authentication
  • Permissions
  • Scope Authorization
Schema Builder
  • Builder Configuration
  • Scalar Types
  • Comparison Types
  • Pagination
  • Input Validation
Customization
  • Custom Queries & Mutations
  • Extending Core
  • CORS Configuration
GitHub
LakeQL
  1. API
  2. Authentication
  3. Scope Authorization

On this page

  1. Auth scopes
  2. Read permission logic
  3. Write permission logic
  4. Custom resolvers
  5. Applying scopes to fields

Scope Authorization

Three authorization scopes control access to GraphQL fields — authorized, readPermission, and writePermission.

LakeQL uses Pothos's scope auth plugin to enforce authorization at the field level. Every query or mutation field can declare which scope is required to access it.

Auth scopes #

ScopeCheckDescription
authorized!!context.currentUserUser is authenticated (any valid JWT)
readPermissionEvaluates catalog/schema/table against permissionsUser can read from the specified table
writePermissionEvaluates catalog/schema/table against permissionsUser can write to the specified table

Read permission logic #

The default hasReadPermission resolver follows this decision model:

  1. No user → deny
  2. No permission entry for user → allow (Trino handles auth for human users)
  3. Permission entry exists but no Query rules → allow
  4. Query rules exist → at least one rule must match the catalog, schema, and table name

This default-allow model works because human users (OAuth2 Authorization Code Flow) typically have direct Trino identities. Technical users that go through a shared system account need explicit rules.

Write permission logic #

The default hasWritePermission resolver is stricter:

  1. No user → deny
  2. No permission entry for user → deny
  3. Permission entry exists but no Mutation rules → deny
  4. Mutation rules exist → at least one rule must match the catalog, schema, and table name

Writes always require explicit permission because they execute via a system user in Trino and bypass Trino's per-user authorization.

Custom resolvers #

Override the default logic by providing custom resolvers in defineConfig:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { defineConfig } from "@lakeql/api/config"

import { allConfigs } from "./config-registry"

export const config = defineConfig({
  allConfigs,
  hasReadPermission: ({ context, catalog, schema, tableName }) => {
    // Custom logic: check external authorization service
    return checkExternalAuthService(context.currentUser, {
      action: "read",
      resource: `${catalog}.${schema}.${tableName}`,
    })
  },
  hasWritePermission: ({ context, catalog, schema, tableName }) => {
    // Custom logic: all writes require admin role
    return context.currentUser?.role === "admin"
  },
})

Applying scopes to fields #

When building custom query schemas, use authScopes to protect fields:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { builder } from "@lakeql/api/builder"

builder.queryField("sensitiveData", (t) =>
  t.field({
    type: "String",
    authScopes: {
      readPermission: {
        catalog: "hive",
        schema: "internal",
        tableName: "secrets",
      },
    },
    resolve: () => "classified information",
  })
)

For mutations, use writePermission:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21builder.mutationField("updateRecord", (t) =>
  t.field({
    type: "Boolean",
    authScopes: {
      writePermission: {
        catalog: "hive",
        schema: "production",
        tableName: "records",
      },
    },
    args: {
      id: t.arg.string({ required: true }),
      value: t.arg.string({ required: true }),
    },
    resolve: async (_parent, args, context) => {
      // perform the write operation
      return true
    },
  })
)

Fields without authScopes are publicly accessible (no authentication required).

Previous page

Permissions

Next page

Schema Builder

src/config.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21