Skip to main content
ApiGlow v0.3.0
Copy page

Credentials, and how they travel.

Credentials live in environment variables, never in the page. This guide covers how the try-it console authenticates, and how you hand a ready-made environment to a teammate with a setup link.

The environment, first

An environment is a name, a base URL, variables, and default headers added to every request. It also carries a colour from a closed palette, shown on the selector, so production in red is visible before the send rather than after.

The selector shows a badge when credential variables the schema expects are missing. On a schema’s first load, a button offers to create one environment per servers entry — on request, never automatically.

Environments declared in the config are added by name on load, without overwriting anything the reader already has. From then on, the reader’s own storage is authoritative.

What the schema declares, the console understands

Every scheme in your securitySchemes gets a working input: bearer, basic, apiKey in a header, a query parameter or a cookie, OAuth2, and OpenID Connect.

The convention is one variable per scheme: scheme X reads auth.X. Set it in the active environment and every operation secured by that scheme sends it. Leave it empty and the {{var}} interpolation blocks the send rather than leaking a literal {{auth.X}} to your API.

Sensitive variables are masked in the UI. Their values live in the browser’s local storage, unencrypted — treat a shared machine accordingly.

OAuth2 without a backend

ApiGlow runs Authorization Code + PKCE entirely in the browser: a full-page redirect to your identity provider, the code exchange in the page, and the token into auth.X. There is no server of ours, and no client secret in the config.

Client credentials is supported too, for machine-style APIs. The demo’s OAuth flows point at a mock provider; against your API you supply your real authorization and token URLs.

The config carries the public client id, per scheme, and the reader can override it with the auth.X.clientId variable:

{ "oauth": { "petstore_auth": { "clientId": "public-client-id" } } }

The drivable flows are exactly those two. implicit and password are documented from the schema but carry no “Get a token” button.

What a browser cannot do

Two schemes are rendered with a stated platform limit instead of a broken button, in three places each — the operation doc, the auth summary, and the try-it credentials block:

  • mutualTLS is documented but never sent: “a browser cannot present a client certificate — call this endpoint from a client that can (cURL, your own code).”
  • The OAuth2 device-authorization flow (OpenAPI 3.2) is not run: “it polls the token endpoint outside the browser — obtain the token elsewhere and paste it into the variable.”

Both limits are the platform’s rather than the schema’s, so the documentation stays complete.

When the host page already has a session

The docs page is static: no server renders it, so nothing can inject a per-user token at delivery time. But the reader often already has a session — a cookie, or SSO — in that browser, and your backend could mint an API token for them on demand.

The host credentials bridge closes that gap. The page that embeds ApiGlow registers a provider, and the app asks it for credentials when it needs them.

<!-- after the ApiGlow script tag -->
<script type="module">
  apidoc.registerCredentialsProvider(async ({ specId, reason }) => {
    const r = await fetch('/api/docs-token', { credentials: 'include' })
    if (!r.ok) return null
    return { bearerAuth: (await r.json()).access_token }
  })
</script>

The returned map is keyed by scheme names from your securitySchemes. A string fills auth.X; an object fills auth.X.username-style suffixes. The reader lands on docs where the credential badge already reads “from host”.

Four rules govern the bridge:

  • It fills only what is empty. A host value applies only where the selected environment has none. A value the reader types, or one an OAuth flow writes, always wins, and clearing it falls back to the host value.
  • Memory only. Host values are never persisted — not in storage, not in history exports (they are captured as sensitive, so redaction applies), not in environment exports. Reload the page and the provider is asked again.
  • One retry on 401. If a request whose credentials came from the host gets a 401, the app asks the provider once more (reason: "expired") and replays once, with a visible note in the response panel.
  • The trust boundary is the page. Only host-page code can register a provider. Nothing in an OpenAPI document can trigger or influence one, and the app never fetches a credentials URL itself.

Two more members cover the trivial cases: apidoc.setCredentials(map) pushes values the host already holds, and apidoc.clearCredentials() empties the bridge on host logout. A classic script that may run before the app should listen for the apidoc:ready event rather than assume the global.

In multi-spec, one provider serves every spec: it receives the active specId and routes internally.

There is no config key for any of this. The bridge is a runtime surface, so a token cannot end up written in a public page. The provider’s own fetch typically authenticates with the session cookie, which means credentials: "include" and the CORS constraints that come with it are yours to meet.

The environment manager carries a band of its own — Set a teammate up — with two ways in.

The first shares the environment you have. You pick what travels: checked variables carry their value, and unchecked ones travel by name with an empty value, a skeleton your teammate fills in with their own credentials. Non-sensitive rows start checked, sensitive ones start unchecked.

If you do include a credential, the dialog says so in place rather than behind a confirm: whoever holds the link holds that credential, and a link outlives the conversation that carried it. Links past 2,000 characters get a warning too, since some chat clients truncate silently.

Building one without creating the environment

Sharing normally starts from an environment you already own, which means creating it locally first, with values you may not want to keep on your machine.

The second entry point, Build from scratch, removes that requirement: a form that describes the environment the team should have, and produces the link. It is also offered as a card on the welcome view, which is where a lead actually lands.

The form is a pure generator: it writes nothing. Building a link is not creating an environment, and the dialog has no path to your storage. Fill in a name, a base URL, a colour, variable rows and default-header rows. Each variable row carries a sensitive flag and a send the value checkbox, unchecked by default for sensitive rows, so a credential travels only when you say so.

The link rebuilds as you type, and the bounds are checked live, naming the one that failed rather than handing you a link the recipient would refuse.

Preview as recipient closes the builder and opens the link right there. It is not a rendering of the landing dialog; it is the landing dialog, so the preview cannot drift from what your teammate sees. Cancel writes nothing. On a multi-spec install, the link targets the spec you are on.

Here is the same idea, running on this page rather than in the app — a real setup link, in the exact format ApiGlow reads, pointed at the demo:

BUILD A SETUP LINK · TRY IT ON THE DEMO

Unchecked: auth.api_key travels by name with an empty value — the skeleton your teammate fills in.

Open the link and you are on the receiving end. Nothing is written until you agree: the link opens a review table — each variable marked created, updated or kept, sensitive values hidden, empty ones flagged “you fill this in” — and the link is immediately stripped from the address bar.

Three guards refuse wrong situations: an unreadable link changes nothing, a link built for another spec changes nothing, and on an environmentsLocked install a setup link cannot change environments at all.

You do not need either form to produce a link. It is your docs URL followed by a setup pseudo-parameter in the hash, carrying base64url-encoded JSON — which is what lets a CI job or an intranet page generate onboarding links for a whole team.

https://docs.example.com/#/?setup=<base64url(payload)>

The payload, version 1:

{
  "v": 1,
  "spec": "petstore",
  "env": {
    "name": "Staging",
    "baseUrl": "https://staging.petstore.example",
    "vars": [
      ["auth.api_key", "", true],
      ["team", "docs-squad"]
    ],
    "headers": [["X-Team", "docs"]]
  }
}

What you need to know to write one yourself:

  • Each variable is a row [name, value] or [name, value, true], where the third slot marks it sensitive — the recipient’s field is masked from the moment it is created. The flag travels even when the value does not.
  • An empty value is the skeleton: the variable is created by name, and the recipient fills it in.
  • spec is optional but recommended on a multi-spec install. Without it there is no wrong-API guard.
  • baseUrl, color and headers are optional.
  • The encoding is unpadded base64url (+→-, /→_, = stripped). In Node: Buffer.from(JSON.stringify(payload)).toString("base64url").
  • Bounds are enforced on read: 8 KB of decoded JSON, 50 variables, 20 headers, 200 characters per name, 4 KB per value. Past any of them the whole link is refused as unreadable, and a name used twice refuses it too. Past ~2,000 URL characters, beware chat clients that truncate silently.

The generated first call

features: { "onboarding": true } adds a “First call” page at the top of the reference nav (#/first-call), for the reader who has never sent anything.

ApiGlow picks the simplest read the schema declares — a GET, no body, nothing left to type once the declared examples are pre-filled — and shows it under a three-step preamble: pick a language, enter credentials, press Send. The steps happen in the ordinary try-it rail, so the reader ends up where they will work.

It is off by default, and absent anyway when the schema declares no such read.

Locked deployments

environmentsLocked is for controlled corporate installs: environments come from the site’s configuration and readers cannot edit them, which also disables incoming setup links.

See the configuration reference for the key, and Try-it & CORS for why a correctly authenticated call can still be blocked by the browser.