Skip to content

[15/36] Add OC-050 auth and transport support - #49

Open
cjohnsto-nz wants to merge 3 commits into
feature/oc-100-request-type-uxfrom
feature/oc-050-auth-transport
Open

[15/36] Add OC-050 auth and transport support#49
cjohnsto-nz wants to merge 3 commits into
feature/oc-100-request-type-uxfrom
feature/oc-050-auth-transport

Conversation

@cjohnsto-nz

@cjohnsto-nz cjohnsto-nz commented Jun 14, 2026

Copy link
Copy Markdown
Owner

@cjohnsto-nz
cjohnsto-nz force-pushed the feature/oc-100-request-type-ux branch from 485d897 to 2a70d05 Compare June 14, 2026 22:50
@cjohnsto-nz
cjohnsto-nz force-pushed the feature/oc-050-auth-transport branch from 0e43ad8 to 4bcd234 Compare June 14, 2026 22:50
@cjohnsto-nz cjohnsto-nz changed the title feature/oc-050-auth-transport [15/21] Add OC-050 auth and transport support Jun 14, 2026
@cjohnsto-nz cjohnsto-nz changed the title [15/21] Add OC-050 auth and transport support [15/27] Add OC-050 auth and transport support Jun 15, 2026
@cjohnsto-nz cjohnsto-nz changed the title [15/27] Add OC-050 auth and transport support [15/36] Add OC-050 auth and transport support Jun 15, 2026
@APKiwi

APKiwi commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Verdict: request changes, one real credential-leak vulnerability. Otherwise this is strong work, and doing OAuth2/PKCE/proxy/mTLS entirely on Node core with zero new packages is the best dependency showing in the stack.

  • Major, security: cross-origin redirects forward credentials. _buildRedirectRequest copies {...current.headers} including Authorization and any api-key header, and _sendWithRedirects follows Location by default (httpClient.ts:287-298). Any server you call can 302 to an attacker origin and receive the user's bearer/basic token. Classic curl CVE-2018-1000007 class. Fix: when the redirect target's scheme+host+port differs, strip Authorization, Cookie, Proxy-Authorization and any query-placed token before re-sending. All current redirect tests are same-origin path changes, so this path is also untested.
  • Minor, security: query-placed tokens are logged in cleartext. With the new query token/api-key placement, the fully resolved URL including the live access_token goes to the "Missio Requests" output channel via the executing log line (httpClient.ts:288). Redact query auth params in that line.
  • Minor: examples/demo-api/fixtures/mtls-client.key is a real RSA private key committed to the repo, and the same key is inlined in test/httpClient.test.ts. Throwaway localhost cert, but it trips secret scanners and stays in history forever. Generate at demo/test runtime instead.
  • Minor, behavior change: gRPC/WS _applyAuth now throws on unhandled auth types where they previously no-opped and sent unauthenticated. Failing loud is right, but existing grpc-with-oauth2 requests now error, changelog-worthy.
  • Minor: encodeUrl: false is not honored for query params. _setQueryParam ignores its _encodeUrl argument and always percent-encodes via URLSearchParams, so the setting is threaded through but dead.
  • Nit: _resolveSettings now honors literal timeout 0 / maxRedirects 0 (?? vs ||). Probably fine, noting the change.
  • Nit: user-pinnable static state weakens per-request CSRF, opt-in and still validated on return, acceptable.

Positives verified: mTLS material passes through tls.ConnectionOptions and never touches disk, OAuth2 tokens live in the SecretStorage-backed store, callback URL is locked to localhost with explicit port, returned state is validated, dry-run redacts the query api key.

Tests: strong and fixture-backed (live token server, proxy auth + bypass, mTLS success and failure, redirects, unsupported-auth throws). Gaps: no cross-origin redirect stripping test (the vulnerability), no HTTPS CONNECT tunnel test, no assertion that query secrets stay out of the log.

Deps: none added. Genuinely good build-vs-buy discipline for an auth PR.

@APKiwi

APKiwi commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Re-review (whole-stack pass, per REVIEW_GUIDE.md)

