toga-ai 1.0.817 → 1.0.818

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.
@@ -76,6 +76,23 @@ This mirrors the overlap the 2.0 `saml` gateway provides, though the gateway now
76
76
  a multi-cert list out of Secrets Manager rather than two files on disk — see the SP
77
77
  cert-rotation workflow.
78
78
 
79
+ ### Two ACS handlers, two SAML libraries — tell them apart from the stack trace
80
+
81
+ `store/_/app/framework.php` has **two** separate `case 'acs'` handlers that use **different SAML
82
+ libraries**. They are easy to confuse when reading a production stack trace, and they run on
83
+ **different servers**:
84
+
85
+ | Handler | Library | Host / EB environment |
86
+ |---|---|---|
87
+ | The `togahub` host block's `case 'acs'` (the `$samlKeyPairs` dual-keypair decrypt) | **LightSAML** (`decryptMultiAssertion`) | `prudential.togahub.com` → `prudential-store-prod` |
88
+ | The `prudential` host block's `case 'acs'` | **OneLogin_Saml2_Auth** (`isAuthenticated()`, `getNameId()`, `getAttribute('givenname'/'surname'/'emailaddress')`) | `prudential.togacommerce.com` → `true-togacommerce-prod` |
89
+
90
+ **Diagnostic rule:** a trace with `LightSaml\Model\Assertion\*` frames is the **togahub** block
91
+ (`prudential-store-prod`). A trace with `OneLogin` frames is the **prudential** block
92
+ (`true-togacommerce-prod`). This is how a production decrypt error gets pinned to the right
93
+ server — and the two are deployed by different pipeline actions, so fixing the wrong one changes
94
+ nothing. See the Prudential store SAML deploy-topology workflow.
95
+
79
96
  ### Private key loading
80
97
 
81
98
  The three inline `$settingsInfo` SP blocks read the key from disk:
@@ -108,6 +125,23 @@ key; it is a record of what we do, not a recommendation.
108
125
  - **Don't assume where the ACS runs.** The host that terminates `?acs` for a client may be a
109
126
  different EB environment from the storefront. See the Prudential store SAML deploy-topology
110
127
  workflow before deploying a cert change.
128
+ - **A decrypt stack trace CANNOT tell you how many keys were tried.** In
129
+ `library/LightSaml/Model/Assertion/EncryptedElementReader.php`, `decryptMulti()` returns
130
+ `$this->decrypt($key)` from *inside* the `foreach`, and re-throws `$lastException` after the
131
+ loop. The re-thrown exception keeps the stack from where it was first thrown, so the captured
132
+ trace looks **identical** whether one key was tried or several all failed. Seeing
133
+ `decryptMultiAssertion` in a trace therefore does **not** prove the dual-keypair code is
134
+ deployed — the old single-key code called the same method with a one-item array. To tell them
135
+ apart, check the deployed EB `Deployment.VersionLabel`, or look for the new code's distinct log
136
+ line `SAML keypair not readable, skipping:`.
137
+ - **A mismatched cert/key pair fails with an OAEP padding error, not a clear message.** The
138
+ symptom is `Failure decrypting Data (openssl private) -
139
+ RSA_padding_check_PKCS1_OAEP_mgf1: oaep decoding error` (also seen as
140
+ `RSA_EAY_PRIVATE_DECRYPT: padding check failed`) thrown out of the LightSAML
141
+ `EncryptedElementReader`. Confirm a suspected mismatch by comparing the **modulus** of the cert
142
+ and the key with `openssl x509 -noout -modulus` / `openssl rsa -noout -modulus` — identical
143
+ modulus means the pair matches. Compare the **committed git blobs**, not just the local working
144
+ files, or you will verify a pair that was never deployed.
111
145
 
112
146
  ## Client_True migration (deferred 2026-09-15)
113
147
 
@@ -136,6 +170,14 @@ What the migration must handle:
136
170
  architecture doc's Known issues.
137
171
 
138
172
  ## Change history
173
+ - 2026-09-15 — Documented that `framework.php` has two `case 'acs'` handlers on two different
174
+ SAML libraries (LightSAML for `togahub` vs OneLogin for `prudential`) running on two different
175
+ EB environments, and how to read a stack trace to tell which one failed. Recorded that a
176
+ LightSAML `decryptMulti()` trace cannot reveal how many keys were tried (re-thrown exception
177
+ keeps the original stack), so `decryptMultiAssertion` in a trace does not prove the
178
+ dual-keypair build is deployed — check the EB VersionLabel or the
179
+ `SAML keypair not readable, skipping:` log line. Added the OAEP-padding-error signature of a
180
+ mismatched cert/key pair and the openssl-modulus check against committed blobs. (rgirish)
139
181
  - 2026-09-15 — Rotated the `togahub.com` SP cert to the ACM-issued `togahub_com_2027-03-31.crt`
140
182
  and added a dual-keypair fallback in `?acs` so both the old and new certs decrypt during the
141
183
  overlap (`decryptMultiAssertion`). Removed three hardcoded RSA private-key blobs from
