View markdown

Authentication

There are two OAuth flows in play, and it's worth keeping them straight:

  1. Your AI client ↔ AdsSyncs — proves you are the workspace owner. Happens once per device.
  2. AdsSyncs ↔ each ad platform — proves AdsSyncs may read your Google / Meta / LinkedIn / Bing data. Happens once per connection, in our dashboard.

You never type a password into AdsSyncs, and we never see one.

Flow 1: AI client → AdsSyncs

When your client first talks to https://mcp.adsyncs.net/mcp, it walks through standard OAuth 2.0 Authorization Code with PKCE (RFC 6749 + RFC 7636). Discovery is via the /.well-known/oauth-authorization-server metadata document.

The short version:

  1. Client requests /authorize with code_challenge.
  2. Browser opens app.adsyncs.net — you sign in (Google or email magic link).
  3. You approve the client's access to your workspace.
  4. Client exchanges the authorization code at /token for an access token + refresh token.
  5. Subsequent MCP requests send the access token as Authorization: Bearer ….

Access tokens expire after 1 hour; refresh tokens after 30 days of inactivity. Refresh is automatic via standard OAuth refresh-token grant.

Supported grants

GrantEndpointUse
authorization_codePOST /oauth/tokenFirst-time login
refresh_tokenPOST /oauth/tokenSilent token refresh
client_credentialsNot supported (workspace authentication required)

Flow 2: AdsSyncs → ad platforms

The first OAuth flow gets you into AdsSyncs. To do useful work, AdsSyncs also needs read (and optionally write) access to your ad platforms.

You start that second flow inside the dashboard:

  1. Open app.adsyncs.net/connections.
  2. Click the platform you want to connect.
  3. We redirect you to that platform's OAuth screen. You approve.
  4. The platform redirects you back to https://app.adsyncs.net/connections/callback with an authorization code.
  5. We exchange the code for a refresh token, encrypt the refresh token at rest, and store the encrypted blob in Supabase.
  6. Back in the dashboard, you pick which ad accounts the AI is allowed to see.

From then on, when a tool runs, AdsSyncs uses the stored refresh token to mint a short-lived access token, calls the platform, and discards the access token. We never store access tokens at rest.

Scopes we request

PlatformRead scopesWrite scopes (only if you turn writes on)
Google Adshttps://www.googleapis.com/auth/adwordsSame scope; gated by our app-level Writes toggle.
Metaads_read, read_insights, business_managementads_management
LinkedInr_ads, r_ads_reportingrw_ads
Binghttps://ads.microsoft.com/msads.manageSame scope; gated by Writes toggle.

What if I revoke at the platform?

If you revoke AdsSyncs from your Google Account / Meta Business / LinkedIn / Microsoft account, the stored refresh token becomes invalid. The next tool call returns auth_required and we mark the connection as expired. Re-authorize in our dashboard to restore access — no data is lost on our side, but we can't refresh silently.

Writes are off by default

Even with the Write scope granted, mutation-tier tools are off until you toggle Writes per provider in app.adsyncs.net/settings. The toggle is workspace-wide. When off, attempting to call a mutation tool returns a clear error and nothing on the platform changes.

For MCP reviewers

Both flows comply with the MCP authorization model in the 2025-06-18 spec revision. The server emits oauth-protected-resource metadata pointing at the authorization server. Dynamic client registration (RFC 7591) is available at POST /oauth/register. Tokens are JWT-shaped, signed with rotating ES256 keys advertised at /.well-known/jwks.json.

Token & data lifecycle

  • AdsSyncs access tokens — 1h TTL, never persisted server-side.
  • AdsSyncs refresh tokens — 30d sliding window, hashed (not stored in clear).
  • Provider refresh tokens — encrypted with per-workspace keys, decrypted only inside the running container at request time.
  • Account deletion — Delete a workspace from dashboard settings → all refresh tokens are revoked at the platforms and purged from our DB.