Repository navigation
feat: refresh an expired token and retry instead of failing the request - #34
Merged
Merged
Conversation
The az token boardwalk starts with is good for about an hour, and the program is meant to be left open longer than that. Every request after the expiry was failing with an auth error the user could do nothing about, since the az login behind the token was still current. send now retries once against a fresh token when Azure DevOps refuses the credentials. Refusal has two shapes: a plain 401, and a 203 whose body is the sign-in page — the second was not even reaching the error path before, it was being decoded as the payload and failing as malformed JSON. The refresh is single-flight: a request whose token was already replaced by another goroutine takes the new one rather than minting a second. When the refresh fails, the login itself is gone, and the error says so rather than retrying. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TqaFJiW6wTt1daQqZjpBDC
…ssage in the error The stub client left newToken nil, so a test whose server answered 401 reached the real az CLI: green on a developer's machine, red on a build agent that has never logged in. It now hands back a token of its own. A failed refresh was also reporting only itself. The rejected request's own message is the half that says which rejection it was — a token that aged out and one that was never scoped for the resource both arrive as 401 — so the error now carries both. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TqaFJiW6wTt1daQqZjpBDC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
boardwalk gets its bearer token from
az account get-access-tokenonce, at startup, and holds it for the life of the process. That token is good for about an hour; the program is meant to be left open longer. Past the expiry every request failed with an auth error the user could do nothing about — theaz loginbehind the token was still current, only the token had aged out.What changed
Client.sendnow retries once against a fresh token when Azure DevOps refuses the credentials.>= 300check and was decoded as the payload, failing as malformed JSON.req.GetBody, so a PATCH or PUT that hit the expiry is resent intact.az loginrather than retrying.newTokenis a test seam in the same style asbaseURL: production leaves it nil and falls back to the az CLI.No UI change — the refresh is invisible unless it fails.
Testing
Six tests in
internal/azdo/client_test.go, each checked against a mutation of the code it covers to confirm it fails when the behavior is removed: 401 then success, 203 then success, a retried write resending its body,replayablerestoring a drained body, a failed refresh namingaz loginand stopping, a rejection that survives the refresh being reported after exactly one retry, and eight concurrent rejections sharing a single refresh.go vet ./...andgo test ./... -raceare green.🤖 Generated with Claude Code
https://claude.ai/code/session_01TqaFJiW6wTt1daQqZjpBDC