App Auth Guide

How Do You Call Third-Party APIs From JavaScript Without Managing OAuth Tokens?

Connect once, store a connection ID, and let a managed auth proxy add and refresh tokens for every call.

App AuthOAuthJavaScript

By Troy Tessalone · · 10 minutes

App Auth Guide

A practical field guide from Automation Ace.

Short answer: to call third-party APIs from JavaScript without managing OAuth tokens, use a managed auth platform such as Nango, Pipedream Connect, Composio, or Arcade. Your users connect their accounts through the platform's hosted flow, you store the connection ID it returns, and your backend calls the app through the platform's authenticated proxy or SDK with your server-side key plus that ID. The platform adds the access token, refreshes it, and tells you when a user needs to reconnect, so tokens never live in your code or database.

Short answer

  1. Add the app as an integration in a managed auth platform.
  2. Create a short-lived connect session on your backend for the signed-in user.
  3. Open the platform's connect UI in your frontend.
  4. Store the connection ID next to your user.
  5. Call the API from your backend through the proxy or SDK.
  6. Catch auth errors and offer a reconnect flow.

What managing OAuth tokens really involves

If you handle OAuth yourself, you build…A managed auth platform gives you…
An OAuth app per provider, redirect handling, and state checksHosted connect flows and (often) managed OAuth apps
Encrypted storage for access and refresh tokensToken storage on their side
Refresh logic, races, and rotated refresh tokensAutomatic refresh
Detection of revoked or expired accessConnection status, webhooks, and reconnect flows
Per-provider quirks (headers, base URLs, scopes)Provider configs and a proxy that adds auth

None of it is your product, and getting refresh or storage wrong breaks integrations quietly. Background: API authentication for automation.

How managed auth works

1. Your backend asks the platform for a short-lived connect session
   (tagged with your user ID).
2. Your frontend opens the platform's connect UI with that session.
3. The user signs in to the app and approves access.
4. The platform stores the tokens and sends your backend the connection ID
   (webhook or callback). You save it next to your user.
5. Later, your backend calls the API through the platform with
   your server-side key + the connection ID. The platform adds the token,
   refreshes it when needed, and forwards the request.

Step 1: Pick a platform and add the integration

Add the app (for example HubSpot, Google, Slack) in the platform's dashboard. For OAuth apps, choose the platform's managed OAuth app if one is offered, or enter your own client ID and secret with the platform's callback URL. Compare options in the best managed app auth platforms, and see managed OAuth app vs your own OAuth client.

Step 2: Let users connect from your app

Your backend creates a short-lived session for the signed-in user. With Nango, for example:

// Backend route: create a connect session for the signed-in user
const r = await fetch("https://api.nango.dev/connect/sessions", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.NANGO_SECRET_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    tags: { end_user_id: user.id, end_user_email: user.email, organization_id: user.orgId },
    allowed_integrations: ["hubspot"],
  }),
});
const { data } = await r.json();      // data.token (about 30 minutes), data.connect_link
return { sessionToken: data.token };

Your frontend then opens the platform's connect UI with that token (with Nango's frontend SDK, nango.openConnectUI({ sessionToken })). Tags tie the resulting connection back to your user and organization.

Step 3: Save the connection ID

When the user finishes, the platform notifies your backend (Nango sends an auth webhook with the connectionId and your tags). Store it on your side against the user or organization that owns it. That ID, not a token, is what your code uses from now on.

Step 4: Call the API through the proxy

// Backend only (Node.js 18+). NANGO_SECRET_KEY never goes to the browser.
const res = await fetch("https://api.nango.dev/proxy/crm/v3/objects/contacts?limit=10", {
  headers: {
    "Authorization": `Bearer ${process.env.NANGO_SECRET_KEY}`,
    "Provider-Config-Key": "hubspot",          // your integration ID in Nango
    "Connection-Id": user.hubspotConnectionId, // saved when the user connected
  },
});
if (!res.ok) throw new Error(`HubSpot via Nango: ${res.status} ${await res.text()}`);
const contacts = await res.json();

The path after /proxy is the app's own API path. Query parameters and headers pass through, and Nango adds the Authorization header for the app. With the SDK:

// Same call with Nango's Node SDK (npm i @nangohq/node)
import { Nango } from "@nangohq/node";
const nango = new Nango({ apiKey: process.env.NANGO_SECRET_KEY });

