The integration catalog — Google, Slack, Microsoft, and more — connects through Nango, an OAuth connection broker. The tenant server is the only thing that talks to Nango; the Flutter app never sees provider tokens. This page covers running Nango, the callback proxy, registering provider credentials, and hardening the admin surface.
Nango handles OAuth connections, token storage, and proxied API calls. It is not the automation engine — agents and workflows execute on the Python LangGraph engine. Nango is the connection layer those tools call through.
A user clicks Connect; the tenant server injects the provider's client credentials into Nango; the user completes consent at the provider; and Nango keeps the resulting tokens. Every later API call is proxied through Nango so tokens stay server-side.
You can use Nango Cloud or run Nango yourself. Self-hosting keeps every OAuth secret on your own infrastructure, which is the right choice for an air-gapped or compliance-bound deployment.
| Option | Best for | Operational note |
|---|---|---|
| Nango Cloud | Fastest path; no infrastructure to run | Provider secrets live in Nango's hosted environment |
| Self-hosted Nango | Full data residency and air-gapped deployments | Action execution needs the full process set, not just the server container (see below) |
The nangohq/nango-server:hosted image runs only the server process — enough for OAuth connections, the catalog, and action discovery. Executing integration actions also needs the orchestrator, jobs, persist, and runner processes. Run those as additional containers (the deploy defines them under a nango-full Compose profile). Without them an action fails with a "runtime is starting up or not available" message.
OAuth providers redirect the user's browser to a publicly reachable callback URL, which would otherwise mean exposing Nango on its own public hostname — surface area you can simply avoid. Instead, the tenant server hosts a thin proxy that replays the OAuth callback to Nango over the internal Docker network, so Nango never needs a public hostname.
| Environment | Callback URL |
|---|---|
| Production | https://ai.yoffice.ai/integrations/oauth-callback |
| Local dev | https://tenant-api.dev.yoffice.ai/integrations/oauth-callback |
The proxy forwards the query string, cookies, and User-Agent verbatim, relays Set-Cookie and 3xx Location headers back to the browser without following redirects itself, and caps the response body at 1 MiB. It accepts GET only. To switch the flow over, update each provider's authorised redirect URI to the proxy URL and point Nango's NANGO_SERVER_URL at the proxy so it advertises the matching redirect_uri.
Each provider you enable needs its own OAuth app registered both at the provider and in Nango. The flow is the same for every integration:
In the provider's developer console (Google Cloud Console, Slack API, Azure AD, the GitHub developer settings, …) register an OAuth app and request the scopes that integration needs. Note the client ID and client secret.
Point the app's authorised redirect URI at the tenant-server callback proxy — for production, https://ai.yoffice.ai/integrations/oauth-callback. Many providers let you register more than one URI, so you can keep an old one in place while you migrate.
Open the Nango dashboard (reached over an SSH tunnel for a hardened self-hosted instance), choose Set up new integration, pick the provider template, and paste the client ID and secret. Nango's provider key uses hyphens — for example google-mail for Gmail, not gmail.
In Your Office AI, open Integrations, click Connect on the integration, and complete the consent flow. Nango stores the access and refresh tokens; the Flutter app never touches the provider or Nango directly.
Nango provider keys use hyphens, and they do not always match the Flutter catalog ID. A mismatch here is the classic cause of a "connect fails" bug. The Flutter side maps its catalog ID to the Nango key via CatalogIntegration.nangoProviderKey — that field is the source of truth.
| Flutter catalog ID | Nango provider key |
|---|---|
google_calendar | google-calendar |
google_drive | google-drive |
gmail | google-mail (not gmail) |
google_tasks | google-tasks |
outlook_calendar | outlook-calendar |
microsoft_teams | microsoft-teams |
onedrive | onedrive |
slack | slack |
github | github |
Some providers serve binary content from a different host than their JSON API (Dropbox uses content.dropboxapi.com for downloads). The materializer routes those through Nango's Base-Url-Override header, so add the content host to the provider's "Base URL Override Allowlist" in the Nango provider config.
Nango's admin dashboard and admin API show every configured provider's client ID and secret, and Nango OSS ships with its auth gate off, so keep the admin surface private — reachable only from your own infrastructure, never the public internet. The defenses below make that straightforward.
| Defense | What it does |
|---|---|
| Loopback port binding | Bind Nango's port 3003 to 127.0.0.1 only, so it is never reachable from the public internet. |
| nginx path allowlist | Allow only the OAuth, Connect, and webhook paths through the vhost; every other path (including /api/v1/* and the dashboard) returns 403. |
| SSH-tunnel-only dashboard | Reach the dashboard by forwarding localhost:3003 over SSH. The tunnel itself is the access control — no public login. |
| Remove from public DNS | Once the callback proxy is live, delete the public Nango vhost and DNS record entirely so Nango has no public surface area at all. |
Set a strong NANGO_ENCRYPTION_KEY so stored tokens are encrypted at rest inside Nango, and verify inbound provider webhooks by their signed secret. On the Your Office AI side, stored integration credentials and API keys get an additional layer of AES-GCM encryption at the application layer.
Continue to Voice providers to enable the unified voice bridge, or read the Integrations guide for how the catalog is used day to day.