Skip to main content
implements OAuth 2.0 Authorization Code for confidential partner backends, PKCE S256, RFC 9207 authorization response issuer identification, rotating refresh tokens, and RFC 7009 token revocation. It is not an OpenID Connect login provider and does not issue ID Tokens or expose UserInfo.

Environments and discovery

Always read endpoint URLs and token lifetimes from authorization-server metadata: Set AIRCTRL_API_ORIGIN, AIRCTRL_API_URL, and AIRCTRL_WEB_URL from Environments. The payloads below use Production values only to make each example concrete; substitute one complete environment as a unit. Example response:
The values above show production. QA and local metadata return their own issuer and endpoints.

Start authorization

Generate high-entropy state, a PKCE verifier, and its SHA-256 challenge in the confidential backend. Store state, the verifier, redirect URI, pending external-key intent, and expiry in a short-lived server session.
Required parameters: For a private App, consent is bound to the App owner’s organization. For a public App, consent lists every organization where the approving person is an active member, holds oauth-installations:connect, and has active product access. When several qualify, the user must choose one before Allow is enabled. Active subscription, trial, and payment-retry (past_due) states qualify. The authorization code and every resulting token are bound to the selected organization and one installation. The App owner’s organization, other eligible organizations, and other installations are not fallback targets. The prepared consent request remains pending for 10 minutes. After approval, the one-time authorization code remains valid for 300 seconds, as published in airctrl_token_lifetimes.

Handle the callback

Successful callback:
Before exchanging the code, require:
  • exact state match;
  • exact iss match with discovery metadata;
  • non-expired, single-use server session;
  • exact redirect URI used at authorization; and
  • the original PKCE verifier.
Denied or invalid authorization preparation redirects with the original state when a trusted redirect URI is available:
Do not exchange a callback containing error. The server rechecks permission and product access when the user chooses Allow access. If that final transaction fails before a code is committed, the authorization page reports the API error and does not produce a partner callback.

Exchange the authorization code

Authenticate the confidential client with HTTP Basic. Use a form-encoded body; do not put the client secret in JSON, query parameters, or browser code.
refresh_token is present only when offline_access was authorized. Store tokens encrypted and bind them to the OAuth installation/organization, never to a browser user session.

Rotate a refresh token

Serialize refreshes per installation. Two workers must never use the same refresh token concurrently.
Atomically replace the stored refresh token after success. Reusing an older token is replay and revokes the family. A successful refresh resets the 90-day refresh inactivity window.

Revoke a token

Success is HTTP 200 with no response body. Revocation is idempotent; an unknown, already-revoked, or wrong-type token still returns 200 after valid client authentication. Revoking an access token ends that token; revoking a refresh token ends its refresh family. This protocol operation does not delete projects, gateways, provider credentials, logs, or audit history.

Protocol errors

Authorization errors return through the trusted redirect URI when possible. Token and revocation errors return JSON:
Authorization redirects use standard OAuth error values and stable error_description codes: The decision endpoint also rejects a missing or changed organization with oauth_target_required or oauth_target_invalid. Never retry with an organization that was not shown by the prepared consent response. A user denial returns error=access_denied without an error_description.

Security requirements

  • Keep the client secret, verifier, provider keys, and all returned tokens in the confidential backend.
  • Use a fresh state and PKCE verifier for every authorization transaction.
  • Validate both state and RFC 9207 iss before exchanging the code.
  • Use exact discovery-derived endpoints; do not construct them from a browser URL.
  • Serialize refreshes and replace refresh tokens atomically.
  • Never log Basic authorization, token request bodies, token responses, or callback codes.