@@ -23,6 +23,28 @@ opposite one from what you would guess. Read this before deploying anything SAML
23
23
  Prudential. It also records the EB deploy failure mode we hit on 2026-09-15 (a wedged command
24
24
  agent that times out after 14 minutes while the environment still shows Green).
25
25
 
26
+ ## The full login flow — the two legs run on two different servers
27
+
28
+ The outbound (first) leg does **not** hop through togahub. It redirects the browser straight to
29
+ Microsoft Entra:
30
+
31
+ 1. User hits `prudential.togacommerce.com` (**`true-togacommerce-prod`**). The `prudential`
32
+ host block in `store/_/app/framework.php` fires when the session host is `prudential`, the app
33
+ is not in test mode, and the session has no `accountId`.
34
+ 2. With no recognized `?ssoCommand`, it falls to the `default:` case, which calls
35
+ `$auth->login($originUrl)` on `OneLogin_Saml2_Auth`.
36
+ 3. That redirects to Prudential's Entra tenant:
37
+ `https://login.microsoftonline.com/d8fde2f5-9392-4260-8a03-0ad01f4746e9/saml2`.
38
+ 4. The SP block advertises entityId `https://prudential.togahub.com/?meta` and ACS
39
+ `https://prudential.togahub.com/?acs` — so **Entra POSTs the response to togahub**, i.e. the
40
+ inbound leg lands on **`prudential-store-prod`**, a different EB environment.
41
+ 5. `prudential-store-prod` decrypts the assertion (LightSAML), then redirects back to
42
+ `prudential.togacommerce.com/?hash=<base64 of xid:first:last:email>`.
43
+
44
+ **Why this matters:** the outbound and inbound legs are different servers with different
45
+ deploys. A deploy that succeeds on one does **not** fix the other. Decrypt/cert changes belong on
46
+ `prudential-store-prod`.
47
+
26
48
  ## The routing — the pipeline action names are misleading
27
49
 
28
50
  | Hostname | CNAME → EB environment | Deployed by pipeline action |
@@ -92,7 +114,41 @@ Observed 2026-09-15: `prudential-store-prod` on `i-07ae1b81ee48df938` (t3.micro,
92
114
  A reboot restored the EB command channel on 2026-09-15, but SSM still did not report after 3
93
115
  minutes — EB working again is the signal to retry, not SSM.
94
116
 
117
+ ## Incident 2026-09-15 — OAEP decrypt error caused by the stuck deploy
118
+
119
+ Production Sentry showed, from the togahub (LightSAML) ACS handler:
120
+
121
+ ```
122
+ Failure decrypting Data (openssl private) - RSA_padding_check_PKCS1_OAEP_mgf1: oaep decoding error
123
+ ```
124
+ (also seen as `RSA_EAY_PRIVATE_DECRYPT: padding check failed`)
125
+
126
+ **Root cause: not a bad key — a deploy that never landed.** `prudential-store-prod` was still
127
+ running the pre-rotation code (commit `275d16a9`, an Aug 13 build) because its deploy kept timing
128
+ out with the wedged-agent failure above. That old code loaded a **mismatched cert/key pair**: the
129
+ `togahub_com_2025-03-25.crt` cert with `toga_private_key.key`, which belongs to the 2026-02-27
130
+ cert. Confirmed by comparing the two openssl moduli — they differ. The new dual-keypair fallback
131
+ fixes this, but it was not on the box.
132
+
133
+ The `library` repo was verified **clean and not the cause**: all three new files committed and
134
+ pushed on `_production`, both keypairs (2027 active and 2026-02-27 fallback) verified as matching
135
+ pairs against the **committed git blobs** (not just local files), both keys unencrypted (no
136
+ `Proc-Type` / `DEK-Info`), and both loading via the exact call the code makes —
137
+ `openssl_pkey_get_private($pem, "")`.
138
+
139
+ **Lesson:** when a SAML decrypt error appears right after a cert rotation, check *what build is
140
+ actually deployed* before you suspect the key material. Verify against committed blobs, and note
141
+ that the stack trace alone cannot tell you which build is running (see the cert & keys feature
142
+ doc's gotcha on `decryptMulti`).
143
+
95
144
  ## Change history
145
+ - 2026-09-15 — Traced the outbound SAML leg: `prudential.togacommerce.com` redirects the browser
146
+ straight to Prudential's Entra tenant (no togahub hop), and Entra POSTs back to
147
+ `prudential.togahub.com/?acs` on a different EB environment — so a deploy to one leg does not
148
+ fix the other. Recorded the OAEP decrypt incident: the error came from `prudential-store-prod`
149
+ still serving the pre-rotation Aug 13 build (`275d16a9`) with a mismatched cert/key pair,
150
+ because its deploy kept timing out; the `library` repo was verified clean against committed
151
+ git blobs. (rgirish)
96
152
  - 2026-09-15 — Created while rotating the `togahub.com` SAML cert. Documented that
97
153
  `prudential.togahub.com` (SAML ACS) and `prudential.togacommerce.com` (storefront) are separate
98
154
  EB environments with swapped-sounding pipeline action names, the split AWS accounts/regions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.817",
3
+ "version": "1.0.818",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",