Docs Decisions
0032 · A connection can sign in with OAuth: an app management registers, and a person's own consent
Status: Accepted · 2026-11-02. It adds a way to give an http connection
(0025) its credential: signing in, as well as pasting a token. What a
connection may do (read, and the calls of 0028) does not
change.
Context
An http connection carries one secret header, pasted when it is made. That works for the APIs that
give out a token: Stripe, Linear, Notion's own integrations, Airtable, GitHub, HubSpot's private apps.
It doesn't work for the ones a company's data most often lives in:
- Google Workspace: Sheets, Drive, Calendar and Gmail take an OAuth access token, which lasts an hour and is refreshed with a refresh token. On 2026-11-01 a flow that tracks applications in a Google Form's Sheet found CoDNA couldn't read the Sheet. The workaround was to have Claude read it.
- Microsoft 365: Graph (Teams, Outlook, SharePoint, OneDrive) is the same. The Silkbank pilot runs on Teams.
- Salesforce, Zoom, Xero and others offer OAuth only, or prefer it.
0025, 0027 and 0028 each left OAuth open. A token pasted by hand can't be refreshed, and a person can't get one without writing code.
Two things make OAuth harder here than it is for a single product:
- Every OAuth app is registered with the provider, with its redirect address. Google reviews an app that asks for Drive or Gmail access before it may be used outside one organisation. That takes weeks and a security assessment.
- An app registered inside a company's own Google Workspace or Microsoft tenant ("internal") needs no review, and only that company's people can sign in to it.
Decision
Management registers the instance's OAuth apps
- Per instance, in the company's own Google Cloud project or Microsoft tenant, as an internal
app: management pastes its client id and secret, in the console for a hosted instance or with
codna oauth. The secret is sealed like any connection's, and never shown again. - CoDNA shows the redirect address to register:
https://acme.codna.me/connections/oauth/callback. - Known providers have their addresses and API base built in: Google (
https://www.googleapis.comand its APIs) and Microsoft (https://graph.microsoft.com, with the tenant). Any other OAuth 2.0 provider is registered with its authorize and token addresses. That is how HubSpot, Salesforce and Zoom are added. - Management lists the scopes people may ask for, from the provider's, such as
https://www.googleapis.com/auth/spreadsheets.readonly. A person can't ask for more than that. - The audit records each app registered, changed or removed, never a secret.
A person signs in for a connection of their own
- On the Connections page, an
httpconnection may be made with Sign in with Google (or Microsoft, or a provider management registered) instead of a pasted header. The person picks the scopes it needs, from those management allows, and signs in in their own browser. CoDNA receives the tokens on its callback, with PKCE and a state it checks, and seals them. - The connection then shows whose account it acts as, such as
nino@acme.geon Google, and its scopes. It never shows a token. - Its address is the provider's API, such as
https://sheets.googleapis.com/v4. Reads and calls work as with anyhttpconnection, and the methods it may be sent (0028) are its owner's choice, as before. - Signing in happens only in CoDNA's own pages, never over MCP, as pasting a secret does (0025). An
agent sees the connection, its account and its scopes in
list_connections, and sends its person to the page to sign in.
Tokens are refreshed by CoDNA
-
Before a read or a call, an access token that has expired, or expires within a minute, is refreshed with the refresh token. The new one is sealed and used. Two reads at once refresh once.
-
When refreshing fails, because the person revoked access, left the company, or the app was removed, the connection needs signing in again:
- its reads and calls fail with that reason, recorded on each run;
- its owner is told in the inbox once;
- it shows on the Connections page with Sign in again.
Nothing else about it changes, so signing in again restores it for everyone it is shared with.
-
Removing the connection revokes its tokens with the provider, where the provider allows it.
Sharing
A signed-in connection is shared like any other (0025). Whoever it is shared with reads and calls as the account that signed in, within its scopes. The share dialog says so: "Their automations will read Google as nino@acme.ge."
Consequences
- Google Sheets, Drive, Calendar, Gmail and Microsoft Graph become sources, and with 0028 places a script writes to, within the scopes management allows.
- Each company registers its own apps. That is a few minutes in Google Cloud or Entra. It is work an FDE engagement does once per customer, and it keeps their data under their own app and admin consent.
- A refresh token is long-lived access to a person's account, held by CoDNA. It is sealed with
CODNA_SECRETS_KEY, limited to the scopes management allows, visible to nobody, and revocable by the person in their Google or Microsoft account at any time. - A connection can stop by itself, when access is revoked. The person who can fix it is told, and every run says why.
Alternatives rejected
- One CoDNA app for every instance, verified by Google and Microsoft: a security review of weeks for each sensitive scope, renewed yearly, with one app's consent covering every customer. It may come later for small customers. An internal app per company works now and keeps consent theirs.
- Pasting an access token: it lasts an hour.
- A service account (Google) or app-only access (Microsoft): it reads the whole domain, with admin consent, rather than what one person can. That doesn't fit a connection whose owner answers for it. A provider's service account may come later as a kind of its own.
- Signing in through an AI conversation: the authorization code and tokens would pass through it, which 0025 keeps secrets from.
- Refreshing in the sandbox: a script never sees a credential (0023).
Settled when it was accepted
Tornike accepted it as proposed on 2026-11-02, settling:
- Management registers the apps, per instance, inside the company's own Google Cloud project or Microsoft tenant.
- Google and Microsoft are built in, and any OAuth 2.0 provider is registered by its addresses.
- A signed-in connection is shared like any other, and the dialog says whose account it acts as.
- When refreshing fails, the connection stops and its owner is told once, until they sign in again.
Still open
- One verified CoDNA app per provider, for customers without their own Google Cloud or tenant.
- Service accounts and app-only access, as a kind of their own.
- Incoming from these providers: Drive and Gmail push notifications, Graph subscriptions, through an incoming connection (0027), which need a validation handshake.
Building it
- OAuth:
- the instance's apps, management's view, the console,
codna oauthand the audit; - signing in for a connection, with the callback, PKCE, state, scopes and the account it acts as;
- refreshing before reads and calls, and needing to sign in again, with the inbox notice;
- revoking on removal;
- the Connections page, the share dialog's words, and
list_connections.
- the instance's apps, management's view, the console,
- Docs: automations.md, the guide to registering a Google and a Microsoft
app, and the
set-up-an-automationskill.