Skip to main content
Version: 2.0

Connector authentication patterns

Every enterprise integration needs credentials. Google Drive, GitHub, Salesforce, ServiceNow, Wolken, Confluence, Box, Jira — each one authenticates differently, and most platforms treat that as a custom integration job per service. Vectara handles all of them with the same two-axis model: pick a storage scope, pick an auth mechanism. The platform resolves credentials by symbolic $ref at call time — the LLM never sees the literal value.

This page walks the three patterns developers reach for most. Each is a real, working tool configuration — drop into your agent.json and ship.

The two axes

Two choices, four combinations. Pick by who provisions the credential and how the upstream service issues tokens.

AxisChoicesDecides
Storage scopeagent.secrets.* or session.metadata.* / session.secrets.*Service account vs. per-user. Whose identity drives the call.
Auth mechanismauth.type=oauth_refresh_token or per-header $ref for a static bearerOAuth refresh-token grant (auto-rotates access tokens) vs. a long-lived bearer / API key.

Same $ref resolver, same web_get machinery — three valid choices.

PatternStorageMechanismWho provisions
Per-user OAuthsession.metadata.*auth.type=oauth_refresh_token (auto-refresh)End user via OAuth dance
Per-user static bearersession.metadata.*headers.Authorization via per-header $refEnd user via OAuth code → static bearer
Service-account OAuthagent.secrets.*auth.type=oauth_refresh_token (auto-refresh)Admin, one-time PATCH

Below: the actual JSON for each.

Pattern 1 — Per-user OAuth refresh-token grant

Use when the agent must act as the end user and the upstream issues expiring access tokens (Google, Microsoft, Atlassian, Slack user tokens).

Your app handles the OAuth consent flow in the browser, then passes the user's client_id, client_secret, and refresh_token to Vectara via session.metadata at session creation. Vectara's OAuth2Client mints a fresh access token from the refresh token on every call and caches it until expiry — your code never touches the access token.

"gdrive_read_doc": {
"type": "web_get",
"argument_override": {
"method": "GET",
"auth": {
"type": "oauth_refresh_token",
"client_id": { "$ref": "session.metadata.GOOGLE_CLIENT_ID" },
"client_secret": { "$ref": "session.metadata.GOOGLE_CLIENT_SECRET" },
"refresh_token": { "$ref": "session.metadata.GOOGLE_REFRESH_TOKEN" },
"token_endpoint": "https://oauth2.googleapis.com/token"
},
"head_lines": 200,
"max_content_bytes": 524288
}
}

Your app starts the session with the user's tokens:

POST /v2/agents/{agent_key}/sessions
{
"session": {
"metadata": {
"user_id": "alice.chen",
"GOOGLE_CLIENT_ID": "your-google-client.apps.googleusercontent.com",
"GOOGLE_CLIENT_SECRET": "GOCSPX-...",
"GOOGLE_REFRESH_TOKEN": "1//04..."
}
}
}

What the agent sees. Just a tool named gdrive_read_doc that takes a url. No auth header to construct, no token to manage. The LLM cannot leak a credential it cannot see.

Best for. Google Workspace, Microsoft 365, Atlassian, Slack user-token flows, any OAuth provider with refresh tokens.

Pattern 2 — Per-user static bearer via per-header $ref

