You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
OAuth token requests carry no User-Agent, so installs looping on a dead refresh token can't be traced to a version #906
Some basecamp-cli installs keep presenting a refresh token that Basecamp has already refused with invalid_grant. We can't tell which versions they are, because the CLI's token requests carry no User-Agent of their own.
What production shows (2026-10-14)
Over one hour, the token endpoint refused grant_type=refresh_token requests from client_id=basecamp-cli, all with BC5 resource indicators (urn:bc:account:…), in two patterns:
One install retries the same dead refresh token every 2 minutes, around the clock.
Another sends about 10 refreshes within 3 seconds, about every 15 minutes. That looks like an agent or script starting several commands at once, each refreshing on its own.
Overnight, a handful of installs doing this were the main source of Basecamp's OAuth abuse-tracker blocks, about 950 per half hour at the peak. The server now counts a repeated refused refresh token once (bc3#13781), so it no longer locks these people out. But the requests keep coming, and the people behind them aren't being told to sign in again.
Already fixed in current code
#711 and #854 handle this. On invalid_grant, refreshCredential (internal/auth/auth.go) forgets a BC5 credential, so later commands fail locally with "Your session has expired or was revoked" instead of retrying. So these are almost certainly older installs. Nobody has reported the loop itself, open or closed.
What's missing: a User-Agent on token requests
API requests identify the CLI (version.UserAgent() via basecamp.WithUserAgent in internal/appctx/context.go). Token-endpoint requests don't. Refresh, and presumably the code and device exchanges, go through the SDK's oauth.Exchanger, which sets no User-Agent, so they arrive as Go-http-client/2.0. On the server we can see that basecamp-cli is looping, but not which release, so we can't tell whether a current version regressed or it's only old installs.
Ask:
Send version.UserAgent() on every OAuth request: token, refresh, revocation, device authorization, discovery. Either give oauth.Exchanger a User-Agent option in the SDK, or set it from the http.Client the CLI passes in.
The original Launchpad-type credentials are deliberately not forgotten on invalid_grant (see the comment in refreshCredential). That's fine; these requests are all BC5.
Activity