Platform Identity Current Consumer Refresh — 2026-08-10¶
- Status:
CURRENT SOURCE RECONCILIATION / DOCUMENTATION ONLY - Task:
CTC-CDOC-004 - Source baseline actually reviewed:
qa/master@8981a02f7b2a2fa256a7365d6921f3d7636f1fe2 - Original packet baseline:
qa/master@3c084671c034f8f5136b02dce990ecff6c66d107 - Prior refresh:
PLATFORM_IDENTITY_CURRENT_CONSUMER_REFRESH_2026-08-09.md - Source authority:
PLATFORM_IDENTITY_SESSION_IMPERSONATION_SOURCE_AUTHORITY.md - Target-only packet:
PLATFORM_IDENTITY_SESSION_IMPERSONATION_REBUILD_PACKET.md - Runtime/environment effects:
NONE - Phase impact:
NONE
1. Scope And Precedence¶
This document refreshes current Identity/Session/Impersonation consumers only. It does not activate the target rebuild packet, change authentication/session policy, authorize credential access, or prove runtime behavior.
Precedence for this review is:
current source at 8981a02
> accepted current policy
> prior current-source refresh
> original rebuild/source packet
> historical candidate
The 2026-08-09 refresh remains useful history but is stale wherever current source has changed since its reviewed baseline.
2. Core Owner Freshness¶
The original identity packet used 3c084671.... Current owner blobs were compared against that baseline and current source semantics were reread where the blob changed.
| Owner | Original blob / current blob | Classification | Current-source meaning |
|---|---|---|---|
login.php |
bd73f235... / 2b47ba68... |
CHANGED, AUTH CORE SUBSTANTIALLY PRESERVED |
Username-only active-user lookup, password verification/rehash, session ID regeneration and original session fields remain. LAST_LOGIN write now uses a PHP timestamp and ID+tenant predicate rather than the prior username predicate. No source-visible MFA, lockout or privileged reauth was added. |
config/session.php |
c2fb4d25... / c2fb4d25... |
BLOB UNCHANGED |
One 20-hour cookie/session/GC lifetime; strict mode, cookies only, HttpOnly, SameSite=Lax and Secure when HTTPS is detected. No separate idle/absolute/revocation timers. |
config/config.php |
b49bdf04... / 5daf910b... |
MATERIAL IDENTITY CHANGE |
Scalar effective Actor/Subject/Tenant helpers remain, and request-time active-user/role validation plus session clearing now exist. |
app/platform/audit/super_admin_post_helper.php |
prior/current 76c9bef4... |
BLOB UNCHANGED FROM RECONCILED OWNER |
Super-admin user impersonation start remains a current writer; no new reason, expiry or reauth contract is visible in this owner. |
app/platform/tenant/api_strict_principal.php |
current b613e783... |
CURRENT STRICT ADAPTER |
Strict API requests reconstruct typed tenant execution context and reject malformed tenant-only impersonation. |
app/shared/api/runtime.php |
prior refresh blob 54675... / current 6ca30568... |
MATERIAL INTEGRATION CHANGE |
apiAssertAuthenticated() now invokes active-user validation before protected routes continue. Legacy apiContext() and strict apiStrictPrincipalContext() still coexist. |
app/shared/api/router.php |
current 70fbdaef... |
CURRENT MIXED-MODE DISPATCH |
strictTenantPrincipal=true routes use the strict adapter before module/handler dispatch; other protected routes continue through scalar apiContext(). |
admin-console.php |
current bd6e5ba5... |
CURRENT CONSUMER CHANGED |
Physical super admin can select any existing tenant for console viewing; tenant admin is pinned to its session tenant. main_open creates tenant-only impersonation for super admin. impersonate_end only unsets IMPERSONATE. |
Blob change alone is not interpreted as a security improvement. The semantic differences above are the current facts relevant to this task.
3. New Session Invalidation Semantics Since The Prior Refresh¶
config/config.php now defines two materially relevant functions:
kjClearAuthenticatedSession()
kjValidateActiveSessionUser()
3.1 Original actor validation¶
On validated protected requests, current source rereads the original physical session identity from:
login_id
CLIENT_ID
login_username
login_role
It then queries the active T_USER row by ID + tenant + username and compares the active role IDs with the session role IDs.
If the original user is missing/deleted or the active role list differs, current source clears the in-memory session, expires the session cookie with the existing cookie parameters and destroys the current PHP session.
This changes the prior gap classification:
role/user drift for an active request
= request-time invalidation now present
central all-session revocation/versioning
= still not established
The mechanism is request-time validation, not a persisted revocation ledger. Another existing browser/session is not shown being proactively deleted; it is expected to fail when it next reaches a path that invokes the validator.
3.2 Impersonated target validation¶
When IMPERSONATE.TARGET_USER_ID > 0, the validator also checks that the target user still exists for the target tenant/username. If not, it removes IMPERSONATE but preserves the physical actor session.
This is a useful current fail-safe, but it does not establish target-role freshness comparison, privileged reauthentication, impersonation expiry, or a durable revocation record.
3.3 Fail-closed request behavior versus durable session destruction¶
If the validation query throws, kjValidateActiveSessionUser() returns false. Current page/API callers then deny the request or redirect to login. The catch path itself does not prove that the stored PHP session was destroyed. Therefore:
request continuation on validation error = denied
persistent session deletion on validation error = NOT ESTABLISHED
4. Current Validation Consumers¶
Current repository search shows kjValidateActiveSessionUser() used by:
index.phpfor the main PHP application shell;app/shared/api/runtime.phpfor protected API dispatch;app/shared/endpoint/response.phpfor the legacy endpoint response boundary;- tests that assert the contract.
This increases coverage compared with the original packet, but does not prove every standalone/public/provider callback or historical PHP entrypoint uses the validator. Unlisted entrypoints remain evidence-required.
5. High-Risk Consumer Inventory¶
5.1 Provider credential reveal API¶
Current route remains:
POST /api/platform/admin/tenants/{clientId}/credentials/{credentialKey}/reveal
The route is authenticated, moduleKey = null, and marks redactRequestBody = true. The handler calls platformApiTenantManagedCredentialAccess(..., 'REVEAL'), which first requires Admin Console access. A non-superadmin caller is additionally restricted to clientId === getClientId().
Current guard meaning:
- physical super admin: allowed by
isSuperAdmin(); - non-superadmin: effective role ID
1required and URL tenant must equal effective tenant; - an impersonating physical super admin is still physically super admin because
isSuperAdmin()reads the original session identity; - no current route-level check prohibits reveal while impersonation is active;
- no current route-level privileged reauthentication, MFA, reason reference or short-lived reveal grant is visible.
Secret-safe note: this task did not call the route, read a credential value, inspect ciphertext, or retrieve any provider secret.
5.2 Current Admin Console UI versus reveal API¶
Current frontend/pages/platform/admin-console/tenants.php does not contain a /reveal caller in the reviewed source. Provider secret fields render as password inputs with an empty value and only indicate that an existing secret is set. Saving requires entering a replacement secret; blank input does not reveal the stored value.
Therefore the current source distinction is:
reveal API surface = PRESENT
current reviewed Admin Console reveal UI caller = NOT FOUND
current Admin Console secret replacement UI = PRESENT
This corrects any reading of the 2026-08-09 refresh that implied the reviewed current UI necessarily exercised reveal. The API remains high risk because it exists even without a current UI caller.
5.3 Provider credential update/replacement¶
Current provider UI sends replacement values through the general tenant config save path. Separately, the managed credential update route exists under /credentials/{credentialKey} and shares Admin Console access plus tenant-boundary checks. Identity implications are the same: authorization is based on the current physical/effective PHP session, not on a separate privileged-action reauthentication state.
Credential storage/resolver semantics belong to CTC-CDOC-005 and are intentionally not redefined here.
5.4 Admin Console tenant selection¶
admin-console.php distinguishes physical super admin from tenant admin:
- super admin may choose an ID from the current
T_CLIENTlist; - tenant admin ignores
client_idselection and stays on its physical sessionCLIENT_ID; - the selected client is placed in
KJ_ADMIN_CONSOLE_CLIENT_IDfor the console projection; - selecting a tenant for console viewing is not by itself persisted impersonation.
Actor/Subject/Tenant:
super-admin console selection:
Actor = physical super admin
Subject = none
Tenant = selected console tenant for the affected console operation
Access mode = admin selection/override, not user impersonation
5.5 Tenant-only impersonation¶
admin-console.php?action=main_open creates an IMPERSONATE object for physical super admin with TARGET_USER_ID=0, target tenant ID, display username/name and start timestamp.
Current strict adapter explicitly recognizes tenant-only impersonation only when target user ID is exactly zero and target username is non-empty.
Actor = physical super admin
Subject = none
Tenant = target tenant
Access mode = IMPERSONATION
This is support context, not target-role acceptance evidence.
5.6 User impersonation¶
The super-admin writer remains in super_admin_post_helper.php.
Actor = physical super admin
Subject = target user
Tenant = target user's tenant
Access mode = IMPERSONATION
Current scalar helpers project target user/tenant values to legacy callers, while physical-super-admin checks continue to see the original actor. This duality is intentional compatibility but remains high risk for any mutation that does not explicitly distinguish Actor from Subject.
5.7 API admin override¶
Legacy apiContext() permits X_ADMIN_CLIENT_ID only for a bounded path allowlist and only when isSuperAdmin() is true. It changes the request's effective clientId without writing IMPERSONATE.
Actor = physical super admin
Subject = none unless another caller-specific projection says otherwise
Tenant = validated header target tenant
Access mode = API ADMIN OVERRIDE
It must not be described as impersonation or target-role evidence.
5.8 Strict API tenant principal¶
Routes opting into strictTenantPrincipal=true clear the preexisting scalar telemetry context, authenticate, build a typed tenant execution context, and only then perform strict module checks.
tenantApiStrictPrincipalContext() resolves:
- direct session: Actor and Subject are the direct user;
- user impersonation: Actor is original super admin, Subject is target user;
- tenant-only impersonation: Actor is original super admin, Subject is null, Tenant is target tenant;
- malformed tenant-only principal: rejected.
The strict adapter is a current compatibility bridge, not evidence that all APIs are strict. Non-opted routes continue through scalar apiContext().
5.9 Legacy scalar PHP session paths¶
The scalar compatibility model remains:
getClientId()returns impersonated target tenant first, then session tenant;getSessionUserId()returns target user when present, otherwise original login ID;getSessionUsername()returns target username when present, otherwise original username;- physical-super-admin checks use original session state.
Consequently a legacy caller using only effective scalar identity can lose Actor/Subject distinction during impersonation. Such a caller must be classified as SCALAR COMPATIBILITY / PROVENANCE INCOMPLETE unless it separately records the physical actor.
6. Actor / Subject / Tenant Reconciliation¶
| Consumer | Actor | Subject | Tenant | Current certainty |
|---|---|---|---|---|
| Direct PHP/API user | logged-in user | same user | session tenant | RESOLVED |
| Super-admin Admin Console tenant selection | physical super admin | none | selected tenant | RESOLVED |
| User impersonation | physical super admin | target user | target tenant | RESOLVED |
| Tenant-only impersonation | physical super admin | none | target tenant | RESOLVED |
| Legacy scalar caller during user impersonation | physical actor exists but may not be passed | effective target user | target tenant | PROVENANCE GAP AT CALLER |
| Legacy scalar caller during tenant-only impersonation | physical actor exists but may not be passed | none semantically; scalar username is tenant display label | target tenant | PROVENANCE GAP AT CALLER |
| API admin override | physical super admin | none | header-selected tenant | RESOLVED AT API BOUNDARY |
| Strict API direct | typed direct actor | same typed subject | strict local tenant | RESOLVED |
| Strict API user impersonation | typed physical actor | typed target subject | strict target tenant | RESOLVED |
| Strict API tenant-only impersonation | typed physical actor | null | strict target tenant | RESOLVED |
| Credential reveal/update | physical actor is inferable from session; handler context is not a full typed provenance envelope | effective subject depends on impersonation | URL/effective tenant guard | AUTHZ RESOLVED; DURABLE PROVENANCE/REAUTH UNKNOWN |
7. Logout, Timeout, Revocation And Reauthentication Refresh¶
Logout¶
Current index.php?page=logout is still a GET action. It unsets selected session fields and calls session_destroy(). It does not call the newer kjClearAuthenticatedSession() helper, so explicit cookie expiration is still not established for the ordinary logout path.
Session timeout¶
config/session.php remains one 20-hour lifetime for cookie and PHP GC. Source still does not distinguish:
idle timeout
absolute session lifetime
impersonation timeout
privileged reauthentication lifetime
revocation grace period
Revocation/invalidation¶
Current state is now mixed:
- deleted/missing physical user: request-time invalidation on validated paths;
- physical user's role-list change: request-time invalidation on validated paths;
- deleted/missing impersonated target user: impersonation object removed on validated paths;
- password change/reset: no current session-version comparison or all-session revocation was found;
- explicit administrator revoke-all-sessions command: not found in reviewed source;
- session ID rotation at impersonation start/end: not found;
- session ID rotation after own/admin password change: not found.
Privileged-action reauthentication¶
No separate recent-password proof, MFA proof, reauth timestamp, privilege lease or per-action grant was found for:
- credential reveal;
- credential replacement;
- impersonation start/end;
- admin password reset;
- tenant-only support context.
This remains an open policy/implementation gap; this documentation task does not decide the policy.
8. Secret-Safe Credential-Reveal Implications¶
A reveal response makes secret material available to the authenticated browser/request context for at least the lifetime of that response. Even with request-body redaction and no-store/no-cache response controls, identity/session safety still depends on:
- who the physical actor is;
- whether impersonation/admin override is active;
- whether the session was freshly reauthenticated;
- whether the client/browser is trusted;
- whether response/logging/telemetry paths exclude the secret;
- whether a durable audit records the physical actor, target tenant, credential key, action and result without secret content.
Current source does not establish all of these as one complete control. Therefore a source-present reveal endpoint must not be treated as operationally approved merely because it is guarded by Admin Console access.
9. Current Unknowns And Policy Questions¶
- Should provider secret reveal remain available at all when the current Admin Console UI uses replacement-only behavior?
- If reveal remains, which actors may use it: physical super admin only, tenant role 1, or a narrower capability?
- Must reveal/update be prohibited during user or tenant-only impersonation?
- What recent-auth/MFA/step-up interval is required for reveal, password reset and impersonation?
- Should logout move to a POST/CSRF-protected command and use the centralized session clear helper?
- What are the approved idle, absolute and impersonation timeout values?
- What all-session revocation/version mechanism is required after password reset, account disable or high-risk role change?
- Which remaining legacy entrypoints bypass
kjValidateActiveSessionUser()? - Which current mutation/audit writers persist physical Actor separately from effective Subject?
These are not silently answered by the target rebuild packet.
10. Read-Only Evidence Boundary¶
The executable-but-not-run evidence plan is defined in:
../operations/platform-identity/RBR-IDENTITY-002-READONLY-PREFLIGHT.md
It is explicitly secret-safe and prohibits credential values/ciphertext, login, logout, impersonation, password changes and credential reveal/update.
11. Result¶
Core owner freshness classified: PASS (source review)
High-risk consumer inventory: PASS (source review)
Actor/Subject/Tenant reconciliation: PASS with caller-level provenance gaps marked
Credential secret retrieval: NOT_RUN
Session/account/credential mutation: NOT_RUN
Runtime/environment evidence: NOT_RUN
M3/M4/stabilization evidence: NOT_APPLICABLE / not claimed
External effects: NONE
Phase impact: NONE
---¶
Source:
docs/reconstruction/PLATFORM_IDENTITY_CURRENT_CONSUMER_REFRESH_2026-08-10.md— ctc repository · This page is an English translation of the source document. Identifiers, file paths and code symbols are unchanged.