Skip to content

OAuth discovery never advertises offline_access, which ChatGPT looks for #159

Description

@ifahimreza

What

Saddle's OAuth discovery documents never advertise offline_access in
scopes_supported. OpenAI's current guidance says ChatGPT inspects a provider's
discovery metadata for exactly that scope before relying on refresh tokens.

Why it matters

From Developer mode and MCP apps in ChatGPT
(OpenAI Help Center, updated 2026-08-22):

Verify the provider's discovery .well-known endpoints
(.well-known/openid-configuration or .well-known/oauth-authorization-server)
list offline_access (or your provider's equivalent) in scopes_supported or a
similar capability field. If offline_access (or the equivalent refresh token
scope) is not advertised or refresh tokens are not being issued, enable offline
or refresh access in your provider's admin console [...] then recreate the app
so ChatGPT fetches the updated metadata.

If OAuth is configured without offline_access, ChatGPT may lose access after
the original authorization expires because refresh-token renewal may be
unavailable, and users may need to reauthenticate.

With ACCESS_TTL = 3600, the failure mode would be a ChatGPT connector that works
for one hour and then dies, with reconnecting as the only remedy — which is
indistinguishable from the "connected but broken" reports we already field.

Current state

  • Saddle_OAuth::SCOPES = array( 'saddle:read', 'saddle:write', 'saddle:admin' )
    (includes/oauth/class-saddle-oauth.php:49)
  • scopes_supported is fed straight from that constant
    (includes/oauth/class-saddle-oauth-discovery.php:333 and :361)
  • offline_access appears nowhere in the tree
  • Refresh tokens ARE implemented and issued unconditionally
    (grant_refresh_token(), class-saddle-oauth-endpoints.php:278)
  • grant_types_supported DOES advertise refresh_token
    (class-saddle-oauth-discovery.php:364)
  • ACCESS_TTL = 3600, REFRESH_TTL = 2592000 (class-saddle-oauth-store.php:50,55)

Confidence

Suspected, not confirmed. OpenAI frames this as OpenID Connect guidance, and
Saddle is a plain OAuth 2.1 authorization server that returns a refresh token
whether or not the scope was requested. ChatGPT may well accept it regardless.
Nobody has reported an hour-long ChatGPT session dying, but we have few ChatGPT
connector users and that symptom would look like a generic disconnect.

Proposed fix

Cheap and low-risk, because the refresh machinery already works:

  1. Advertise offline_access in scopes_supported (discovery only — do not add
    it to SCOPES, which is the tier-clamp set and must stay exactly three).
  2. Accept offline_access in an authorization request as a no-op: never let
    it reach get_tier(), never map it to a capability. It must not widen access.
  3. Pin both in tests: the discovery document lists it, and a grant carrying it
    resolves to the same tier as one without it.

The scope clamp (min(site tier, granted scope)) must be provably untouched —
that is the load-bearing OAuth invariant.

Alternative

Verify empirically first: connect ChatGPT to a Saddle site and confirm whether the
connection survives past the 1-hour access-token expiry. Needs a real ChatGPT
Business/Enterprise account and an hour of waiting.

Refs the docs sweep on plugpress.co that surfaced this.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions