From 941f098e0f6bf33fc064aedb9dbf424aab6c7b49 Mon Sep 17 00:00:00 2001 From: Kenneth Hough Date: Fri, 22 May 2026 12:17:08 -0500 Subject: [PATCH] fix(jwt): call dbappconnect alongside loadAppModels in resolveAuthContext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v4.4.3 unlocked the user_model constant via loadAppModels but left a second gap: the loaded model_definition has 'appId' set, so ModelObject->retrieve() auto-switches to the app DB via Api::dbswitch(true). But the app-DB credentials were never configured — Api::dbappconnect() is called only by the normal MVC pipeline (Api.php:690) and JWT bypasses that. Result: mysqli with null host/user/password → Unix socket attempt → "No such file or directory" → HTTP 500. Fix: call Api::dbappconnect($app->db_name, $app->db_username, $app->db_password) immediately after loadAppModels. The HMAC pipeline does these two calls back-to-back at Api.php:688-690 — JWT now matches. defineAppEnvironmentConstants / defineAppDataStore are deliberately NOT mirrored: they're private instance methods on Api (not statically reachable), and login is just user lookup + password_verify which doesn't need KYTE_APP_ENV / KYTE_APP_DATASTORE constants. All 12 JwtEndpointTest tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 14 ++++++++++++++ src/Core/Auth/JwtEndpoint.php | 25 ++++++++++++++++++++----- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14af973..9c8788a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## 4.4.4 + +### Bug Fix: `/jwt/login` still 500s on apps with custom `user_model` (continuation of 4.4.3) + +v4.4.3 fixed the `Undefined constant "User"` fatal by calling `Api::loadAppModels($app)` in `resolveAuthContext`. That uncovered a second gap: the app-scoped model definition has `appId` set, which causes `ModelObject->retrieve()` to auto-switch to the app DB via `Api::dbswitch(true)`. But the app-DB credentials (host/user/password) were never configured because `Api::dbappconnect()` is called only by the normal MVC pipeline (Api.php:690) — which JWT bypasses. + +Result: mysqli received null host/user/password and tried a Unix-socket connection → `No such file or directory` → HTTP 500. + +Fix: `resolveAuthContext` now also calls `Api::dbappconnect($app->db_name, $app->db_username, $app->db_password)` immediately after `loadAppModels`. The HMAC pipeline does both calls back-to-back at Api.php:688-690; JWT now matches. + +Apps without a custom `user_model` (default `KyteUser` path) are unaffected — they were never in the app-DB branch. + +No schema changes. Composer upgrade is sufficient. + ## 4.4.3 ### Bug Fix: `/jwt/login` fatals on apps with custom `user_model` diff --git a/src/Core/Auth/JwtEndpoint.php b/src/Core/Auth/JwtEndpoint.php index 3faf3db..ece6005 100644 --- a/src/Core/Auth/JwtEndpoint.php +++ b/src/Core/Auth/JwtEndpoint.php @@ -335,12 +335,27 @@ private static function resolveAuthContext(?string $appIdentifier): array } if ($app->user_model !== null && $app->username_colname !== null && $app->password_colname !== null) { - // App-specific DataModel constants ("User", "Customer", etc.) are - // registered lazily by Api::loadAppModels when an app context is - // detected in the normal MVC pipeline. JwtEndpoint dispatches - // BEFORE that pipeline runs, so the constant we want may not - // exist yet. Load explicitly to make the constant available. + // Mirror the subset of per-app setup that Api::route() does for + // HMAC. JWT dispatches BEFORE the normal MVC pipeline runs, so + // without this block the app-specific user model can't be + // resolved or queried: + // + // 1. loadAppModels — registers app DataModel constants + // like "User" so constant($app->user_model) resolves. + // Without this, PHP 8 fatals "Undefined constant". + // 2. dbappconnect — sets the app's DB credentials. + // Without this, ModelObject->retrieve on an app-scoped + // model auto-switches to the app DB (because the loaded + // model_definition has 'appId' set) and mysqli connects + // with null host/user/password → "No such file or + // directory" socket error → HTTP 500. + // + // We skip defineAppEnvironmentConstants / defineAppDataStore + // (private instance methods) because login is just user + // lookup + password_verify; those constants matter for + // controller execution, not auth. \Kyte\Core\Api::loadAppModels($app); + \Kyte\Core\Api::dbappconnect($app->db_name, $app->db_username, $app->db_password); if (!defined($app->user_model)) { // loadAppModels couldn't define it (no matching DataModel