Skip to content

Preserve authorization across rel-link paging - #11

Merged
chris-peterson merged 1 commit into
mainfrom
preserve-auth-on-rel-link-paging
Sep 12, 2026
Merged

chris-peterson merged 1 commit into
mainfrom
preserve-auth-on-rel-link-paging

Conversation

@chris-peterson

@chris-peterson chris-peterson commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Context

Invoke-GithubApi pages by handing Invoke-RestMethod the -FollowRelLink flag and letting it walk the rel=next chain GitHub 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 and comes back 401. Anything using -All over a result longer than one page breaks. -PreserveAuthorizationOnRedirect keeps 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 diffsrc/GithubCli/Utilities.psm1

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 here points back at the same api.github.com the 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.com with a raw Invoke-RestMethod, so the result isn't shaped by this module:

Endpoint without the flag with the flag
/issues?filter=assigned&state=open (106 items, 20/page) 401 on page 2 106 items
/search/issues (96 items, 20/page) 96 items 96 items

The effect is endpoint-specific, and the plain list endpoint is the one Get-Issue -Mine uses. /search walks its rel=next chain 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=all returned 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#167 carries the identical one-line fix. The two are independent — neither has to merge first.

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 chris-peterson self-assigned this Sep 10, 2026
@chris-peterson chris-peterson added the bug Something isn't working label Sep 10, 2026
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
chris-peterson marked this pull request as ready for review September 12, 2026 16:02
@chris-peterson
chris-peterson merged commit 80008f5 into main Sep 12, 2026
10 checks passed
@chris-peterson
chris-peterson deleted the preserve-auth-on-rel-link-paging branch September 12, 2026 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant