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:
Start authorization
Generate high-entropystate, 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.
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:- exact
statematch; - exact
issmatch with discovery metadata; - non-expired, single-use server session;
- exact redirect URI used at authorization; and
- the original PKCE verifier.
state when a trusted
redirect URI is available:
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.Revoke a token
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
stateand RFC 9207issbefore 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.