gitproto: retry credential auth against the actual challenge URL · Entire

gitproto: retry credential auth against the actual challenge URL

5ec540d→main·

Soph·1mo ago·3 files·+247 added/-46 removed

The 401-retry path replayed against c.EndpointURL even when the 401 came from a cross-host redirect (e.g. github.com → replica.example). Go's http.Client strips Authorization on cross-host redirects (per shouldCopyHeaderOnRedirect), so the retry hit the challenger without auth, got 401 again, and we Reject'd the user's valid credentials — locking them out on the next sync, since Lookup would then return nothing.

Production flow that triggers this:

  1. GET origin/info/refs (anonymous) → 307 → challenger returns 401.
  2. res.Request.URL.Host == challenger; challengeURL keyed correctly.
  3. Lookup(challenger) returns the stored creds.
  4. Retry builds URL from c.EndpointURL = origin, attaches auth.
  5. http.Client follows 307 → strips Authorization → challenger 401.
  6. We hit the reject branch → Reject(challenger, valid-creds). Lost.

Fix:

doInfoRefsRequest / doPostRPCRequest now accept an optional target *url.URL to support the override; nil keeps the previous "build from c.EndpointURL" behaviour for non-retry callers.

Regression tests:

Sessions

dcdc9414badfView transcript

Changes

3

// TestMain isolates the package's tests from the developer's local
// credential helper. Without this, `git credential fill` could find
// stored credentials for 127.0.0.1 (e.g. cached from an earlier test
// run) and turn EnsureAuthForService's would-be no-op into a real
// auth-probe POST, throwing off receive-pack POST counts.
// credential helper. EnsureAuthForService probes /git-receive-pack with a
// flush-packet POST unconditionally (required to discover cross-host
// auth challenges and auth-on-POST-only gates), so without stubbing the
// helper, `git credential fill` could find stored credentials for
// 127.0.0.1 (e.g. cached from an earlier test run) and attach them,
// changing the wire shape of the push the test under inspection.
// The probe itself still happens — receive-pack POST counts include it —
// but the stub guarantees no credentials are attached and the probe
// returns without further side effects on the helper.
// Tests that need to exercise helper behaviour explicitly should
// restore auth.GitCredentialCommand in their own setup.

Test EnsureAuthForService_SkipsProbeWhenHelperHasNoCredentials

This test avoids a wasted no-op POST when there are no credentials to attach anyway — the common shape for anonymous syncs and for syncs running in test/CI environments with no credential helper configured.