Add Dialogues auth

Documentation

Add Dialogues auth

Pick your integration mode, then implement Grant Access

Updated July 4, 2026

Add Dialogues auth to your app

This tutorial implements the client facet of your Connector — the auth path software uses to connect. Owner-mediated connectors (file upload, in-UI entry) never need this tutorial.

Grant Access (Attach Dialogues) lets a signed-in user approve your connector and gives your client a plugin attach token plus resource_id for app_ingest.

Start with Integration Modes below. That table is the distinguisher: it tells you whether you follow the PKCE path (browser extension, public web, mobile without a trusted backend) or the client secret path (web or mobile app with a server that can hold credentials).

Prerequisite: Register a Connector (scopes, source declarations, redirects, and the matching client_auth_mode).

Prerequisites

  1. Tutorial 1 complete — app registered with the client_auth_mode that matches your row in Integration Modes.
  2. Your client can open a browser window or tab for user login and consent (all modes).

Integration Modes

Find your integration. Follow the Auth path column for the rest of this tutorial.

Integration modeTypical examplesclient_auth_modeAuth pathWhere token exchange runs
Browser extensionChrome / Chromium MV3 plugin (e.g. browser-history-plugin)public_pkcePKCEIn the extension — no client_secret in the shipped bundle
Web app with backendNext.js + API routes, Rails/Django server, any app with a trusted serverclient_secret_requiredClient secretOn your server only — issue cas_* in App Sheaf; never in frontend code
Web SPA (no backend)React/Vue SPA, static site with Attach in the browserpublic_pkcePKCEIn the browser — same PKCE flow as extensions
Mobile app (no backend)iOS/Android app without a secure server for secretspublic_pkcePKCEIn the app — PKCE in the mobile client
Mobile app with backendApp + your API that holds the Dialogues secretclient_secret_requiredClient secretOn your backend after the user completes /connect in the app
Local dev onlyQuick prototypeslegacy_noneDev / legacyNot for production — set public_pkce or client_secret_required before ship

Checkpoint: You know your row. PKCE rows → Sections B–E. Client secret rows → Section F.

Concepts (do not confuse these)

NameWhat it isWhere it lives
public_pkceRegistry policy: this app_id is a public OAuth clientApp metadata (Sheaf or API)
client_secret_requiredRegistry policy: exchange must prove possession of cas_*Secret only on your server
PKCE verifier / challengeProof for this attach sessionGenerated in the client when using PKCE
erk_*Optional key for extension redirect registration onlyOperator-issued; unrelated to PKCE

Section B — Generate PKCE (PKCE path only)

Skip to Section F if your Integration Mode uses client secret.

  1. Generate a random code_verifier (43–128 characters).
  2. Compute code_challenge = BASE64URL(SHA256(verifier)) and set code_challenge_method=S256.
  3. Create a new verifier/challenge pair for every attach attempt (do not reuse).

Reference: lib/pkce.js in browser-history-plugin

Checkpoint: Client holds the verifier locally; challenge is ready for /connect.

Section C — Start Grant Access

All modes start here in the user’s browser. PKCE modes must include challenge parameters; client-secret modes may omit PKCE on /connect (exchange still uses the secret on the server).

Build GET /connect with query parameters:

  1. app_id — your registered id
  2. redirect_uri — must match an allowed redirect for this app
  3. code_challenge and code_challenge_method=S256required for public_pkce
  4. scopes — subset of your allowed_scopes (e.g. activity:write)
  5. source_id — optional; often the primary ingest source you will use
  6. state — random string you verify on callback
  7. force_login=1 — optional; require fresh login

Example (browser extension, PKCE):

https://cp.logu3s.com/connect?app_id=my-extension&redirect_uri=https%3A%2F%2F<extension-id>.chromiumapp.org%2F&code_challenge=...&code_challenge_method=S256&scopes=activity%3Awrite&source_id=browser_visits&state=grant-abc&force_login=1
  1. Open the URL; user signs in via Dialogues (Keycloak) if needed.
  2. User sees the consent screen listing your app name and requested access.
  3. On approve, the browser redirects to redirect_uri with a one-time ?code=... (and usually state).

Reference: lib/grantAccess.jsbuildGrantConnectUrl in browser-history-plugin

Checkpoint: You received code on your redirect URI (or your backend received it via redirect to your web callback).

Section D — Exchange the code (PKCE path)

curl -sS -X POST "https://cp.logu3s.com/connect/exchange" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "code": "ONE_TIME_CODE_FROM_REDIRECT",
    "app_id": "my-extension",
    "code_verifier": "YOUR_CODE_VERIFIER"
  }'
  1. POST /connect/exchange with code, app_id, and code_verifier.
  2. Do not send client_secret when client_auth_mode is public_pkce.
  3. Parse response: plugin_attach_token, resource_id, optional mcp_access_token.
  4. Store tokens securely (chrome.storage, mobile secure storage, etc.).

Reference: lib/grantAccess.jsexchangeGrantCode in browser-history-plugin

Checkpoint: Attach succeeded; token and resource_id persisted.

Section E — Operate and recover (all modes)

  1. 401 Unauthorized — token expired or invalid. Clear stored credentials and re-run attach + exchange for your path.
  2. 403 Forbidden — user revoked access in Topos → Sharing → Connected apps. Clear credentials; prompt re-attach.
  3. Code already used or expired — codes are single-use and short-lived. Start a new attach (new PKCE pair for public clients).

Checkpoint: Error handling is implemented in your UI.

Section F — Exchange with client secret (server path)

For Web app with backend and Mobile app with backend in Integration Modes.

  1. User completes GET /connect in the browser; your redirect lands on your frontend or deep link with ?code=....
  2. Send the code to your backend (never expose cas_* to the browser or mobile binary).
  3. Your server calls exchange:
curl -sS -X POST "https://cp.logu3s.com/connect/exchange" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "code": "ONE_TIME_CODE_FROM_REDIRECT",
    "app_id": "my-web-app",
    "client_secret": "cas_YOUR_SECRET_FROM_APP_SHEAF"
  }'
  1. Store plugin_attach_token and resource_id in your server session or database, scoped to the signed-in user.
  2. Your frontend calls your API for ingest; your API uses the stored token on app_ingest.

Checkpoint: Exchange ran only on the server; no Dialogues secret in client-side code.

Verify with the reference smoke script (PKCE / extension)

From a clone of browser-history-plugin:

CONTROL_PLANE_URL=https://cp.logu3s.com APP_ID=browser-history-plugin ./scripts/smoke-test-connect.sh

If this passes for browser-history-plugin, your Dialogues API host and PKCE contract match production.

Checkpoint: Attach and token exchange work for your integration mode; implement app_ingest and Connected apps in your client.