Preserve authorization across rel-link paging - #11
Merged
Merged
Conversation
PowerShell 7.6 drops the Authorization header when Invoke-RestMethod follows a rel=next link, so a paged request 401s the moment it reaches page 2. Any list longer than one page fails outright instead of returning a short result, which makes -All unusable against a real account. PreserveAuthorizationOnRedirect keeps the header attached. It is broader than the fix this deserves, since it also preserves the header across cross-origin redirects, which is the case the strip was added to close. Every rel-link here points back at the same api.github.com the request already went to, so the exposure it reopens is theoretical while the breakage it repairs is not. Upstream is scoping the strip to actual origin crossings; this comes out once that ships. The GitLab client carried the same paging shape and the same fix. Upstream issue: PowerShell/PowerShell#27861 Scoped fix: PowerShell/PowerShell#27981 Forward-port: PowerShell/PowerShell#27873
chris-peterson
added a commit
to chris-peterson/pwsh-gitlab
that referenced
this pull request
Sep 12, 2026
## Context `Invoke-GitlabApi` pages by handing `Invoke-RestMethod` the `-FollowRelLink` flag and letting it walk the `rel=next` chain GitLab puts in its `Link` header. PowerShell 7.6 drops the `Authorization` header when it follows one of those links, so the request for page 2 goes out unauthenticated. Anything using `-All` over a result longer than one page breaks — GitLab serves 20 per page by default, so that is most of them. `-PreserveAuthorizationOnRedirect` keeps the header attached. The upstream bug is [PowerShell#27861](PowerShell/PowerShell#27861); the properly scoped fix is in flight as [PowerShell#27981](PowerShell/PowerShell#27981) (with [#27873](PowerShell/PowerShell#27873) as the forward-port). This comes back out once that ships — hence the comment pointing at the issue rather than a bare flag. ## Review guide **Core change, and the whole diff** — [`src/GitlabCli/Utilities.psm1`](https://github.com/chris-peterson/pwsh-gitlab/pull/167/changes#diff-bcad0c863b627b9e28cb755bd24ac1be9ab715a01e6750820f02009a80ec2c3cR103) ## Approach & trade-offs The flag is broader than what this deserves: it also preserves the header across *cross-origin* redirects, which is the case the strip was added to close. Every `rel=next` GitLab emits here points back at the same host the request already went to — measured, not assumed — so the exposure it reopens is theoretical while the breakage it repairs is not. The narrower alternative was switching the header to `PRIVATE-TOKEN`, which PowerShell doesn't strip. It works, but it's GitLab-specific and swapping the auth scheme to dodge a client bug is a bigger change to reason about than one flag with an expiry date on it. ## Validation Measured against `gitlab.getty.cloud` with a raw `Invoke-RestMethod`, so the result isn't shaped by this module. Both endpoints page at 20 per request: | Endpoint | without the flag | with the flag | | --- | --- | --- | | `issues?assignee_username=…` (112 items) | **401 on page 2** | 112 items | | `merge_requests?…&scope=all` (55 items) | **21 items, exit 0, no error** | 55 items | The second row is the one worth pausing on. The same defect does not always surface as a failure — that call returned a partial result and reported success, and a short list looks like an answer. This module's callers have no way to tell 21 from 55. The effect is endpoint-specific rather than uniform: on GitHub's API the plain `/issues` list endpoint throws the same 401 while `/search/issues` walks its chain intact. So which symptom you meet, or whether you meet one at all, depends on the call you reach for first. [`pwsh-github#11`](chris-peterson/pwsh-github#11) carries the identical one-line fix. The two are independent — neither has to merge first.
chris-peterson
marked this pull request as ready for review
September 12, 2026 16:02
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.
Context
Invoke-GithubApipages by handingInvoke-RestMethodthe-FollowRelLinkflag and letting it walk therel=nextchain GitHub puts in itsLinkheader. PowerShell 7.6 drops theAuthorizationheader when it follows one of those links, so the request for page 2 goes out unauthenticated and comes back401. Anything using-Allover a result longer than one page breaks.-PreserveAuthorizationOnRedirectkeeps the header attached.The upstream bug is PowerShell#27861; the properly scoped fix is in flight as PowerShell#27981 (with #27873 as the forward-port). This comes back out once that ships — hence the comment pointing at the issue rather than a bare flag.
Review guide
Core change, and the whole diff —
src/GithubCli/Utilities.psm1Approach & trade-offs
The flag is broader than what this deserves: it also preserves the header across cross-origin redirects, which is the case the strip was added to close. Every
rel=nexthere points back at the sameapi.github.comthe request already went to — measured, not assumed — so the exposure it reopens is theoretical while the breakage it repairs is not.Validation
Measured against
api.github.comwith a rawInvoke-RestMethod, so the result isn't shaped by this module:/issues?filter=assigned&state=open(106 items, 20/page)/search/issues(96 items, 20/page)The effect is endpoint-specific, and the plain list endpoint is the one
Get-Issue -Mineuses./searchwalks itsrel=nextchain intact, which is why this can look like it isn't happening depending on which call you reach for first.Worth knowing for anyone auditing the blast radius: on GitLab the same defect also produced a silent partial result rather than an error —
merge_requests?scope=allreturned 21 of 55 items and exited 0. No equivalent silent case turned up on GitHub, where both endpoints tested were all-or-nothing, but a short list that looks like an answer is the failure worth watching for.pwsh-gitlab#167carries the identical one-line fix. The two are independent — neither has to merge first.