toga-ai 1.0.937 → 1.0.938

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.
@@ -16,6 +16,7 @@ files:
16
16
  - template/pages/dashboard.php
17
17
  - config.prod.ini
18
18
  - dbchanges2/Core/2026-10-02a - TogaDeskSsoDomain.sql
19
+ - dbchanges2/Core/2026-10-02b - TogaDeskSsoDomainReuseRow.sql
19
20
  related:
20
21
  - ./staff-login-authentication.md
21
22
  - ../architecture.md
@@ -29,7 +30,7 @@ related:
29
30
  TOGa Desk staff "Sign in with SSO" via saml.togahub.com (TRUE-78064, `Sso` class, Tools pattern); open before touching SSO login, the `?saml=` return, the `[saml]` config, or the TogaDesk Core.Domains row.
30
31
 
31
32
  ## Summary
32
- TRUE-78064 replaces Active Directory for **TOGA staff** with SSO through `saml.togahub.com`, copying the Tools `App_Sso` pattern. Status 2026-10-02: written + locally tested, **not committed or deployed**; the return leg can only be fully tested in prod (needs the prod Core.Domains row + prod key).
33
+ TRUE-78064 replaces Active Directory for **TOGA staff** with SSO through `saml.togahub.com`, copying the Tools `App_Sso` pattern. Status 2026-10-02: committed on branch `TRUE-78064` (togadesk `f6f850af`, dbchanges2 `1338980` + `4b677cb`), prod Core.Domains row in place, **not deployed**. The return leg can only be fully tested in prod (prod key).
33
34
 
34
35
  Decisions (Mark + Brandon/call center, 2026-10-02):
35
36
  - Staff only. Matched to an existing `TOGaDeskSupport.people` row by email. SSO **never creates** people rows — no row = cannot log in.
@@ -54,8 +55,10 @@ Decisions (Mark + Brandon/call center, 2026-10-02):
54
55
  5. **`Sso::isAvailable()`** = every `[saml]` key set (`idp_single_sign_on_service_url`, `idp_entity_identifier`, `assertion_consumer_service_url`, `domain_uuid`, `true_client_uuid`, `return_url`), `[database_true]` + `[database_toga2core]` hostnames set, and `HTTP_HOST` == host of `[saml] return_url`. It does **not** call `getRestrictedClient()` (the root portal also calls it). So client custom-URL hosts never show the button.
55
56
  6. **Buttons** on both the root portal login card (`template/pages/dashboard.php` — the page staff land on at `/`) and `/desk/?route=signin` (where failures return).
56
57
 
57
- ### Core.Domains row (`dbchanges2/Core/2026-10-02a - TogaDeskSsoDomain.sql`)
58
- `uuid d1e526b9-e2fb-4462-935f-ed449e8f5986` (= `[saml] domain_uuid`), `clientId 1` (True), `appId 18` (togadesk-1, from a historic seed — **confirm in prod before running**), `environmentId 1`, `domain https://togadesk.togatech.com/desk/`. Unqualified `Domains`, guarded `WHERE NOT EXISTS` on uuid OR (clientId, appId, environmentId). Domain must have **no query string** — see the [gateway contract](../../../2.0/apps/saml/features/downstream-integration-contract.md).
58
+ ### Core.Domains row (prod id 261)
59
+ `uuid d1e526b9-e2fb-4462-935f-ed449e8f5986` (= `[saml] domain_uuid`), `clientId 1` (True), `appId 18` (togadesk-1, confirmed in prod), `environmentId 1`, `domain https://togadesk.togatech.com/desk/`. Domain must have **no query string** — see the [gateway contract](../../../2.0/apps/saml/features/downstream-integration-contract.md).
60
+ - `2026-10-02a` (INSERT) inserted **0 rows** in prod: slot (1, 18, 1) was already taken by id 261, an unused seed row `https://devteam.togadesk.worker.agilantsolutions.com` (old uuid `6ec4d305-104f-5c6f-8589-ed4382d8a77c`). It stays as a harmless no-op.
61
+ - `2026-10-02b` UPDATEs id 261 to the uuid + domain above, guarded on old uuid + old domain + client/app/env. Run and verified on prod Core by Mark 2026-10-02. Config `domain_uuid` did not change.
59
62
 
60
63
  ### Kill switch
61
64
  Set `[saml] domain_uuid = ""` in `config.prod.ini` and deploy → button hidden, SSO off, password login unaffected.
@@ -69,4 +72,5 @@ Set `[saml] domain_uuid = ""` in `config.prod.ini` and deploy → button hidden,
69
72
  - **Local testing ends at the gateway.** Gateway "Invalid SAMLResponse (Code = 2)" locally = local `Core_2.Parameters` key differs from prod. Expected; not a code bug.
70
73
 
71
74
  ## Change history
75
+ - 2026-10-02 — Domains INSERT was a no-op in prod (slot held by devteam seed row 261); repointed that row via `2026-10-02b`; committed, not deployed (mhammontree)
72
76
  - 2026-10-02 — Created: staff SSO button + `Sso` class, prod-only, staff matched by email, no auto-create (TRUE-78064, not yet deployed) (mhammontree)
@@ -53,7 +53,7 @@ all encrypted under the Core `API_SECRET_ACCESS_TOKEN`. `urlParameters` from Rel
53
53
  Exact build (`saml/Controller/Index.php` ~L459): `$domain->domain . '?saml=' . $authPayload . '&urlParameters=' . …`. So:
54
54
  - The `Core.Domains.domain` value **must have no query string** — the gateway appends `?saml=` blindly.
