Release candidate — 1.0.0-rc.6
StitchAPI
GuidesAuth

oauth2

OAuth2 client_credentials: fetch, cache, and auto-refresh a token behind the capability boundary.

Use oauth2 when an API authenticates with the OAuth2 client_credentials grant and you want the access token fetched, cached, and refreshed for you — the stitch holds the client id and secret, the caller never sees the token.

Example

import { , ,  } from 'stitchapi';

const  = ({
    : 'https://api.example.com',
    : '/orders',
    : ({
        : 'https://api.example.com/oauth/token',
        : ('CLIENT_ID'),
        : ('CLIENT_SECRET'),
        : 'orders.read',
    }),
});

The first call to orders() POSTs the form-encoded token request, caches the token in the stitch store (TTL from expires_in), and attaches it as Authorization: Bearer …; later calls reuse the cached token until it nears expiry, all behind the capability boundary.

Options

tokenUrl is the client_credentials token endpoint. Pass clientId and clientSecret as secret resolvers — e.g. env('CLIENT_ID') — so the credentials are read at call time and never committed. scope is an optional space-delimited scope string.

The token is cached and auto-refreshed through one refresh envelope (CONTRACT.md P24). A bare StatusMatch — a status, a list, or a predicate — is shorthand for refresh: { on }: the status(es) that mean the token was rejected and force a fresh fetch plus retry (default [401], and refresh: 419refresh: { on: [419] }). Reach refresh.skew — how many milliseconds before expiry to refresh so the token is never used mid-flight (default 30_000) — through the envelope form: refresh: { skew: '1m' }.

Give two stitches the same key plus a shared store and one token serves them all — across stitches and across workers, surviving restarts. See Reference → Auth strategies for every field.

Client authentication

By default the client authenticates with client_secret_post: client_id and client_secret ride in the form body. Some providers require client_secret_basic instead — the credentials as an HTTP Basic header, with only grant_type in the body. Set clientAuth: 'basic' for those:

import { , ,  } from 'stitchapi';

const  = ({
    : 'https://sms.example.com',
    : '/messages',
    : ({
        : 'https://sms.example.com/oauth/token',
        : ('CLIENT_ID'),
        : ('CLIENT_SECRET'),
        : 'basic',
    }),
});

The Basic header is Base64 of id:secret, encoded without Node's Buffer so it works in a browser bundle too. For provider-specific extras on the token request, audience sets the OAuth2 audience field, params merges arbitrary fields into the token-request body (e.g. resource, or a custom grant_type), and headers adds headers to it. The client credentials are always applied last, so params can never shadow them.

Anti-pattern: don't rely on the default token cache across multiple workers — it lives in memory, process-local, so every worker fetches and refreshes its own token and hammers the token endpoint; give the stitches a shared key and a durable store so one token serves them all. See Pluggable store.

The token lives only in the store and the outgoing Authorization header — the caller, an agent included, never receives it. That is the capability boundary; see Capability, not credential.

See also

On this page