Prior findings:

  • Cross-origin redirect credential leak: still open, and worse than my first pass said. _buildRedirectRequest (src/services/httpClient.ts:596-609) does { ...current.headers } onto the redirect target with no origin check, and followRedirects defaults true. Header-placed auth (basic/bearer/apikey-header/cli/oauth2-header) replays to any 3xx Location including cross-origin. On 307/308 it also preserves method and body, so a POST leaks the request body too. Critical: default-on, server-triggered, no user action.
  • Correction to my first pass: Proxy-Authorization and the mTLS client cert do NOT leak. Proxy auth is re-derived per hop and only sent to the configured proxy, and the client cert is re-matched by hostname per hop. I overcounted before.
  • Query-placed tokens are dropped on redirect rather than leaked (new URL(location, base) discards the base query). Correctness gap, not exfil.
  • Query-placed tokens logged cleartext (httpClient.ts:288 logs current.url): still open.
  • Committed RSA key examples/demo-api/fixtures/mtls-client.key, same key inlined in test/httpClient.test.ts: still open.
  • gRPC/WS _applyAuth now throws on unhandled auth: confirmed, no changelog entry.
  • 06e680c wiring (from the [03/36] Add OC-030 gRPC unary protobuf support #37 thread): verified, constructs and injects the ws/grpc/runtime clients. Lands via an ancestor, not this PR's own diff.

New (low): missio.rejectUnauthorized is a single global TLS-verification toggle, and the shipped mtls-secure.yml tells users to set it false for a self-signed local fixture, which disables cert verification for every host. Per-request scoping would be safer, at minimum reword the demo instruction.

Fix: when the redirect scheme+host+port differ, strip Authorization, auth-derived headers, and Cookie before re-sending, and add a cross-origin redirect test (both current redirect tests are same-origin, so none cover this).

Verdict: changes-needed. The redirect leak is the blocker. The rest of the transport work is solid (zero new deps, mTLS material never hits disk, tokens in SecretStorage, callback locked to localhost, per-hop proxy/cert re-resolution).

@cjohnsto-nz

Copy link
Copy Markdown
Owner Author

Fixed in f97c1c6d5aedd83d8874b2cfc850823c7d6bcb71.

The accepted findings are addressed on this branch:

  • Redirect handling now compares origins and strips credentials only when the origin changes. That includes Authorization, Cookie, Proxy-Authorization, configured API-key/OAuth/CLI auth headers, and tracked authentication query parameters. Same-origin redirects retain their credentials.
  • Request execution logs redact tracked authentication query values.
  • encodeUrl: false now preserves reserved characters in query values instead of routing them through URLSearchParams.

I am not changing the remaining items:

  • The checked-in key is paired with a self-signed localhost-only certificate and is a deterministic test fixture, not a deployable credential or a trust anchor. PR50 already excludes examples/demo-api/fixtures/*.key from the VSIX. Generating a self-signed certificate at runtime would add an OpenSSL/system dependency or a certificate-generation package, while removing this file now would not remove it from Git history.
  • Failing loudly for unsupported WebSocket/gRPC authentication is the OC-050 acceptance criterion: unsupported combinations must produce a clear diagnostic rather than silently sending an unauthenticated request. This is pre-release feature-stack behavior, so there is no released behavior change requiring a changelog entry.
  • Literal timeout: 0 and maxRedirects: 0 are intentional explicit values. They mean no request timeout and no permitted redirects respectively.
  • A caller-supplied OAuth state remains an explicit opt-in and is validated; generated state remains the default.

Validation:

  • PR49 tree: all 408 tests pass; production build passes.
  • PR49 merged with the current stack/rework tip: all 497 tests pass; compile and production build pass.
  • The final PR49→PR50 merge-tree check is STACK_CLEAN.
  • After pushing, GitHub reports PR50 MERGEABLE/CLEAN.

@cjohnsto-nz

Copy link
Copy Markdown
Owner Author

Response to the second review:

The redirect and log findings were valid and are fixed on this PR's current head, f97c1c6. Cross-origin redirects now strip Authorization, Cookie, Proxy-Authorization, auth-derived custom headers, and sensitive query parameters before the next hop. Request logging redacts configured sensitive query values. test/httpClient.test.ts now includes a two-server cross-origin regression proving that credentials and query secrets do not reach the target. 307/308 method/body preservation remains standard redirect semantics; the credential-bearing headers and query data are what are stripped at the trust boundary.

The demo RSA key and the inline test copy are intentionally retained as public, non-production fixture material. The source fixture is required to make the local mTLS demo reproducible and .vscodeignore excludes it from the packaged VSIX. PR #72 b220ecb now says this explicitly and warns never to reuse the key.

The global missio.rejectUnauthorized concern was also valid. The mTLS request and demo README now require a dedicated local-only workspace/window and explicitly say to restore the setting to true before any external request.

Final transport/full-stack verification passes, including all 23 HTTP client tests, build, TypeScript compile, 31 files / 539 tests, and 47/47 demo validation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants