Skip to content

Fix auth token renewal: URL-encode requests, update tokens in place, don't start a login (or exit) from the background - #501

Open
leifrossau wants to merge 1 commit into
jstaf:masterfrom
leifrossau:pr/auth-renewal
Open

leifrossau wants to merge 1 commit into
jstaf:masterfrom
leifrossau:pr/auth-renewal

Conversation

@leifrossau

Copy link
Copy Markdown

Problem

  • Auth.Refresh builds its form body by string concatenation, so tokens or redirect URLs containing &, + or = break the request.
  • On failure it does a = newAuth(...), which only reassigns the local receiver, so callers never see the new tokens.
  • newAuth opens an interactive GTK login window and log.Fatal()s if it is closed. Reached from a renewal failure, this kills a mounted filesystem running as a systemd service: that happened to me — the service opened a login window by itself, I closed it, and the mount died.

Fix

  • Use url.Values for all token requests (Refresh and getAuthTokens).
  • Update tokens in place and save them; never reassign the receiver.
  • Transient errors (network, 5xx, 429, temporarily_unavailable/server_error) keep the tokens and are retried later (at most every 10 s).
  • A definitive rejection (400/401/403, e.g. invalid_grant) logs an error pointing at onedriver --auth-only and keeps the filesystem running from its cache. Only Authenticate() at startup still starts an interactive login.
  • The 401 handling in Request force-renews the token in place instead of starting a login.

Testing

Three new httptest-based unit tests (no account needed): in-place update and form encoding; transient 503 / connection refused keep the tokens; a 400 invalid_grant is treated as rejected without hammering the endpoint. go build ./... and go vet ./... pass. Note: the tests need fs/graph's TestMain to not require a live login to run offline.

Notes for reviewers

  • Behaviour change: no login window pops up mid-run any more. If new app permissions are needed, the user runs onedriver -a.
  • getAuthTokens, getAuthCodeHeadless and newAuth still log.Fatal; they are now only reached at startup.

🤖 Generated with Claude Code

Auth.Refresh() built its form body by string concatenation, so refresh tokens
or redirect URLs containing '&', '+' or '=' were sent broken. Use url.Values
for both the refresh and the auth code requests.

When renewal failed, Refresh() did 'a = newAuth(...)', which only reassigns
the local receiver variable and so never updated the caller's tokens. Worse,
newAuth() opens the interactive GTK login window and log.Fatal()s if it is
closed or fails, which killed a mounted filesystem running as a background
service on any error from the token endpoint (even a 5xx or a hiccup right
after resuming from suspend).

Now:
- tokens are updated in place and saved to disk,
- network errors, HTTP 5xx/429 and 'temporarily_unavailable' keep the current
  tokens and are retried later (at most every 10 seconds),
- only a definitive rejection (e.g. invalid_grant) is treated as a lost login.
  In the background this logs an error pointing to 'onedriver --auth-only'
  and leaves the tokens alone, the filesystem keeps running,
- the interactive login is only started from Authenticate() at startup,
- the 401 handling in Request() renews the tokens in place instead of
  starting a login, unless another request already did so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant