Build SAML login (enterprise/internal/loginhandler), mirroring OIDC
Adds GET /auth/saml/login + POST /auth/saml/acs alongside the existing OIDC pair, both converging on the same upsert-user/resolve-tenant/ issue-session path. loginhandler.New now takes an optional *saml.ServiceProvider, RegisterRoutes registers each protocol's routes independently so either, both, or neither can be configured. SAML's replay/unsolicited-response defense (InResponseTo, standing in for OIDC's state) is carried via a SameSite=None sentry_saml_request cookie -- None because the ACS endpoint receives a cross-site POST from the IdP's origin, which SameSite=Lax cookies are never sent on. enterprise-auth's main.go now fetches+parses SAML_IDP_METADATA_URL at startup (samlsp.FetchMetadata) and wires the result through. Verified to the same bar as OIDC: a real fake IdP (crewjam/saml/samlidp, genuine XML signing/verification) drives the full login->ACS->session-cookie round trip and negative paths (bad InResponseTo, missing request cookie, missing email/NameID, no/multiple tenant memberships), all in loginhandler/saml_test.go, no Docker needed. The login-form HTML is bypassed by pre-seeding a saml.Session directly into samlidp's session store and presenting the matching `session` cookie -- an IdP-supported shortcut (confirmed by reading GetSession), the same "skip the UI, keep the crypto real" approach oidctest gave the OIDC tests. Writing that test caught two real bugs in internal/saml.ParseResponse, both fixed here: it never called r.ParseForm() before reading the POSTed SAMLResponse field, so every real ACS POST would have silently decoded an empty response; and its email-attribute matching missed urn:oid:0.9.2342.19200300.100.1.3 (the standard LDAP "mail" OID), which is what an IdP sends by default absent an explicit AttributeConsumingService request for "email" -- exactly what samlidp's own DefaultAssertionMaker does, and plausibly what real IdPs' default SAML app templates do too. Docs (CLAUDE.md, threat-model.md, architecture.md, enterprise/README.md, phase-4-runbook.md, docker-compose.yml's enterprise-auth comment) updated in lockstep: SAML login moves from "protocol mechanics only" to "built, verified with a real fake IdP, not yet tried against a real external IdP or a running enterprise-auth container" -- the same disclosed gap OIDC already carried.
This commit is contained in:
+9
-5
@@ -241,11 +241,15 @@ services:
|
||||
# here so it can be built/run/curled like every other service, but
|
||||
# deliberately NOT wired into api's ENTERPRISE_AUTH_URL or alerting's
|
||||
# API_SERVICE_TOKEN below: turning that on makes every /query and
|
||||
# /dashboards request require a valid session/service token, and there
|
||||
# is no OIDC/SAML login flow built yet to issue a human one (see
|
||||
# enterprise/cmd/enterprise-auth/main.go's doc comment) -- flipping it
|
||||
# on by default would break the web UI and sentryctl with no way to
|
||||
# log in. See enterprise/README.md for how to turn enforcement on for
|
||||
# /dashboards request require a valid session/service token. Both
|
||||
# OIDC and SAML login flows now exist (enterprise/internal/loginhandler),
|
||||
# but this compose file sets neither OIDC_ISSUER_URL nor
|
||||
# SAML_IDP_METADATA_URL, so both stay disabled here, and there's still
|
||||
# no admin UI to create the first tenant_memberships row -- see
|
||||
# /docs/phase-4-runbook.md sections 3a/3b for wiring a real IdP and
|
||||
# bootstrapping that row by hand. Flipping enforcement on by default
|
||||
# without that would break the web UI and sentryctl with no way to log
|
||||
# in. See enterprise/README.md for how to turn enforcement on for
|
||||
# manual testing (mint a service token, set the two env vars, restart).
|
||||
enterprise-auth:
|
||||
build:
|
||||
|
||||
Reference in New Issue
Block a user