Phase 4: real OIDC human login (enterprise/internal/loginhandler)

Closes the other major named gap from this phase: until now, there was
no way for a human to actually log in -- only /alerting's RoleService
credential could be minted. GET /auth/oidc/login and GET
/auth/oidc/callback drive the real coreos/go-oidc flow already wired in
enterprise/internal/oidc: CSRF state in a short-lived cookie, code
exchange, ID token verification, upserting a users row, resolving
tenant/role from exactly one tenant_memberships row (refusing outright
on zero or more than one, rather than guessing), and issuing a real
session cookie.

Unlike everything else built this phase, this one is genuinely verified
end to end: the tests spin up coreos/go-oidc's own oidctest fake IdP,
which signs real RS256 ID tokens, and drive the full login->callback->
session-cookie round trip through actual signature verification -- no
live database or Docker needed, so nothing here is asserted without
having actually been run in this session. Also fixes a real bug caught
while wiring this into enterprise-auth's main.go: assigning a nil
*oidc.Provider to the handler's interface field would have produced a
non-nil interface wrapping a nil pointer (Go's classic typed-nil trap),
silently breaking the "OIDC not configured" no-op path -- New() now
takes the concrete pointer type and checks it before ever converting to
the interface, with a regression test pinning the fix down.

Still missing: SAML's equivalent (ACS endpoint), a tenant-picker UI for
multi-membership identities, and any admin UI to actually create a
tenant_memberships row (today that's manual SQL, documented in the
runbook's new bootstrap walkthrough).
This commit is contained in:
2026-08-13 23:00:35 -07:00
parent 1d57e697b1
commit 1fab02abd5
10 changed files with 752 additions and 50 deletions
+13
View File
@@ -252,12 +252,25 @@ services:
context: enterprise
dockerfile: Dockerfile
container_name: sentry-enterprise-auth
depends_on:
metadata-migrate:
condition: service_completed_successfully
ports:
- "8082:8082"
environment:
# Dev-only, same framing as CLICKHOUSE_PASSWORD above -- not a real
# secret. Must be at least 32 bytes (see internal/config.Load).
ENTERPRISE_SESSION_SIGNING_KEY: "sentry-dev-only-session-signing-key-32bytes+"
POSTGRES_ADDR: "metadata-postgres:5432"
POSTGRES_DATABASE: "sentry_metadata"
POSTGRES_USERNAME: "sentry"
POSTGRES_PASSWORD: "sentry-dev-only"
# Where the browser lands after internal/loginhandler sets a
# session cookie -- web's mapped host port (see web's build args
# for why this is localhost:3000, not the compose network's
# service DNS name: the browser resolves this, not a sibling
# container).
POST_LOGIN_REDIRECT_URL: "http://localhost:3000"
healthcheck:
test: ["CMD", "/enterprise-auth", "-healthcheck"]
interval: 5s