fix(auth): use the configured subfolder when refreshing OAuth tokens
Why
Reported in #8532 (closed): on a self-managed instance installed under a subfolder, refreshing an expired OAuth2 access token goes to
https://gitlab.example.com/oauth/tokeninstead of
https://gitlab.example.com/<subfolder>/oauth/tokenso the refresh reaches the wrong endpoint and fails.
NewConfigTokenSource built its endpoint from the hostname alone:
oauth2Config := gitlaboauth2.NewOAuth2Config(fmt.Sprintf("%s://%s", protocol, hostname), clientID, redirectURL, scopes)which is the same construction #8399 (closed)
replaced for the authorization URL. That fix
(!3604 (merged)) added oauthBaseURL and
routed StartFlow and the device flow through it — but the refresh path kept building its own
URL, and so kept ignoring hosts.<hostname>.subfolder.
It was the only remaining site. Every OAuth base URL in internal/:
| site | before |
|---|---|
oauth2.go (authorization) |
oauthBaseURL |
device.go (device flow) |
oauthBaseURL |
token_source.go (refresh) |
hand-built from hostname |
What
oauthBaseURL takes a protocol, and all three flows call it, so the base URL is built in one
place and a future flow cannot reintroduce the omission by writing its own.
Nothing else changes. AuthEndpoint already falls back to DefaultProtocol on an empty
string and trims slashes around the subfolder, so the two existing callers keep their exact
behaviour.
Verification
| check | result |
|---|---|
go test -race ./internal/oauth2/... |
ok |
go build ./..., go vet ./internal/oauth2/... |
clean |
The regression test drives a real refresh against an httptest server with an expired
token and asserts the path the server actually received:
| subfolder | expected path |
|---|---|
gitlab |
/gitlab/oauth/token |
/gitlab/ |
/gitlab/oauth/token |
| (none) | /oauth/token |
It asserts on the received path rather than on the constructed oauth2.Config, because the
constructed value is exactly what looked right before and was wrong once the URL left the
helper.
Mutation-tested. Restoring the old fmt.Sprintf("%s://%s", protocol, hostname):
--- FAIL: .../subfolder_configured expected: "/gitlab/oauth/token"
--- FAIL: .../surrounding_slashes_are_trimmed expected: "/gitlab/oauth/token"Both subfolder cases fail and no subfolder still passes, so the test is pinning the subfolder specifically and not just any change to the URL.
Closes #8532 (closed)