stacklok / stacklok/toolhive

`thv run` ignores `--oidc-insecure-allow-http` and `--thv-ca-bundle` — both dropped in `createOIDCConfig` (same as #1470)

Open Beginner friendly
#6,522 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

authentication bug cli oidc p1
Dominant language
Go
Stars
2.2k
Forks
300
Avg merge
1d 15h
Merged PRs (30d)
184

Description

Summary

On CLI thv run, two OIDC flags are accepted but do nothing:

  • --oidc-insecure-allow-http ("Allow HTTP (non-HTTPS) OIDC issuers for local
    development/testing")
  • --thv-ca-bundle ("Path to CA certificate bundle for ToolHive HTTP operations
    (JWKS, OIDC discovery, etc.)")

So there is no working way to point thv run at a local OIDC issuer — plain HTTP,
or HTTPS with a self-signed / private CA. Valid token gets 401 at request time.

One root cause: createOIDCConfig() in cmd/thv/app/run_flags.go builds the
auth.TokenValidatorConfig used for the auth middleware, but never sets
InsecureAllowHTTP or CACertPath. It only sets AllowPrivateIP. Same class as
#1470 ("Pass allowPrivateIP into createOIDCConfig") — that PR fixed
AllowPrivateIP in this function, the other two fields were left out.

Confirmed on v0.46.0 (commit c6c425a924) and still on main (6b40bf3d).

Why

CLI builds OIDC config two times:

  1. WithOIDCConfig(...) (pkg/runner/config_builder.go) gets all fields, including
    ThvCABundle and InsecureAllowHTTP. This fills the top-level
    RunConfig.OIDCConfig.
  2. setupOIDCConfiguration()createOIDCConfig() (cmd/thv/app/run_flags.go)
    builds a second TokenValidatorConfig. This one goes to
    WithMiddlewareFromFlags(...), and it is what builds the auth middleware
    config. Here InsecureAllowHTTP and CACertPath are missing.
    setupOIDCConfiguration also passes only runFlags.JWKSAllowPrivateIP, not
    runFlags.InsecureAllowHTTP and not runFlags.ThvCABundle.

At runtime the proxy rebuilds the auth middleware from the middleware config
(auth.CreateMiddlewarejson.Unmarshal(params)GetAuthenticationMiddleware
NewTokenValidator). So it gets InsecureAllowHTTP:false / CACertPath:"", and
OIDC discovery fails in networking.ValidatingTransport.

Proof from the run config

The proxy reads ~/Library/Application Support/toolhive/runconfigs/<name>.json.
OIDC config is there two times and they do not match. The one the proxy uses has
the fields dropped:

top-level  oidc_config.InsecureAllowHTTP           = true    # from WithOIDCConfig
MIDDLEWARE oidc_config.InsecureAllowHTTP (used)     = false   <-- dropped

top-level  oidc_config.CACertPath                   = /path/to/ca.pem
MIDDLEWARE oidc_config.CACertPath (used)            = ""      <-- dropped

           oidc_config.AllowPrivateIP (both)        = true    # survives, it is wired (#1470)

AllowPrivateIP survives but the other two do not. That is the tell — it is the
only one of the three that createOIDCConfig sets.

Repro A — --oidc-insecure-allow-http does nothing

  1. Serve any OIDC issuer over HTTP on localhost (discovery + JWKS), for
    example http://localhost:8099 with issuer = http://localhost:8099.
  2. thv run --name t --transport stdio \
      --oidc-issuer http://localhost:8099 --oidc-audience test \
      --oidc-insecure-allow-http=true --jwks-allow-private-ip=true \
      <image>
    
  3. POST /mcp with a valid Authorization: Bearer <token> (token iss/aud
    match).

Expected: HTTP is allowed for discovery (flag is set), token validates.
Actual: HTTP 401:

{"error":"invalid_token","error_description":"Invalid token: OIDC discovery failed:
 ... Get \"http://localhost:8099/.well-known/openid-configuration\": the supplied
 URL ... is not HTTPS scheme"}

Repro B — --thv-ca-bundle does nothing

  1. Serve the same issuer over HTTPS with a self-signed CA cert
    (basicConstraints=critical,CA:TRUE, SAN DNS:localhost).
  2. thv run --name t --transport stdio \
      --oidc-issuer https://localhost:8099 --oidc-audience test \
      --jwks-allow-private-ip=true --thv-ca-bundle /path/to/ca.pem \
      <image>
    
  3. POST /mcp with a valid Bearer token.

Expected: CA bundle is trusted for discovery / JWKS, token validates.
Actual: HTTP 401; proxy log:

oidc discovery failed after retries ... Get
"https://localhost:8099/.well-known/openid-configuration":
 tls: failed to verify certificate: x509: certificate signed by unknown authority

The --oidc-jwks-url "bypass" from #2288 does not help here either. Issuer
validation and the JWKS fetch use the same client, built without these fields.

Environment

  • thv v0.46.0 (Commit c6c425a924fac51c86cbade15d0e720e29a600ab), Homebrew
  • Runtime: OrbStack (Docker 29.4.0), macOS · Transport: --transport stdio
  • Also checked main @ 6b40bf3d in source — same createOIDCConfig.

Suggested fix

Same as #1470, just for the other two fields:

  • createOIDCConfig(...) — add insecureAllowHTTP bool and caCertPath string
    params, set InsecureAllowHTTP / CACertPath on the returned
    TokenValidatorConfig.
  • setupOIDCConfiguration(...) — pass runFlags.InsecureAllowHTTP and
    runFlags.ThvCABundle into createOIDCConfig.

(AuthTokenFile / --jwks-auth-token-file looks dropped by the same function
too — maybe wire it in the same change.)

Related, but not duplicates

  • #1470 (merged) — fixed AllowPrivateIP in this same function. This is the
    same thing for the last two fields.
  • #2288 (completed) — added insecureAllowHTTP for MCPRemoteProxy / the
    operator
    (spec.oidcConfig). CLI thv runcreateOIDCConfig was not part
    of it.
  • #5776 / #5787insecure_allow_http for the authserver upstream
    issuer. Different subsystem, not thv run client-token validation.

Happy to open a PR for the CLI path.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in cmd/thv/app/run_flags.go with setupOIDCConfiguration and createOIDCConfig, then compare their fields with pkg/runner/config_builder.go and the wiring from issue #1470. Trace the returned config into the middleware path. Done means the middleware configuration preserves both CLI flags and the HTTP and private-CA reproductions validate tokens successfully.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
authentication, cli, security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
82/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.