Skip to content

feat: pure grpcAuth adapter — translate .http-auth.json profiles into gRPC channel + metadata credentials (#24)#37

Merged
rwrife merged 1 commit into
mainfrom
feat/issue-24-grpc-auth-adapter
Jul 7, 2026
Merged

feat: pure grpcAuth adapter — translate .http-auth.json profiles into gRPC channel + metadata credentials (#24)#37
rwrife merged 1 commit into
mainfrom
feat/issue-24-grpc-auth-adapter

Conversation

@rwrife

@rwrife rwrife commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Closes part of #24.

Next slice on the gRPC track. Pure-core module + tests only — sets up the auth translation the (still-pending) @grpc/grpc-js wire-up will need at send time.

What lands

  • src/core/grpcAuth.tsbuildGrpcCredentials({ request, auth, authName }){ metadata, tls?, channelSecurity, warnings, errors }.
  • test/grpcAuth.test.ts — 15 vitest cases covering the rules listed below.

Reuses:

  • ResolvedAuth from src/core/auth.ts (whatever applyAuth produces for a profile — headers, query, optional tls).
  • ParsedGrpcRequest from src/core/grpc.ts (already merged).

No dependency changes. Still no @grpc/grpc-js in the tree — that ships with the transport slice.

Why a dedicated adapter

applyAuth already knows how to turn an AuthProfile into HTTP-flavoured pieces, which is what undici wants. gRPC is close but different enough to justify one small translation layer instead of teaching applyAuth about gRPC:

  • gRPC has no URL query string → apiKey in=query profiles need to hard-error, not silently drop.
  • Metadata keys must be lowercased ASCII per HPACK / http/2.
  • The transport reserves several header names (te, content-type, :authority, grpc-timeout, Host, hop-by-hop, …). We strip them from both request-block metadata and auth-emitted headers with a warning so users don't spend an hour on a mystery UNIMPLEMENTED.
  • clientCert material rides at the channel layer via TLS, not per-call metadata. We surface a channelSecurity: 'plaintext' | 'tls' | 'mtls' verdict so the extension layer knows whether to build createInsecure(), createSsl(), or createSsl() + createFromSecureContext().
  • Combining grpc:// (plaintext) with a clientCert profile is a hard mismatch — the whole point of a client cert is TLS. We block the send with a clear error message instead of quietly dropping the cert.
  • Auth-profile headers override request-block metadata but emit a warning when they shadow an explicit key so users notice the collision.

Rules (mirrored in tests)

  • Metadata keys lowercased; user-supplied casing preserved in warnings.
  • Reserved keys stripped: te, content-type, :authority, :method, :path, :scheme, :status, grpc-encoding, grpc-accept-encoding, grpc-timeout, user-agent, HTTP/1.1 hop-by-hop headers, and host.
  • Invalid metadata keys (anything outside /^[a-z0-9._-]+$/ after lowering) dropped with a warning.
  • apiKey in=query → blocking error naming the offending keys.
  • clientCert + grpcs://channelSecurity: 'mtls' and tls populated (PEM or PFX with resolved passphrase).
  • clientCert + grpc:// (plaintext) → blocking error, tls cleared, channelSecurity: 'plaintext' for display so the panel can still render the mismatch.

What's left on #24

  • @grpc/grpc-js client + ServerReflectionInfo streaming — decodes reflection bytes into the DescriptorIndex shape (which grpcPreflight already consumes).
  • Wire buildGrpcCredentials into sendGrpcRequest (extension layer) once the transport lands.
  • Response viewer: decoded JSON body + status + trailers + preflight banner reuse.
  • Integration test gated behind a local grpc test server.

Verification

  • npm run lint — clean
  • npm run typecheck — clean
  • npm run test:unit — 304 tests pass (23 files, +15 from this PR)

… credentials (#24)

Introduces src/core/grpcAuth.ts + 15 vitest cases. The adapter takes a
ParsedGrpcRequest plus the ResolvedAuth already produced by applyAuth and
returns a plain-object GrpcCallCredentials bundle the (upcoming)
@grpc/grpc-js wire-up can hand straight to Metadata + ChannelCredentials:

- metadata: lowercased, HPACK-safe map of headers to send with the call
- tls: TlsMaterial (reuses .http-auth.json clientCert profiles for mTLS)
- channelSecurity: plaintext | tls | mtls verdict for picking channel creds
- warnings / errors: structured translation notes for the response panel

Rules enforced:
- Reserved gRPC / http/2 transport headers (te, content-type, grpc-timeout,
  :authority, Host, hop-by-hop, ...) are stripped with a warning whether they
  come from the request block or an auth profile.
- Metadata keys are validated against the HPACK charset (/^[a-z0-9._-]+$/);
  invalid keys are dropped with a warning.
- apiKey in=query profiles produce a blocking error since gRPC has no URL
  query string — points users at 'apiKey in=header' instead.
- clientCert + grpc:// plaintext is a blocking error (drops the cert to make
  the mismatch obvious) and the panel keeps rendering the rest of the report.
- Auth-profile headers override request-block metadata but emit a warning
  when they shadow an explicit key so users notice the collision.

Pure, VS Code-free, transport-free — lives in src/core/ per AGENTS.md.
No new dependencies; @grpc/grpc-js is still deferred to the wire-up slice.
@rwrife rwrife merged commit 82319a9 into main Jul 7, 2026
8 checks passed
@rwrife rwrife deleted the feat/issue-24-grpc-auth-adapter branch July 7, 2026 09:02
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.

1 participant