Repository navigation
Commit f0f0eb1
authored
fix(runtime): require an access token for OAuth status (#1725)
## Description
`Runtime.GetAllServers` treats a persisted DCR registration as a valid
OAuth token even when `AccessToken` is empty. That makes the status
payload report `authenticated=true` and `oauth.token_valid=true`, and
the CLI print `Token: Valid`, while the token store correctly returns
`ErrNoToken`.
Require a non-empty access token before projecting authentication and
expiry, matching `PersistentTokenStore.GetToken`. A DCR-only record
still identifies an OAuth server, including autodiscovery, so it keeps
the login action. The read preserves the registration. Actual
non-expiring tokens, expired-token semantics, and static-header
authentication are unchanged.
Fixes #1723.
### Regression proof
The test creates registrations through `UpdateOAuthClientCredentials` in
temporary BBolt storage and exercises the real runtime projection. Five
scenarios run in both explicit and autodiscovery modes:
- DCR registration only.
- Empty access token despite future expiry and refresh metadata.
- Real non-expiring access token.
- Real unexpired access token.
- Real expired access token.
On base `14cb288f`, the four empty-access-token scenarios fail
(`authenticated` is true instead of false, `oauth_status` is
authenticated instead of none); the six real-token controls pass. With
the fix, all ten pass. The test also checks `ErrNoToken` for the
incomplete records, the login action, absence of access-token expiry
claims, and preservation of the registration.
### Scope
This changes the runtime status projection only. It does not change
token storage, refresh, or OAuth instance retirement. #1722 separately
tracks pending logins being retired by replacement and callback UX; the
retirement barrier remains intact. All fixtures are synthetic and use
`.invalid`; no real providers or credentials are involved.
## Testing
- [x] Tested locally with the regression red/green cycle described
above.
- [x] Added tests that demonstrate the fix and preserve the real-token
controls.
- [x] Existing tests pass in the scope below; full upstream CI is
pending.
Go 1.26.0, darwin/arm64:
```sh
go test -short -race -p 2 -timeout 20m -skip 'E2E|Binary|MCPProtocol|TestInfoEndpoint|TestGracefulShutdownNoPanic|TestSocketInfoEndpoint' ./internal/...
go test -short -race -p 2 -tags server -timeout 10m ./internal/serveredition/...
go build -o mcpproxy ./cmd/mcpproxy
go build -tags server -o mcpproxy-server ./cmd/mcpproxy
golangci-lint run --config .github/.golangci.yml ./...
golangci-lint run --config .github/.golangci.yml --build-tags server ./...
```
Results: 79 base packages and six Server-edition packages passed with
race detection; both builds passed; the CI-pinned golangci-lint v2.9.0
reported zero issues for both editions. The full runtime and OAuth
packages also passed with race on Go 1.27.3 (`go test -race
./internal/runtime ./internal/oauth -count=1 -timeout=10m`).
The REST smoke from `scripts/test-api-e2e.sh` passed 70/70, including
the Server-edition audit companion. The harness assertions were
unchanged; its scratch setup used loopback listeners, disabled local
sockets/tray, and a dedicated npm cache. The first smoke attempt could
not start the `everything` fixture because the global npm cache returned
`EACCES`; using the dedicated cache resolved that setup failure without
changing global permissions.1 parent 4f4e1bb commit f0f0eb1
2 files changed
Lines changed: 104 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2795 | 2795 | | |
2796 | 2796 | | |
2797 | 2797 | | |
2798 | | - | |
| 2798 | + | |
| 2799 | + | |
| 2800 | + | |
2799 | 2801 | | |
2800 | 2802 | | |
2801 | 2803 | | |
| |||
2829 | 2831 | | |
2830 | 2832 | | |
2831 | 2833 | | |
2832 | | - | |
| 2834 | + | |
| 2835 | + | |
| 2836 | + | |
| 2837 | + | |
| 2838 | + | |
| 2839 | + | |
2833 | 2840 | | |
2834 | 2841 | | |
2835 | 2842 | | |
| |||
0 commit comments