Hi,
I’m developing a chat plugin and encountering a persistent issue where the session_id (obtained via Session::getId()) changes between two sequential AJAX POST requests from the client to my plugin’s API routes. This causes a consent token, tied to the initial session_id, to become unresolvable.
Problem Overview:
-
Consent: User gives consent via the chat.
- An AJAX POST hits
[code]/api/myvendor/myplugin/consent[/code]. - In
ChatApiController::handleConsentLog(), a uniqueX-Consent-Tokenis generated and stored in themyvendor_myplugin_consent_tokenstable alongsideSession::getId()(let’s call thissession_id_A). The token is sent to the client and stored inlocalStorage.
- An AJAX POST hits
-
Send Message (Proxy): User sends a chat message.
- An AJAX POST hits
[code]/api/myvendor/myplugin/proxy[/code]with theX-Consent-TokenandX-CSRF-TOKENheaders. - In
ChatApiController::handleProxy(), the token is validated by checking for a matchingtokenAND the currentSession::getId()(let’s call thissession_id_B). -
Issue:
session_id_Aandsession_id_Bare different, so the token stored withsession_id_Ais not found when querying withsession_id_B.
- An AJAX POST hits
Log Snippet (from handleProxy illustrating the issue):
[2025-06-02 22:21:05] local.DEBUG: [MyPluginAPI] Proxy: Current Server Session ID (start of request): 'EFLpZ...Bc' [this is session_id_B]
[2025-06-02 22:21:05] local.DEBUG: [MyPluginAPI] Proxy: Searching for token in DB: session_id = 'EFLpZ...Bc', token = '4a68...'
[2025-06-02 22:21:05] local.DEBUG: [MyPluginAPI] Proxy: Token NOT FOUND in DB for combination session_id 'EFLpZ...Bc' and token '4a68...'.
[2025-06-02 22:21:05] local.DEBUG: [MyPluginAPI] Proxy (Debug): Tokens in DB with current session_id 'EFLpZ...Bc': []
[2025-06-02 22:21:05] local.DEBUG: [MyPluginAPI] Proxy (Debug): Sessions in DB with received token '4a68...': [{"id":24,"session_id":"mbXhF...Mo", ...}] [this is session_id_A, with which the token was stored]
(Note: Debug logs confirm the session ID is stable within a single request.)
Key Configuration:
- Effective session settings (from
.envandconfig/session.php):-
APP_URL:https://example.com -
SESSION_DRIVER:file -
SESSION_LIFETIME:120 -
SESSION_DOMAIN:.example.com -
SESSION_SECURE_COOKIE:true -
SESSION_COOKIE_PATH:/ -
SESSION_HTTP_ONLY:true -
SESSION_SAME_SITE:lax -
SESSION_COOKIE_NAME:myapp_session(example name)
-
Relevant Code Snippets:
-
plugins/myvendor/myplugin/routes.php:use MyVendor\MyPlugin\Classes\ChatApiController; // Anonymized namespace use Illuminate\Support\Facades\Route; Route::group(['prefix' => 'api/myvendor/myplugin'], function () { // Anonymized path Route::post('proxy', fn(Illuminate\Http\Request $request) => (new ChatApiController())->handleProxy($request)); Route::post('consent', fn(Illuminate\Http\Request $request) => (new ChatApiController())->handleConsentLog($request)); }); -
JavaScript
fetch(inassets/js/script.js):// Consent call const response = await fetch(CONSENT_LOG_URL, { /* ... headers ... */ credentials: 'include' }); // Proxy call const response = await fetch(PROXY_URL, { /* ... headers ... */ credentials: 'include' }); -
ChatApiController.php(simplifiedSession::getId()usage):
InhandleConsentLog():$sessionId = Session::getId(); // session_id_A // ... generate token ... Db::table('myvendor_myplugin_consent_tokens')->insert(["session_id" => $sessionId, "token" => $newToken, /* ... */]);In
handleProxy():$sessionId = Session::getId(); // session_id_B $dbTokenDetails = Db::table('myvendor_myplugin_consent_tokens') ->where('session_id', $sessionId) // Fails here ->where('token', $clientToken)->first();
Troubleshooting Done:
-
Session Configuration: Verified settings for
domain,secure,http_only,same_site(all seem correct). -
JavaScript
fetch: Addedcredentials: 'include'to all relevantfetchcalls. -
CORS: The application runs entirely under
https://example.com, so cross-origin issues seem unlikely. - CSRF Tokens: Handled correctly.
-
Middleware: Plugin API routes should be using the
webmiddleware group by default (includesStartSession).
My Questions:
- Despite the above, what could cause
Session::getId()to return different values for sequential AJAX POSTs from the same client to the same October CMS instance? - Are there any specific October CMS or Laravel considerations for session handling in plugin API routes that might be overlooked?
- How can I reliably stabilize the
session_idfor these API interactions? - Any further diagnostic steps or potential fixes you can suggest?
Any insights or suggestions would be greatly appreciated as I want to ensure this chat functionality is secure and reliable.