Skip to content

fix(api): drop dead auth knobs, require https sign-out URLs, generic decode errors (FIX-AUTH-P3) - #134

Merged
vanlongme merged 2 commits into
mainfrom
v10/fix-auth-p3
Oct 6, 2026
Merged

vanlongme merged 2 commits into
mainfrom
v10/fix-auth-p3

Conversation

@vanlongme

Copy link
Copy Markdown
Contributor

Summary

Auth-hardening leftovers: dead AuthConfig knobs, a plaintext-scheme gap on the sign-out redirect, and a decoder-error echo in connections.Create.

  • (a) Dead auth knobs — removed, not wired. AuthConfig.IdleTimeout was defaulted in withDefaults but never read: the sliding idle window is owned by the session store (-session-idle/TCDI_SESSION_IDLE → store.NewSessionStore, internal/backend/config.go, wire.go). Wiring it would have created a second source of truth for one window — the misleading pattern itself. AuthConfig.AllowedTenants was enforced in tenantAllowed but no flag/env/chart path could ever populate it, so the gate could never engage; -required-groups (oidc.requiredGroups) remains the supported login gate and -tenant-namespaces bounds what a tenant can do. Deleting both is the smallest correct change: no reachable behavior changes (an empty allowlist already meant allow-all), and the misleading surface is gone rather than grown. TestTenantNotInAllowlistRejected is removed with the feature it tested; TestSessionIdleAndAbsoluteExpiry no longer sets the dead field (it always drove the store's idle directly).
  • (b) Post-logout redirect is https-only off-loopback. PostLogoutRedirect and the discovered end_session_endpoint now require an absolute https URL; http is accepted only for loopback hosts (localhost, 127.0.0.0/8, ::1) via a strict netip parse (mapped/octal spellings don't count). The loopback carve-out matches the codebase's existing dev convention: the in-process test IdP (oidctest/httptest) and dev Keycloak setups serve plain http on loopback, and the whole logout suite exercises it. The Helm schema already required ^https://, so the Go side now matches it.
  • (c) connections.Create no longer echoes decoder errors. It decodes through the shared decodeJSON helper — which now returns the error so handlers can log it, and also rejects trailing data after the first document (the inline decode lacked that check). The 400 carries the generic "invalid request body"; the detail is logged server-side with the request id (WithLogger, wired to b.log). It was the sole handler deviating from the convention.

Threat-model status lines added as S25–S27; Boundary-3 and portal-session claims updated; install runbook wording aligned.

Regression evidence (fails on v0.5.0, passes here)

Run against a v0.5.0 checkout with the new test files applied:

go test ./internal/api/ -run 'TestAuthConfig_NoDeadSessionKnobs|TestNewAuthenticator_RejectsBadPostLogoutRedirect|TestNewAuthenticator_AllowsLoopbackPostLogoutRedirect|TestLogout_UntrustedDiscoveredEndpointIs204|TestCreateConnection_InvalidBodyGenericMessage|TestCreateConnection_TrailingJSONRejected'

v0.5.0 result: TestAuthConfig_NoDeadSessionKnobs FAIL (AuthConfig.IdleTimeout is a dead knob), TestNewAuthenticator_RejectsBadPostLogoutRedirect FAIL (accepted "http://portal.test/signed-out"), TestLogout_UntrustedDiscoveredEndpointIs204 FAIL ("http://idp.example/logout" → 200), TestCreateConnection_InvalidBodyGenericMessage FAIL (body echoed invalid request body: json: unknown field "attacker_field"), TestCreateConnection_TrailingJSONRejected FAIL (201 on {"takeover":true} {"extra":true}). All pass on this branch.

Verification

  • go build ./..., go vet ./... clean; gofmt clean.
  • go test ./internal/api/ ./internal/backend/ — ok; go test -race ./internal/api/ ./internal/backend/ — ok.

Test plan

  • Dead-knob guard: TestAuthConfig_NoDeadSessionKnobs
  • https enforcement: TestNewAuthenticator_RejectsBadPostLogoutRedirect (+ non-loopback http, look-alike loopback names, credential URLs)
  • loopback dev allowance: TestNewAuthenticator_AllowsLoopbackPostLogoutRedirect
  • discovered endpoint: TestLogout_UntrustedDiscoveredEndpointIs204 (+ http://idp.example/logout, http://localhost.evil.example/logout)
  • generic 400 + server-side detail: TestCreateConnection_InvalidBodyGenericMessage; trailing data: TestCreateConnection_TrailingJSONRejected

Diff stat (git diff --stat origin/main...HEAD)

 docs/runbooks/install.md         |  3 +-
 docs/security/threat-model.md    | 13 +++++---
 internal/api/adminquota.go       |  2 +-
 internal/api/adminuserlimit.go   |  2 +-
 internal/api/auth.go             | 66 +++++++++++++++++++++-------------------
 internal/api/auth_test.go        | 17 ++++++++---
 internal/api/connections.go      | 22 +++++++++++---
 internal/api/connections_test.go | 57 +++++++++++++++++++++++++++++++++-
 internal/api/data.go             |  4 +--
 internal/api/logout_test.go      | 66 +++++++++++++++++++++++++++++++++++++---
 internal/api/workspaces.go       | 21 ++++++++-----
 internal/backend/wire.go         |  3 +-
 12 files changed, 212 insertions(+), 64 deletions(-)

v1.0 fix task, requested by the project orchestrator; the advisor reviews and merges.

Generated with Devin

…decode errors (FIX-AUTH-P3)

- AuthConfig.IdleTimeout and AuthConfig.AllowedTenants were dead
  configuration surface: IdleTimeout was defaulted but never consumed
  (the session idle window belongs to the session store, configured by
  -session-idle/TCDI_SESSION_IDLE) and AllowedTenants was enforced by
  tenantAllowed but had no flag/env/chart path able to populate it.
  Both are deleted rather than wired — wiring IdleTimeout would have
  created a second source of truth for one window, and the supported
  login gate remains -required-groups / oidc.requiredGroups.
- PostLogoutRedirect and the discovered end_session_endpoint now
  require an absolute https URL; http is accepted only for loopback
  hosts (localhost, 127.0.0.0/8, ::1 — strict netip parse), matching
  the codebase's plain-http-on-loopback dev/test IdP convention.
- POST /v1/workspaces/{id}/connections no longer echoes the JSON
  decoder error to the client: it decodes via the shared decodeJSON
  helper (now returning the error for server-side logging, and
  rejecting trailing data the inline decode tolerated), answers the
  generic "invalid request body", and logs the detail with the
  request id.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 6, 2026 05:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@vanlongme
vanlongme merged commit d84d297 into main Oct 6, 2026
14 checks passed
@vanlongme
vanlongme deleted the v10/fix-auth-p3 branch October 6, 2026 05:55
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.

2 participants