Skip to content

OAuth & Service Canvas

Queria integrates with authenticated external APIs through Service Canvas -- special DSL canvases with purpose = SERVICE that describe how to authorize an OAuth2 provider and which operations they expose. User credentials are stored encrypted in a vault and referenced by consumer canvases (CHAT, INGESTION, WIDGET) through the connection_ref type.

BYO (Bring Your Own)

V1/V2 only support the BYO approach: the tenant brings their own OAuth client_id and client_secret registered at the provider. Queria does not provide or certify proprietary OAuth apps toward third-party providers.

When you need it

Use caseExample
Chat that sends messages to a channelSlack, Microsoft Teams
Pipeline that creates calendar eventsGoogle Calendar, Microsoft 365
Automatic ingestion from SaaSNotion, Confluence, GitHub Issues
Constructor calling paid APIsStripe, Salesforce, HubSpot
Advanced cloud storageGoogle Drive with delegated permissions, OneDrive

For basic cloud storage with personal user login, standard connectors are already documented in Settings > Cloud Storage.

Architecture

+----------------------------------------+
|  Service Canvas "Slack OAuth"          |
|  purpose = SERVICE                     |
|  +--------------+  +------------------+|
|  | oauth2_config|  | operation_def[]  ||
|  | client_id    |  | post_message     ||
|  | client_secret|  | list_channels    ||
|  | auth_url     |  | get_user_email   ||
|  | token_url    |  +------------------+|
|  | scopes[]     |                      |
|  +--------------+                      |
+----------------------------------------+
            ^ referenced by
            |
+-----------------------------+
|  CHAT consumer canvas       |
|  ... -> external_tool(      |
|    serviceCanvasId='slack', |
|    operationId='post_msg')  |
+-----------------------------+
            | runtime
            v
+------------------------------------+
|  ExternalServiceConnection (DB)    |
|  per (serviceCanvasId,             |
|       companyId, userId)           |
|  -> accessToken (AES-256 encrypted)|
|  -> refreshToken (encrypted)       |
|  -> scopes, expiresAt              |
+------------------------------------+

Three key concepts:

  1. Service canvas -- provider definition (who it is, how to authorize, which endpoints it exposes).
  2. External_tool node -- used in CHAT/INGESTION canvases to call an operation on the service canvas.
  3. Connection -- DB row with encrypted tokens for the pair (user, service).

Creating a service canvas

The admin follows these steps:

1. Register the OAuth app at the provider

Example Slack:

  1. Go to https://api.slack.com/apps and create a new app.
  2. Configure Redirect URL = https://admin.queria.pro/api/oauth/callback/slack (replace with your tenant Queria domain).
  3. Set the required scopes (e.g. chat:write, channels:read).
  4. Copy client_id and client_secret.

2. Create the service canvas

  1. Admin panel > DSL & Pipelines > OAuth & Service Canvas > New.
  2. Choose a provider template (Slack, Google Workspace, Microsoft 365, GitHub, Notion, Custom).
  3. Paste client_id and client_secret in the oauth2_config node fields. The client_secret is server-side encrypted (AES-256-GCM).
  4. Verify auth_url, token_url, scopes (pre-filled by template, editable).
  5. Add operation_def nodes with HTTP method, path, input/output parameters. You can import them from OpenAPI 3.x (for providers that expose it).
  6. Save. The canvas is validated and marked ACTIVE.

3. Users authorize

On the service canvas a Connect button appears. When a user clicks it:

  1. Redirect to the provider for authorization (user consent, scopes).
  2. Provider redirects back with a code.
  3. Backend exchanges code for access_token + refresh_token.
  4. Tokens are encrypted and stored in ExternalServiceConnection for (serviceCanvasId, companyId, userId).
  5. The user sees the connection as Connected and can revoke it at any time.

Using the connection in a canvas

In the CHAT/INGESTION canvas add an external_tool node:

json
{
  "type": "external_tool",
  "params": {
    "serviceCanvasId": "slack-oauth-canvas-id",
    "operationId": "post_message",
    "inputs": {
      "channel": "{{sys.target_channel}}",
      "text": "{{llm_writer_1.output}}"
    },
    "outputName": "slack_response"
  }
}

At runtime:

  • The engine loads the service canvas.
  • Finds the post_message operation.
  • Resolves the connection for (serviceCanvasId, companyId, ctx.userId).
  • If the token has expired, refreshes it automatically.
  • Executes the HTTP call with Authorization: Bearer <access_token>.
  • Writes the response to sys.slack_response.

Connection management

Admin panel > OAuth & Service Canvas > [service] > Connections:

  • Table of all connections for that service canvas (users, scopes, status, last use).
  • Possible states: ACTIVE, EXPIRED (refresh failed), REVOKED, ERROR.
  • Revoke on the Queria side (removes encrypted tokens) and -- if the provider supports it -- calls the remote revoke endpoint.
  • Audit log: who authorized, when, with which scopes, which canvas consumed the connection.

Security

  • Tokens at rest: encrypted with AES-256-GCM. The master key LLM_VAULT_KEK is derived from JWT_SECRET (in production make sure it's not the default dev-secret-change-me).
  • Tokens in transit: HTTPS only to the provider; internal calls go through the isolated Docker network.
  • Minimum scopes: the template suggests minimum scopes per operation. Don't request admin:* if not needed.
  • Per-user: every connection belongs to a single user. When a canvas runs, it uses the token of the user who triggered the execution (ctx.userId).
  • Audit: every OAuth external_tool call logs (userId, serviceCanvasId, operationId, statusCode, latencyMs) to IntegrationCallLog.

Limits and roadmap

Current V1/V2:

  • Only authorization_code flow.
  • BYO client only (no Queria-owned verified apps).
  • No client_credentials (M2M) -- on V3 roadmap.
  • No device_code flow.
  • No Google Workspace domain-wide delegation.
  • No inbound webhooks from provider (separate roadmap).
  • OpenAPI 3.x operation import in V1.2.

Best practices

  1. One service canvas per provider, even if you use multiple operations: simplifies token management.
  2. Document the scopes in the canvas name or description: "Slack (chat:write only)" beats "Slack OAuth".
  3. Limit active operation_def: add only the operations actually used.
  4. Test on pilot tenant: before enabling for everyone, try on an internal company.
  5. Watch EXPIRED: a drop in successful refreshes often signals the provider revoked the app or changed policies.

Queria v3.5.0 -- External Tools OAuth V2 + Service Canvas

Queria - Document Intelligence con Cog-RAG