Require HTTPS for the OIDC discovery URL #127

Closed
opened 2026-07-28 14:34:55 +00:00 by claude-bot · 1 comment

Severity: LOW

The finding

The security audit confirmed there is no user-driven SSRF in the application: the only outbound
HTTP is Authlib fetching the OIDC discovery document and the token/JWKS endpoints it advertises, and
that URL comes from CIRCA_OAUTH_SERVER_METADATA_URL — operator configuration, not user input. A
grep for requests., urllib, and httpx. in application code found no direct outbound calls.

One residual weakness remains worth closing:

The metadata URL scheme is unvalidated. http:// is accepted. The discovery document is fetched
over whatever scheme is configured, and the endpoints inside it are then trusted for token
exchange and JWKS retrieval. A MITM on a plaintext discovery fetch can redirect token exchange to an
attacker-controlled endpoint.

This is a compromised-or-intercepted-IdP scenario rather than user-driven SSRF, which is why it is
LOW — but the deployment currently defaults to non-HTTPS-enforcing (CIRCA_ENVIRONMENT defaults to
development), and the IdP is on the same internal network, so plaintext is a realistic
configuration.

Fix

  • Require https:// for CIRCA_OAUTH_SERVER_METADATA_URL and refuse to start otherwise, except
    when explicitly in development.
  • Fold this into the startup validation added by #14, so it fails fast with a message naming the
    variable.
  • When #43 adds a configurable provider, keep the metadata URL strictly operator-supplied — it must
    never become user input.

Done when

  • A non-HTTPS metadata URL is rejected at startup outside development
  • The check is covered by a test
  • #43's configurable-provider work preserves the operator-only constraint

References

  • backend/app/config.py:25,48-56
  • backend/app/auth/oauth.py:31

Related: #14 (fail fast on insecure configuration), #43 (GenericOAuth2Backend).

## Severity: LOW ## The finding The security audit confirmed there is **no user-driven SSRF** in the application: the only outbound HTTP is Authlib fetching the OIDC discovery document and the token/JWKS endpoints it advertises, and that URL comes from `CIRCA_OAUTH_SERVER_METADATA_URL` — operator configuration, not user input. A grep for `requests.`, `urllib`, and `httpx.` in application code found no direct outbound calls. One residual weakness remains worth closing: **The metadata URL scheme is unvalidated.** `http://` is accepted. The discovery document is fetched over whatever scheme is configured, and the endpoints *inside* it are then trusted for token exchange and JWKS retrieval. A MITM on a plaintext discovery fetch can redirect token exchange to an attacker-controlled endpoint. This is a compromised-or-intercepted-IdP scenario rather than user-driven SSRF, which is why it is LOW — but the deployment currently defaults to non-HTTPS-enforcing (`CIRCA_ENVIRONMENT` defaults to `development`), and the IdP is on the same internal network, so plaintext is a realistic configuration. ## Fix - Require `https://` for `CIRCA_OAUTH_SERVER_METADATA_URL` and refuse to start otherwise, except when explicitly in development. - Fold this into the startup validation added by #14, so it fails fast with a message naming the variable. - When #43 adds a configurable provider, keep the metadata URL strictly operator-supplied — it must never become user input. ## Done when - [ ] A non-HTTPS metadata URL is rejected at startup outside development - [ ] The check is covered by a test - [ ] #43's configurable-provider work preserves the operator-only constraint ## References - `backend/app/config.py:25,48-56` - `backend/app/auth/oauth.py:31` Related: #14 (fail fast on insecure configuration), #43 (GenericOAuth2Backend).
claude-bot added this to the v0.1.1 milestone 2026-07-28 14:34:55 +00:00
Author

Fixed in 4333b5e. CI green.

resolved_server_metadata_url now refuses a non-https:// discovery URL outside development, with
an error naming the variable and explaining why.

The reasoning recorded in the code: the discovery document determines the token and JWKS endpoints,
which are then trusted for the code exchange and signature verification. So the transport of this
one request is load-bearing for the entire authentication flow — an attacker on the path can
redirect token exchange to an endpoint they control.

Plaintext is still permitted in development so a local provider on http://localhost is not
blocked. Tested for http://, uppercase HTTP://, and a non-HTTP scheme, plus the development
exemption and the Google fallback being https.

**Fixed** in 4333b5e. CI green. `resolved_server_metadata_url` now refuses a non-`https://` discovery URL outside development, with an error naming the variable and explaining why. The reasoning recorded in the code: the discovery document determines the token and JWKS endpoints, which are then trusted for the code exchange and signature verification. So the transport of this one request is load-bearing for the entire authentication flow — an attacker on the path can redirect token exchange to an endpoint they control. Plaintext is still permitted in development so a local provider on `http://localhost` is not blocked. Tested for `http://`, uppercase `HTTP://`, and a non-HTTP scheme, plus the development exemption and the Google fallback being https.
Sign in to join this conversation.
No description provided.