arizuko

arizukohowto › Add an OAuth provider

add an OAuth provider

Some services hand out a static API key you paste once. Others want a proper “Connect” dance — the user authorizes arizuko to act as them, and the service returns a short-lived access token plus a refresh token. That second kind is a surrogate OAuth provider: arizuko runs the dance, stores the token in the secrets table, and refreshes it near expiry so an agent’s tools keep working.

Adding one is pure configuration. A .toml file in the data dir, a redirect URI registered at the provider, two env vars. No code, no rebuild. github and google ship built in; here’s how to add any other.

When to use this vs a plain key: reach for an OAuth provider when the token must be minted as the user (Google Drive, Slack, Notion, Linear) and expires. For a long-lived API key on a folder, an env var or a secrets-table row wired to an [[connector]] REST provider is simpler.

1. drop a descriptor

Write <datadir>/surrogate/<name>.toml. The basename is the provider name — it becomes the URL segment and the env-var stem. Example, Slack:

# <datadir>/surrogate/slack.toml
auth_url       = "https://slack.com/oauth/v2/authorize"
token_url      = "https://slack.com/api/oauth.v2.access"
revoke_url     = "https://slack.com/api/auth.revoke"
scopes         = ["chat:write", "channels:read"]
secret_key     = "SLACK_TOKEN"        # secrets-table key the token lands in
allowed_domain = "slack.com"
access_type    = ""                   # "offline" where a provider gates refresh (google)

auth_url, token_url and secret_key are required. Leave one out and the daemon refuses to boot, naming the file — a half-defined provider is a broken dance, better caught at startup than at the first click. A file here that shares a name with a built-in replaces it, so you can override github.toml’s scopes without touching the binary.

Scopes are space-joined per the OAuth2 spec — the same format every standard provider expects. List them as a TOML array; arizuko joins them.

2. register the redirect URI

In the provider’s app console (Slack API dashboard, Google Cloud console, Notion integrations…), add this exact callback URL:

<WEB_HOST>/dash/me/connections/<name>/callback

For a Slack provider on acme.example.com that is https://acme.example.com/dash/me/connections/slack/callback. The provider rejects the dance if the redirect URI doesn’t match what it has on file — this is the step people forget.

3. set the client creds

The confidential-client id and secret the provider issued go in the instance .env, keyed by the provider name upper-cased (- becomes _):

SURROGATE_SLACK_CLIENT_ID=1234567890.abcdef
SURROGATE_SLACK_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxx

A provider with no creds still loads, but stays hidden from the connections page until it can actually run — so you never show a user a “Connect” button that would 500.

4. restart and connect

sudo systemctl restart arizuko_<instance>

“Connect Slack” now appears in /dash/me/connections. A user clicks it, approves at Slack, and lands back with a live token written to the secrets table under secret_key (SLACK_TOKEN). The broker refreshes it when it nears expiry; if the refresh token is revoked, the agent gets a clear “reconnect” error instead of a silent failure.

give agents a tool backed by it

The stored token reads like any other secret. Point an [[connector]] REST provider or an MCP connector at secret_key and the agent gets a tool that calls the service as the connected user — no credential ever enters the container. See reference/mcp for the tool surface and grants for gating which folders may call it.

checking it works

sudo journalctl -u arizuko_<instance> --since "1 min ago" \
  | grep -i "surrogate provider"

At boot dashd logs each loaded provider and whether it’s connectable (has creds). A missing provider means the .toml didn’t parse or landed in the wrong directory; connectable=false means the SURROGATE_<NAME>_CLIENT_* vars aren’t set.

go deeper