toga-ai 1.0.295 → 1.0.296
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.
|
@@ -6,8 +6,8 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["jcardinal"]
|
|
9
|
+
updated: 2026-07-09
|
|
10
|
+
owners: ["jcardinal", "bala"]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Component/Api/V2/V2.php
|
|
13
13
|
- api2/Component/Api/V2/Response/Response.php
|
|
@@ -54,8 +54,27 @@ PersonaApp, UserGlobal, UserApp — each overriding the previous when set. A Cli
|
|
|
54
54
|
`isOverridable = 0` **locks** the value against all later layers. For the persona layers, the **first
|
|
55
55
|
persona** (in the user's persona order) that sets it wins. The resolved code is validated against
|
|
56
56
|
`Languages.code` (fallback `en`), embedded in the JWT `id.language` claim, and echoed on every
|
|
57
|
-
response as `audience.language`. API-credential auth gets no language.
|
|
58
|
-
|
|
57
|
+
response as `audience.language`. API-credential auth gets no language.
|
|
58
|
+
|
|
59
|
+
**Language re-resolution on token refresh (user tokens only).** `/auth/refresh` now re-resolves the
|
|
60
|
+
user's language and embeds it in the newly minted tokens, so a language switch takes effect on the
|
|
61
|
+
next hourly refresh **without a full re-login** (previously the refresh path copied the decoded JWT
|
|
62
|
+
payload verbatim and only reset `exp`/`iat`, so a language change was invisible until re-auth). The
|
|
63
|
+
re-resolution runs **only** for user tokens — it reads `id.client.user.uuid` off the token and skips
|
|
64
|
+
entirely for API-credential and public tokens (no language claim). It rebuilds the resolution context
|
|
65
|
+
from the existing token — loads `_Model_Client_User` by `uuid` (→ `userId`), `_Model_Core_App` by
|
|
66
|
+
`id.app.uuid` (→ `appId`), and takes `personaIds` from `id.client.personas` — then calls the **same**
|
|
67
|
+
private `resolveSettingValue(slug: _Model_Core_Setting::SLUG_LANGUAGE, context: {appId, personaIds,
|
|
68
|
+
userId})` helper the login path uses (same 8-layer cascade; the client DB is already registered at this
|
|
69
|
+
point). The resolved code is validated against `_Model_Client_Language` (load by `code`):
|
|
70
|
+
- **Valid** → sets `$this->jwtPayload->id->language` to the resolved code.
|
|
71
|
+
- **Unset/empty** → defaults to `'en'`.
|
|
72
|
+
- **Invalid** (non-empty but absent from `Languages`) → **keeps the token's current language** and adds
|
|
73
|
+
a `WZ-4` warning (see below). It deliberately does **not** fail the refresh — a bad setting must not
|
|
74
|
+
log an active user out on their hourly refresh (contrast the login path, which hard-fails with EV-16).
|
|
75
|
+
|
|
76
|
+
Placement is load-bearing: the `id.language` mutation happens **after** client-DB registration and
|
|
77
|
+
**before** both the access and refresh tokens are signed, so both new tokens carry the updated language.
|
|
59
78
|
|
|
60
79
|
**Invalid-language hard failure (auth time).** The resolved code is validated against the client's
|
|
61
80
|
`Languages` table. An **unset/empty** setting defaults to `en` (no error). But a **non-empty** code
|
|
@@ -64,6 +83,14 @@ that does **not** exist in `Languages` now **fails authentication** with HTTP 40
|
|
|
64
83
|
with the offending `language` code in the message `identifiers` — instead of silently falling back to
|
|
65
84
|
`en`. A misconfigured language must surface loudly, not silently degrade.
|
|
66
85
|
|
|
86
|
+
**Invalid-language soft warning (refresh time).** On the refresh path an invalid code is handled with a
|
|
87
|
+
distinct, non-fatal WARNING instead of EV-16: `WZ-4 DEFINED_MESSAGE_WARNING_INVALID_LANGUAGE_SETTING`
|
|
88
|
+
(type WARNING, group VALIDATION, `{language}` identifier placeholder; defined in `Response.php`, added
|
|
89
|
+
via `addDefinedMessage(...)` so the warning has a stable catalogued code). Because it is a WARNING it
|
|
90
|
+
carries no `status` key and does not change the response status — the refresh succeeds and the token
|
|
91
|
+
keeps its current language. This is deliberately softer than EV-16 (status 400), which would fail the
|
|
92
|
+
refresh and log the user out.
|
|
93
|
+
|
|
67
94
|
**Auth-mint status finalization (subtle).** The auth success path sets `status = 201` and attaches the
|
|
68
95
|
tokens. EV-16 is signalled by a local flag `$invalidLanguageSetting` set at the resolution site; the
|
|
69
96
|
finalization branches `if (empty($invalidLanguageSetting))` → `201` + tokens, else
|
|
@@ -134,6 +161,9 @@ None — uniform across all clients. The sidecar table + ACL ship via `dbchanges
|
|
|
134
161
|
`__get` — `isset()`/`?? null` always read false/null. See
|
|
135
162
|
[_Model magic-field access](../../_underscore/features/model-magic-field-access.md); this was the
|
|
136
163
|
cause of the uuid not appearing in the fallback warning.
|
|
164
|
+
- **Refresh re-resolution is user-token-only.** If you extend the JWT with new audience-shaped claims,
|
|
165
|
+
remember the refresh path rebuilds context from the *existing* token (uuid/app-uuid/personas) — it
|
|
166
|
+
does not re-read anything the token doesn't carry.
|
|
137
167
|
- Migrations not yet executed at time of writing; needs live verification.
|
|
138
168
|
|
|
139
169
|
## Change history
|
|
@@ -149,6 +179,14 @@ None — uniform across all clients. The sidecar table + ACL ship via `dbchanges
|
|
|
149
179
|
record `uuid` and dedupes per-record (`recordFieldId:uuid`). Documented the `isOverridable = 0`
|
|
150
180
|
client-global lock as a config gotcha. Discovered the `_Model` `__isset` gap behind the missing-uuid
|
|
151
181
|
bug (see linked _underscore doc). Live-tested on the local Compass DB. (jcardinal)
|
|
182
|
+
- 2026-07-09 — `/auth/refresh` now re-resolves the user's language (same `resolveSettingValue` cascade as
|
|
183
|
+
login) and embeds it in the newly minted access + refresh tokens, so a language switch applies on the
|
|
184
|
+
next refresh with no re-login (previously refresh copied the JWT payload verbatim, resetting only
|
|
185
|
+
`exp`/`iat`). User-tokens only (skips API-credential/public — no language claim); mutates `id.language`
|
|
186
|
+
after client-DB registration and before signing both tokens. Invalid code on refresh is a **soft**
|
|
187
|
+
non-fatal `WZ-4 WARNING_INVALID_LANGUAGE_SETTING` (new constant in `Response.php`) that keeps the
|
|
188
|
+
token's current language — vs. login's hard EV-16 400 — so a bad setting can't log an active user out.
|
|
189
|
+
(TRUE-80064, bala)
|
|
152
190
|
|
|
153
191
|
## Related docs
|
|
154
192
|
|
package/package.json
CHANGED