toga-ai 1.0.1209 → 1.0.1210
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: session
|
|
3
|
+
slug: saml-signature-checks
|
|
4
|
+
title: TRUE-83199 Turn on SAML signature checks — scoping
|
|
5
|
+
author: amadda
|
|
6
|
+
repos: [saml, _underscore, dbchanges2]
|
|
7
|
+
framework: "2.0"
|
|
8
|
+
client: shared
|
|
9
|
+
status: active
|
|
10
|
+
created: 2026-10-29
|
|
11
|
+
updated: 2026-10-29
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Session: saml-signature-checks
|
|
15
|
+
**Date:** 2026-10-29
|
|
16
|
+
**Project/Repo:** saml + _underscore + dbchanges2 (2.0)
|
|
17
|
+
**Task:** Scope TRUE-83199 — make the saml gateway `/acs` check the IdP signature (plus Issuer, Audience, Destination, time window, RelayState client) per client, using new columns on `Core.ClientAuthentications_Sso`. No code written yet; this session was walk-through and agreement only.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## What WORKED
|
|
22
|
+
- Confirmed the gap in code: `saml/Controller/Index.php:414-415` checks only `StatusCode === STATUS_SUCCESS`. The signature check (lines 313-412) is commented-out test code with hardcoded certs, RSA_SHA1, ending in `exit`.
|
|
23
|
+
- Git history: the signature test code was added already commented out in `4bcee38` (2024-08-29, "Final changes for Compass SAML"). It was never live.
|
|
24
|
+
- Package: `litesaml/lightsaml` 3.0.3 (+ `robrichards/xmlseclibs` 4.0.0). LightSaml has a built-in signature check (`getSignature()->validate($key)`), so no new library is needed.
|
|
25
|
+
- Decoded the saved Compass test response (`Index.php:245`, inside the comment block): signed twice (Response + Assertion) with RSA-SHA256, Issuer `https://sso.compassmanager.com`, Audience `https://compass.togacommerce.com/?meta`, Destination `https://compass.togacommerce.com/?acs` (old URL), 30-second time window.
|
|
26
|
+
- `entityIdentifier` meaning resolved: it is OUR SP name (sent as `<Issuer>` in our AuthnRequest, `_underscore/Model/Core/ClientAuthentication.php:118`) — except Rate's rows, which hold Rate's IdP metadata URL.
|
|
27
|
+
- ACS URL is built in code, not stored: `samlHostUrl(env) . '/acs'` (`ClientAuthentication.php:6-11,106`). Prod = `https://saml.togahub.com/acs`. One shared `/acs` for all clients; client found by RelayState domain uuid.
|
|
28
|
+
- `/meta` publishes `WantAssertionsSigned="true"` but `/acs` never checks — code does not match what we promise.
|
|
29
|
+
- Read-only queries on `client-sandbox` Core: SSO rows = Compass Group (production, sandbox-client), CompassCanada (production, sandbox-client), Rate (production, beta), True (production). 46 clients total; SSO 4, LOGIN 1, ACTIVE_DIRECTORY 1.
|
|
30
|
+
- User lookup: `_Model_<Client>_ClientAuthentication::getAuthenticatedSsoUser()` (Compass/Rate/True in `_underscore/Model/<Client>/ClientAuthentication.php`) only matches NameID to an active user.
|
|
31
|
+
- Found: Compass `getAuthenticatedSsoUser()` maps any NameID containing `@togatech.com` to fixed user 4273. In client-sandbox `Client_Compass`, 4273 is active with roles Base, Admin, SuperUser, Compass Base, Manager, Refresh Orders (6 of 16).
|
|
32
|
+
|
|
33
|
+
## What did NOT work — DO NOT RETRY THESE
|
|
34
|
+
- Read-only `toga_query` on `prod` Core (SSO client list) was denied by the Claude Code auto-mode classifier ("The server-side auto mode classifier judged this action dangerous"). Developer said to use sandbox (`client-sandbox`) instead, read-only only.
|
|
35
|
+
- start gate blocked the ticket-reader agent and `cat` during the primer stage; read the earlier session's ticket JSON with the Read tool instead.
|
|
36
|
+
- zsh: unquoted `--include=*.php` fails with "no matches found" — quote it.
|
|
37
|
+
|
|
38
|
+
## Not tried yet (candidates for next session)
|
|
39
|
+
- Local proof of the hole (dev `http://saml` only, never sandbox/prod): fake SAMLResponse (Success + real username, no signature, encrypted to our public encryption cert) + real RelayState → POST `/acs`. Doubles as the regression test.
|
|
40
|
+
- Check prod: SSO client list and whether user 4273 in prod Compass also has Admin/SuperUser (needs permission or a teammate).
|
|
41
|
+
- Find which Destination/ACS URL each IdP uses today (server access logs), since a Sept 2025 fix `1bc6c37` handles legacy `/?acs`.
|
|
42
|
+
- Get IdP metadata (cert + entity ID) for Compass, CompassCanada, Rate (prod + beta), True, Compass sandbox.
|
|
43
|
+
- CTO second opinion before finalizing approach (schema change + auth change).
|
|
44
|
+
|
|
45
|
+
## Current file state
|
|
46
|
+
| File | Status | Notes |
|
|
47
|
+
|------|--------|-------|
|
|
48
|
+
| ~/.claude/projects/-Users-amadda-Desktop-Projects-saml/memory/user-walk-through-before-building.md | created | personal habit note |
|
|
49
|
+
| ~/.claude/projects/-Users-amadda-Desktop-Projects-saml/memory/user-communication-profile.md | created | max 2 simple bullets per reply |
|
|
50
|
+
| ~/.claude/projects/-Users-amadda-Desktop-Projects-saml/memory/MEMORY.md | modified | index lines |
|
|
51
|
+
| (no app repo files changed) | — | saml + dbchanges2 are on branch TRUE-83199; _underscore not switched yet |
|
|
52
|
+
|
|
53
|
+
## Decisions made
|
|
54
|
+
- Plan agreed (high level), numbered:
|
|
55
|
+
1. Capture the response at `/acs`.
|
|
56
|
+
2. Check the signature with the client's saved `idpSigningCertificate` (never the cert inside the response).
|
|
57
|
+
3. Compare `<Issuer>` with saved `idpEntityIdentifier`.
|
|
58
|
+
4. Check Audience/Destination = our URLs, NotBefore/NotOnOrAfter valid, RelayState client = SSO row's client.
|
|
59
|
+
5. On failure: `LOG_ONLY` logs and lets in; `ENFORCE` blocks.
|
|
60
|
+
- Add 3 columns to `Core.ClientAuthentications_Sso`: `idpEntityIdentifier`, `idpSigningCertificate`, `signatureMode` (LOG_ONLY / ENFORCE). Rationale: per-client cert storage; safe rollout. Rejected: turning the old commented code back on (hardcoded certs, SHA1, `exit`).
|
|
61
|
+
- Do not store responses in the DB. Log per-check pass/fail + Issuer, Destination, signing method to Sentry/error_log; never the full response (user data).
|
|
62
|
+
- Keep the shared `/acs` (no per-client ACS URLs) — changing would force every client to reconfigure their IdP.
|
|
63
|
+
- Use LightSaml's built-in signature validation, not hand-written xmlseclibs code.
|
|
64
|
+
|
|
65
|
+
## Blockers
|
|
66
|
+
- Open questions for the developer / team:
|
|
67
|
+
1. Put `_underscore` on branch TRUE-83199 (model needs the 3 new fields)?
|
|
68
|
+
2. Who gets IdP metadata/certs from Compass, CompassCanada, Rate, True?
|
|
69
|
+
3. Ticket misses CompassCanada and Rate beta — add them?
|
|
70
|
+
4. Client with no cert saved yet: let in + log "no cert" (proposed) or block?
|
|
71
|
+
5. Remove or dev-limit the Compass `@togatech.com` → user 4273 shortcut (in this ticket or a new one)?
|
|
72
|
+
6. Rate's `entityIdentifier` holds its IdP URL, unlike the other rows — clean up when filling the new column?
|
|
73
|
+
7. Severity ~9/10 (10 if prod 4273 is admin) — who should be told before the fix ships?
|
|
74
|
+
|
|
75
|
+
## Exact next step
|
|
76
|
+
> Answer blocker questions 1-5 with the developer, then run the `cto` agent for a second opinion on the plan above before writing the dbchanges2 `Core/` SQL file.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
_Saved by /session-save on 2026-10-29_
|
package/package.json
CHANGED