Use when the upstream issues non-expiring access tokens (GitHub's default OAuth flow, GitLab PATs, fine-grained access tokens). No refresh-token machinery needed — drop the bearer header directly.

"github_create_issue": {
"type": "web_get",
"argument_override": {
"method": "POST",
"url": { "$ref": "session.metadata.GITHUB_ISSUES_URL" },
"headers": {
"Authorization": { "$ref": "session.metadata.GITHUB_BEARER_HEADER" },
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
"Content-Type": "application/json"
}
}
}

Your app fills session.metadata at session creation:

POST /v2/agents/{agent_key}/sessions
{
"session": {
"metadata": {
"user_id": "alice.chen",
"GITHUB_BEARER_HEADER": "Bearer gho_...",
"GITHUB_ISSUES_URL": "https://api.github.com/repos/acme/web/issues"
}
}
}

The full Authorization header value — including the literal Bearer prefix — lives in session.metadata. The full issues URL with the user's chosen owner/repo path is also pre-built so the LLM never has to construct it.

What the agent sees. A tool named github_create_issue that takes a JSON body. No URL to assemble, no auth to manage.

Best for. GitHub OAuth, GitLab PATs, ServiceNow basic auth, Confluence personal tokens, Box developer tokens, Wolken API keys. Anything where the upstream issues a long-lived token.

Pattern 3 — Service-account OAuth refresh-token grant

Use when the agent acts as a shared service account, not a specific user. Same auth machinery as Pattern 1, but the credentials live in agent.secrets — the encrypted, masked scope. Set once by an admin, shared across every session, rotated centrally.

"create_ticket": {
"type": "web_get",
"argument_override": {
"method": "POST",
"url": "https://tickets.example.com/api/v1/tickets",
"auth": {
"type": "oauth_refresh_token",
"client_id": { "$ref": "agent.secrets.TICKETS_CLIENT_ID" },
"client_secret": { "$ref": "agent.secrets.TICKETS_CLIENT_SECRET" },
"refresh_token": { "$ref": "agent.secrets.TICKETS_REFRESH_TOKEN" },
"token_endpoint": "https://tickets.example.com/oauth/token"
},
"headers": { "Content-Type": "application/json" }
}
}

The admin PATCHes the agent once to provision:

PATCH /v2/agents/{agent_key}
{
"secrets": {
"TICKETS_CLIENT_ID": "sa_client_...",
"TICKETS_CLIENT_SECRET": "sa_secret_...",
"TICKETS_REFRESH_TOKEN": "sa_refresh_..."
}
}

What the agent sees. A tool named create_ticket that takes a JSON body. The URL, method, and auth are locked by argument_override — the LLM cannot see them, modify them, or substitute different credentials.

Best for. ITSM platforms (ServiceNow, Wolken, Freshservice), HR systems (Workday), CRM service accounts (Salesforce JWT bearer flow), internal back-office APIs.

The scoping trade-off

Pick by who provisions the credential — it is not about which auth mechanism the upstream uses.

If the credential is...Use this scopeWhy
Provisioned by the end user (their OAuth grant)session.metadata or session.secretsDifferent every session. Dies with the session.
Provisioned by an admin (shared service account)agent.secretsSame every session. Rotated centrally.

A quick comparison

Propertyagent.secrets.*session.metadata.*session.secrets.*
ScopeAll sessions on the agentOne sessionOne session
Encryption at restYesNo (standard storage)Yes
Masked in event tracesYes (****)No (visible)Yes (****)
RotationCentral, via PATCHPer-session, at session createPer-session, at session create
Best forService accounts, API keysNon-secret personalizationPer-user OAuth tokens

For production multi-user deployments with external observability, use session.secrets.* (same crypto + masking as agent.secrets.*, scoped per session).

The argument_override safety net

Every pattern above uses argument_override to lock specific tool arguments at config time. The LLM cannot see them, modify them, or know they exist.

  • The Google Drive url is constructed by the LLM (file ID in the query) — but auth is locked.
  • The GitHub url and headers.Authorization are locked. The LLM supplies only the JSON body.
  • The MockTickets url, method, auth, and headers are all locked. The LLM supplies only the JSON body.

A prompt-injection attack against the LLM has nothing to exfiltrate and nowhere to escalate. The credentials are not in the prompt. The URL is not in the prompt. The headers are not in the prompt.

This is what "safe by construction" means in practice.

What this lets you build in minutes

Same shape, different argument_override. Below: ready-to-paste tool configs for the patterns developers ask about most.

Google Drive — read a Doc (per-user OAuth)

"gdrive_read_doc": {
"type": "web_get",
"argument_override": {
"auth": {
"type": "oauth_refresh_token",
"client_id": { "$ref": "session.metadata.GOOGLE_CLIENT_ID" },
"client_secret": { "$ref": "session.metadata.GOOGLE_CLIENT_SECRET" },
"refresh_token": { "$ref": "session.metadata.GOOGLE_REFRESH_TOKEN" },
"token_endpoint": "https://oauth2.googleapis.com/token"
}
}
}

Salesforce — create a Lead (service-account OAuth)

"sf_create_lead": {
"type": "web_get",
"argument_override": {
"method": "POST",
"url": "https://your-org.my.salesforce.com/services/data/v60.0/sobjects/Lead",
"auth": {
"type": "oauth_refresh_token",
"client_id": { "$ref": "agent.secrets.SF_CLIENT_ID" },
"client_secret": { "$ref": "agent.secrets.SF_CLIENT_SECRET" },
"refresh_token": { "$ref": "agent.secrets.SF_REFRESH_TOKEN" },
"token_endpoint": "https://login.salesforce.com/services/oauth2/token"
},
"headers": { "Content-Type": "application/json" }
}
}

Wolken — open a ticket (service-account API key)

"wolken_create_ticket": {
"type": "web_get",
"argument_override": {
"method": "POST",
"url": "https://your-tenant.wolkenservicedesk.com/api/v1/cases",
"headers": {
"X-API-Key": { "$ref": "agent.secrets.WOLKEN_KEY" },
"Content-Type": "application/json"
}
}
}

ServiceNow — query incidents (service-account basic auth)

"sn_query_incidents": {
"type": "web_get",
"argument_override": {
"url": "https://your-tenant.service-now.com/api/now/table/incident",
"headers": {
"Authorization": { "$ref": "agent.secrets.SN_BASIC_AUTH" },
"Accept": "application/json"
}
}
}

GitHub — search code (per-user PAT)

"gh_search_code": {
"type": "web_get",
"argument_override": {
"url": "https://api.github.com/search/code",
"headers": {
"Authorization": { "$ref": "session.metadata.GITHUB_BEARER_HEADER" },
"Accept": "application/vnd.github+json"
}
}
}

Same agent, three tools, three patterns

The three patterns coexist on a single agent. A user can be connected to Google Drive (Pattern 1), connected to GitHub (Pattern 2), and have the agent file tickets via a shared service account (Pattern 3) — all in one session.

This is how a real enterprise integration looks. No SDK lock-in. No custom connector framework. No platform release to onboard a new service. Three lines of JSON per tool.

An internal reference implementation ("OAuth tools demo") wires all three patterns onto a single agent — Google Drive (per-user OAuth), GitHub (per-user bearer), and a mock ticketing service (service-account OAuth) — running side by side. Ask your Vectara contact for access.