const res = await nango.get({
  endpoint: "/crm/v3/objects/contacts",
  params: { limit: 10 },
  providerConfigKey: "hubspot",
  connectionId: user.hubspotConnectionId,
  retries: 3,
});

The same idea with Pipedream Connect

// Pipedream Connect API proxy (shape per Pipedream's docs at the time of writing)
// Target URL is URL-safe base64 encoded; the SDK can do this for you.
const target = Buffer.from("https://slack.com/api/conversations.list")
  .toString("base64url");
const url = `https://api.pipedream.com/v1/connect/${PROJECT_ID}/proxy/${target}` +
            `?external_user_id=${encodeURIComponent(user.id)}&account_id=${accountId}`;

const res = await fetch(url, {
  headers: {
    "Authorization": `Bearer ${pipedreamAccessToken}`, // from your Pipedream OAuth client
    "x-pd-environment": "production",
  },
});

Composio and Arcade work the same way from your point of view: you pass a user ID (and optionally a connected account ID) and run tools or requests, and the platform supplies credentials.

Proxy, SDK, actions, or raw token

ApproachHow your code calls the appGood for
Authenticated proxyHTTP request to the platform with a connection or account IDAny endpoint, your own data models
Platform SDK request helpersSDK call with the connection IDLess boilerplate, built-in retries
Prebuilt actions or toolsRun “create contact” or “send message” by nameCommon operations without reading API docs
Fetch the token, then call directlyAsk the platform for a fresh access token, call the API yourselfSDKs that need a raw token; keep it server-side and short-lived

Prefer the proxy or tools. Fetching raw tokens brings back the risks you were avoiding; if a library insists on one, fetch it per request on the server and never log or persist it. For how these approaches differ, see direct API calls vs authenticated proxies vs MCP tool calls.

Handling expired and revoked access

  • Refresh is automatic, but users can still revoke access, change passwords, or lose permissions.
  • Watch for auth errors from the proxy (for Nango, an invalid_credentials error code on the connection) and mark the connection as needing attention.
  • Offer a reconnect that keeps the same connection ID (Nango calls this a reconnect session).
  • Retry rate limits with backoff; see 429 errors.

Common mistakes

MistakeFix
Calling the platform from the browser with your secret keyOnly call it from your backend, Worker, or serverless function
Trusting a user ID sent by the browserDerive the user from your own session or token
Storing tokens you fetched “to save a call”Fetch per request or use the proxy; tokens rotate
Ignoring invalid connectionsCatch auth errors and show a Reconnect button
One connection ID shared by every userStore connection IDs per user or per organization

Running on the edge? See managed app auth in a Cloudflare Worker. Building an agent? See how an AI agent can access a user's apps without seeing their tokens.

Frequently asked questions

How do I call a third-party API without managing OAuth tokens?

Use a managed auth platform. Users connect through the platform’s hosted flow, you store the connection ID it returns, and your backend calls the API through the platform’s proxy or SDK with your server-side key and the connection ID. The platform adds and refreshes the token.

What is an authenticated API proxy?

A service you send API requests to, along with a connection or account ID, that adds the user’s credentials and forwards the request to the app’s API. Nango and Pipedream Connect both offer one.

Can I call the managed auth proxy from the browser?

No. The proxy is authenticated with your secret key, which must stay on your server, serverless function, or Worker. The browser only uses short-lived connect session tokens to start the connection flow.

Do I still need my own OAuth app?

Not always. Several platforms offer managed OAuth apps for common providers. You may want your own client for branding, scopes, quotas, or provider review requirements.

What happens when a user revokes access?

Calls start failing with an auth error. Detect it, mark the connection as needing attention, and send the user through a reconnect flow that updates the credentials while keeping the same connection ID.

App AuthOAuthJavaScript

Disclaimer: Managed app auth platforms, their SDKs, endpoints, plans, and features change quickly; details reflect public documentation and coverage at the time of writing, so confirm current details with each provider before building. Automation Ace is not affiliated with Composio, Nango, Pipedream, Arcade, or the other companies named here unless a link is marked as an affiliate link. This article may include links to apps, products, or services; some links may be affiliate links, which means Automation Ace may earn a commission at no extra cost to you.

Build Better Systems

Ready to automate with confidence?

Share your tools, process, and goals. Automation Ace can design the Zapier, AI, and automation setup that fits your business.

Start a Project →