From 9e38de6160530ab016d97602ad4cce1d02f1e4c7 Mon Sep 17 00:00:00 2001 From: medievalshell Date: Thu, 21 May 2026 01:01:06 +0200 Subject: [PATCH] feat(auth): capture remember-token from URL and persist for reconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CMS Inertia /client page now passes `&token=&token_exp=` on the iframe src so Nitro can persist the token to localStorage on first boot. `App.tsx::prepare()` reads them from `window.location.search` and calls `SetRememberLogin({ token, expiresAt })` when no remember-login is already stored. This wires up the existing reconnect flow: when the WS drops, the loop in `tryRememberLogin()` (already in this file) POSTs the saved token to `login.remember.endpoint` (defaults to `${api.url}/api/auth/remember`) and uses the returned fresh SSO ticket to reconnect. Without this step the localStorage stayed empty and the reconnect always fell through to "Session expired" after a few retries because Arcturus clears `auth_ticket` on first consume. Server side: the CMS counterpart is in medievalshell/InertiaCMS commit on djoohotel — adds the /api/auth/remember endpoint backed by `users_remember_families` (UUID family + 30-day expiry + revoked flag). --- src/App.tsx | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 6bf4d4b..fe05be2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,6 @@ import { GetAssetManager, GetAvatarRenderManager, GetCommunication, GetConfiguration, GetLocalizationManager, GetRoomEngine, GetRoomSessionManager, GetSessionDataManager, GetSoundManager, GetStage, GetTexturePool, GetTicker, HabboWebTools, LegacyExternalInterface, LoadGameUrlEvent, NitroEventType, NitroLogger, NitroVersion, PrepareRenderer } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useEffectEvent, useRef, useState } from 'react'; -import { ClearRememberLogin, GetRememberLogin, GetUIVersion, StoreRememberLoginFromPayload, persistAccessTokenFromPayload } from './api'; +import { ClearRememberLogin, GetRememberLogin, GetUIVersion, SetRememberLogin, StoreRememberLoginFromPayload, persistAccessTokenFromPayload } from './api'; import { Base } from './common'; import { LoadingView } from './components/loading/LoadingView'; import { LoginView } from './components/login/LoginView'; @@ -495,6 +495,31 @@ export const App: FC<{}> = props => let ssoTicket = window.NitroConfig['sso.ticket']; if(ssoTicket) GetConfiguration().setValue('sso.ticket', ssoTicket); + + // Cattura il remember-token passato via URL (?token=&token_exp=) + // dal CMS Inertia /client e salvalo in localStorage. Serve a + // tryRememberLogin() in reconnect: chiama POST /api/auth/remember + // col token UUID, riceve un nuovo SSO ticket fresco invece di + // riusare quello cleared da Arcturus dopo il primo consume. + try + { + const urlParams = new URLSearchParams(window.location.search); + const tokenParam = urlParams.get('token'); + const tokenExpParam = urlParams.get('token_exp'); + if(tokenParam && !GetRememberLogin()) + { + const parsedExpiry = Number(tokenExpParam || 0); + const expiresAt = (Number.isFinite(parsedExpiry) && parsedExpiry > 0) + ? parsedExpiry + : Math.floor(Date.now() / 1000) + (30 * 24 * 60 * 60); + SetRememberLogin({ token: tokenParam, expiresAt }); + } + } + catch(e) + { + console.warn('[App] failed to persist remember token from URL', e); + } + bumpProgress(10, taskLabel('loading.task.session', 'Verifica sessione')); if(!ssoTicket || ssoTicket === '')