Connector OAuth
harn connect is the guided setup entry point for connector credentials. It is
intended for local operator setup: run the browser flow once, store tokens in
the workspace keyring namespace, then reference the resulting secret ids from
connector configuration.
Provider flows
Built-in OAuth commands are available for Slack, Linear, and Notion:
harn connect slack \
--client-id "$SLACK_CLIENT_ID" \
--client-secret "$SLACK_CLIENT_SECRET" \
--scope "app_mentions:read chat:write"
harn connect linear \
--client-id "$LINEAR_CLIENT_ID" \
--client-secret "$LINEAR_CLIENT_SECRET"
harn connect notion \
--client-id "$NOTION_CLIENT_ID" \
--client-secret "$NOTION_CLIENT_SECRET"
The generic OAuth 2.1 path targets compliant protected resources:
harn connect generic acme https://mcp.example.com/mcp
harn connect --generic acme https://mcp.example.com/mcp
Connector packages can also declare their setup metadata in the registered
provider entry, letting operators run harn connect <provider> without
hardcoded provider logic in core:
[[providers]]
id = "acme"
connector = { harn = ".harn/packages/acme-connector/lib.harn" }
oauth = {
resource = "https://mcp.example.com/mcp",
authorization_endpoint = "https://auth.example.com/oauth/authorize",
token_endpoint = "https://auth.example.com/oauth/token",
registration_endpoint = "https://auth.example.com/oauth/register",
scopes = "mcp.read mcp.write",
token_endpoint_auth_method = "none",
}
Hosts use the same metadata for generic setup and repair UI. Every connector
package should declare a setup table on its [[providers]] entry:
[[providers]]
id = "acme"
connector = { harn = ".harn/packages/acme-connector/lib.harn" }
capabilities = ["webhook", "oauth"]
[providers.setup]
auth_type = "oauth2"
flow = "browser"
required_scopes = ["mcp.read", "mcp.write"]
setup_command = ["harn", "connect", "acme"]
validation_command = ["harn", "connect", "status", "--connector", "acme", "--json"]
[[providers.setup.health_checks]]
id = "credentials"
kind = "command"
command = ["harn", "connect", "status", "--connector", "acme", "--json"]
[providers.setup.recovery]
missing_auth = "Run `harn connect acme`."
expired_credentials = "Refresh or reconnect the OAuth token."
revoked_credentials = "Revoke the stale local token, then reconnect."
missing_scopes = "Reconnect with the scopes listed in required_scopes."
inaccessible_resource = "Grant the connector access to the requested resource."
transient_provider_outage = "Retry after the provider or credential backend recovers."
harn connect setup-plan --connector <id> --json emits a host-renderable plan.
harn connect status --connector <id> --json reports one of healthy,
missing_install, missing_auth, expired_credentials,
revoked_credentials, missing_scopes, inaccessible_resource, or
transient_provider_outage.
When endpoints are not supplied, Harn discovers protected-resource metadata,
then authorization-server metadata. For MCP login, the default client
registration mode is CIMD: Harn sends
https://harnlang.com/.well-known/oauth-client.json as its client id when the
authorization server advertises Client ID Metadata Document support. If CIMD is
not available and the server advertises dynamic client registration, Harn
registers a loopback client for the selected redirect URI.
GitHub App setup is separate from OAuth access tokens:
harn connect github \
--app-slug my-harn-app \
--app-id 12345 \
--private-key-file app.pem
The command opens the GitHub App installation URL and waits for the app setup
callback to include installation_id. You can skip the browser callback when
you already know the installation:
harn connect github \
--installation-id 67890 \
--app-id 12345 \
--private-key-file app.pem
Callback server
OAuth and GitHub App setup callbacks use plaintext HTTP and bind only to
127.0.0.1 or localhost. The default redirect URI uses port 0, so Harn
chooses a random free local port and sends that concrete URI in the
authorization request; custom redirect URIs must also include an explicit port.
Callback listeners are single-use and time out after five minutes. If a callback
request includes an Origin header, Harn requires it to match the redirect
origin.
OAuth guarantees
Harn always sends PKCE S256 parameters. The generic flow validates advertised
PKCE support when authorization-server metadata is available. The generic flow
also sends the resource parameter to both the authorization endpoint and token
endpoint; MCP server URLs are canonicalized first by lowercasing the scheme and
host, dropping default ports, and removing query strings, fragments, and trailing
slashes. Provider-specific flows let you override --resource when a provider
requires one.
Stored secrets
OAuth setup stores connector-friendly ids in the same keyring namespace that
the nearest harn.toml uses:
<provider>/access-token<provider>/refresh-tokenwhen returned by the provider<provider>/oauth-tokenfor refresh metadata such as token endpoint, client id, client secret, scopes, resource, and expiration
GitHub App setup stores:
github/installation-<id>github/app-<app-id>/private-keywhen--private-key-fileis suppliedgithub/webhook-secretwhen a webhook secret is supplied
Use harn connect --list, harn connect --refresh <provider>, and
harn connect --revoke <provider> to inspect, refresh, or remove local
connector credentials.