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.
| Axis | Choices | Decides |
|---|---|---|
| Storage scope | agent.secrets.* or session.metadata.* / session.secrets.* | Service account vs. per-user. Whose identity drives the call. |
| Auth mechanism | auth.type=oauth_refresh_token or per-header $ref for a static bearer | OAuth refresh-token grant (auto-rotates access tokens) vs. a long-lived bearer / API key. |
Same $ref resolver, same web_get machinery — three valid choices.
| Pattern | Storage | Mechanism | Who provisions |
|---|---|---|---|
| Per-user OAuth | session.metadata.* | auth.type=oauth_refresh_token (auto-refresh) | End user via OAuth dance |
| Per-user static bearer | session.metadata.* | headers.Authorization via per-header $ref | End user via OAuth code → static bearer |
| Service-account OAuth | agent.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 scope | Why |
|---|---|---|
| Provisioned by the end user (their OAuth grant) | session.metadata or session.secrets | Different every session. Dies with the session. |
| Provisioned by an admin (shared service account) | agent.secrets | Same every session. Rotated centrally. |
A quick comparison
| Property | agent.secrets.* | session.metadata.* | session.secrets.* |
|---|---|---|---|
| Scope | All sessions on the agent | One session | One session |
| Encryption at rest | Yes | No (standard storage) | Yes |
| Masked in event traces | Yes (****) | No (visible) | Yes (****) |
| Rotation | Central, via PATCH | Per-session, at session create | Per-session, at session create |
| Best for | Service accounts, API keys | Non-secret personalization | Per-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
urlis constructed by the LLM (file ID in the query) — butauthis locked. - The GitHub
urlandheaders.Authorizationare locked. The LLM supplies only the JSONbody. - The MockTickets
url,method,auth, andheadersare all locked. The LLM supplies only the JSONbody.
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.
Related
- Agent secrets — the encrypted credential scope, masking behavior, and rotation.
- Multi-client steering — serving every tenant from one agent definition.
- web_get tool — the underlying HTTP client and its full configuration surface.
- Custom tools — when to write a Lambda
vs. wrap
web_get. - Agent anatomy: Secrets — the three-scope model in context.