55
55
  - The payload is base64 but **not urlencoded** — a `+` arrives as a space; the consumer must `strtr(' ', '+')` before decoding.
56
- - `Core.Domains` has UNIQUE(clientId, appId, environmentId) and a unique `domain`, so an app gets **one return URL per client + app + environment**.
56
+ - `Core.Domains` has UNIQUE(clientId, appId, environmentId) (`Domains_clientId_IDX`, `dbchanges2/Core/HISTORIC/2024/2024-01-01.sql`) and a unique `domain`, so an app gets **one return URL per client + app + environment**.
57
57
 
58
58
  Known 1.0 consumers (all True staff): [Tools](../../../1.0/apps/tools/features/saml-sso-auth.md), [TOGa Desk](../../../1.0/apps/togadesk/features/staff-sso-login.md).
59
59
 
@@ -64,12 +64,14 @@ The downstream app then base64-decodes the `saml` param, JSON-parses it, decrypt
64
64
  2. Implement/extend `_Model_<Client>_ClientAuthentication::getAuthenticatedSsoUser()` in `_underscore` (the gateway repo is not modified — see the per-client extension pattern in the architecture doc).
65
65
  3. Initiate SSO via `singleSignOnServiceUrl()`.
66
66
  4. Consume the `?saml=` handoff and decrypt the UUIDs with `API_SECRET_ACCESS_TOKEN`.
67
+ 5. Register the return domain in `Core.Domains` — **check the (clientId, appId, environmentId) slot first** (see Gotchas).
67
68
 
68
69
  ## Gotchas
69
70
 
70
71
  - **Lazy-transaction write-drop.** `_Database::register()` auto-starts a lazy transaction (since Apr 2026). Any `/acs` write path (e.g. user auto-creation) must call `_Database::transactionCommit()` before exit or the writes are silently rolled back.
71
72
  - **Gateway holds no session** — every downstream app must build and own its own session from the decrypted handoff.
72
73
  - **"Invalid SAMLResponse (Code = 2)"** from the gateway = RelayState could not be decrypted with Core `API_SECRET_ACCESS_TOKEN` / `_PREVIOUS`. Seen when testing from local: local `Core_2.Parameters` key ≠ prod key. A full return-leg test needs the prod `Core.Domains` row + a deploy.
74
+ - **⚠ The Domains slot is often already taken.** Old seed rows `devteam.<app>.worker.agilantsolutions.com` (e.g. ids 259 sync, 260 toga, 261 togadesk) hold the (clientId, appId, environmentId) slot for many apps, and nothing uses them. A guarded `INSERT … WHERE NOT EXISTS` then silently inserts 0 rows. Query the slot first; if a dead seed row holds it, **UPDATE that row** (uuid + domain, guarded on its old uuid + domain) instead of inserting. No FK references `Domains.uuid`, so changing it is safe once no app config uses the old uuid. Example: TOGa Desk `2026-10-02b` ([staff SSO](../../../1.0/apps/togadesk/features/staff-sso-login.md)).
73
75
  - **The handoff is not replay-safe** — no issue time or single-use id. See [acs trust gaps](./acs-assertion-trust-gaps.md).
74
76
 
75
77
  ## Related
@@ -79,4 +81,5 @@ The downstream app then base64-decodes the `saml` param, JSON-parses it, decrypt
79
81
  - [Rate SAML SSO](../../../clients/rate/features/saml-sso.md)
80
82
 
81
83
  ## Change history
84
+ - 2026-10-02 — Gotcha: check/reuse the Domains slot (devteam seed rows) before inserting (mhammontree)
82
85
  - 2026-10-02 — Documented exact return-URL build (no query string in domain, unencoded base64), one domain per client+app+env, Code 2 meaning, TOGa Desk as consumer (mhammontree)
@@ -42,7 +42,7 @@ The public **TOGA Technology website** (repo `togatech`, standalone React SPA, a
42
42
 
43
43
  The **Tools** app (1.0; repo `tools`) authenticates True users via **SSO** through `saml.togahub.com` and reads the `Client_True` DB **read-only** to gate its internal tooling by staff persona (see `1.0/apps/tools/`). Its gateway return domain is registered in `Core.Domains` (`uuid 2927bc15-e347-4358-a430-fb28f9446d27`, `clientId 1`, `appId 30`, env 1, `https://tools.togatech.com/sso`).
44
44
 
45
- **TOGa Desk** (1.0; repo `togadesk`) staff sign in via the same True SSO (TRUE-78064, prod only, not deployed as of 2026-10-02): True user email → one AD People row → existing `TOGaDeskSupport.people` row; no auto-create. Domain row `uuid d1e526b9-e2fb-4462-935f-ed449e8f5986`, `clientId 1`, `appId 18`, env 1, `https://togadesk.togatech.com/desk/`. See [TOGa Desk staff SSO](../../1.0/apps/togadesk/features/staff-sso-login.md).
45
+ **TOGa Desk** (1.0; repo `togadesk`) staff sign in via the same True SSO (TRUE-78064, prod only, not deployed as of 2026-10-02): True user email → one AD People row → existing `TOGaDeskSupport.people` row; no auto-create. Domain row id 261 (repointed from an unused devteam seed row) `uuid d1e526b9-e2fb-4462-935f-ed449e8f5986`, `clientId 1`, `appId 18`, env 1, `https://togadesk.togatech.com/desk/` — live in prod Core 2026-10-02. See [TOGa Desk staff SSO](../../1.0/apps/togadesk/features/staff-sso-login.md).
46
46
 
47
47
  ## ⚠ "Base" maps to Super User in Client_True
48
48
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.937",
3
+ "version": "1.0.938",
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",