Skip to content

Commit 010dc62

Browse files
committed
Add centralized invalid session cleanup and wire Dapper to it
- expose a clearInvalidSession helper from ts-api-react that handles storage, cookies, and stale flags with proper error reporting - update Dapper instantiations (root app, singleton, client loaders) to rely on the shared cleanup hook instead of duplicating logic
1 parent 41c60ce commit 010dc62

File tree

6 files changed

+90
-21
lines changed

6 files changed

+90
-21
lines changed

apps/cyberstorm-remix/app/root.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
} from "@thunderstore/ts-api-react/src/SessionContext";
4747
import {
4848
getPublicEnvVariables,
49+
getSessionTools,
4950
type publicEnvVariablesType,
5051
} from "cyberstorm/security/publicEnvVariables";
5152
import { StorageManager } from "@thunderstore/ts-api-react/src/storage";
@@ -580,12 +581,19 @@ const TooltipProvider = memo(function TooltipProvider({
580581

581582
function App() {
582583
const data = useLoaderData<RootLoadersType>();
583-
const dapper = new DapperTs(() => {
584-
return {
585-
apiHost: data?.publicEnvVariables.VITE_API_URL,
586-
sessionId: data?.config.sessionId,
587-
};
588-
});
584+
const sessionTools = getSessionTools();
585+
const dapper = new DapperTs(
586+
() => {
587+
return {
588+
apiHost: data?.publicEnvVariables.VITE_API_URL,
589+
sessionId: data?.config.sessionId,
590+
};
591+
},
592+
() =>
593+
sessionTools.clearInvalidSession(
594+
data?.publicEnvVariables.VITE_COOKIE_DOMAIN
595+
)
596+
);
589597

590598
return (
591599
<Outlet

apps/cyberstorm-remix/cyberstorm/utils/dapperClientLoaders.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,12 @@ export function makeTeamSettingsTabLoader<T>(
4545

4646
const setupDapper = () => {
4747
const tools = getSessionTools();
48-
const config = tools?.getConfig();
49-
return new DapperTs(() => ({
50-
apiHost: config?.apiHost,
51-
sessionId: config?.sessionId,
52-
}));
48+
const config = tools.getConfig();
49+
return new DapperTs(
50+
() => ({
51+
apiHost: config.apiHost,
52+
sessionId: config.sessionId,
53+
}),
54+
() => tools.clearInvalidSession()
55+
);
5356
};

apps/cyberstorm-remix/cyberstorm/utils/dapperSingleton.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ export function initializeClientDapper(factory?: ConfigFactory) {
3636

3737
if (!window.Dapper) {
3838
const resolvedFactory = resolveConfigFactory();
39-
window.Dapper = new DapperTs(resolvedFactory);
39+
const tools = getSessionTools();
40+
window.Dapper = new DapperTs(resolvedFactory, () =>
41+
tools.clearInvalidSession()
42+
);
4043
}
4144
}
4245

packages/dapper-ts/src/methods/currentUser.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,31 @@ import {
66

77
import { DapperTsInterface } from "../index";
88

9+
function isInvalidTokenError(error: ApiError): boolean {
10+
const detail = extractErrorDetail(error.responseJson);
11+
return (
12+
typeof detail === "string" && detail.toLowerCase().includes("invalid token")
13+
);
14+
}
15+
16+
function extractErrorDetail(payload: unknown): string | undefined {
17+
if (!payload) {
18+
return undefined;
19+
}
20+
if (typeof payload === "string") {
21+
return payload;
22+
}
23+
if (
24+
typeof payload === "object" &&
25+
payload !== null &&
26+
"detail" in payload &&
27+
typeof (payload as { detail?: unknown }).detail === "string"
28+
) {
29+
return (payload as { detail: string }).detail;
30+
}
31+
return undefined;
32+
}
33+
934
export async function getCurrentUser(this: DapperTsInterface) {
1035
try {
1136
const data = await fetchCurrentUser({
@@ -17,13 +42,13 @@ export async function getCurrentUser(this: DapperTsInterface) {
1742
return data;
1843
} catch (error) {
1944
if (error instanceof ApiError && error.response.status === 401) {
20-
// If the user is not authenticated, we remove the session hook
21-
this.removeSessionHook?.();
45+
if (isInvalidTokenError(error)) {
46+
// If the token is invalid, clear any persisted session data
47+
this.removeSessionHook?.();
48+
}
2249
return null;
23-
} else {
24-
// If it's another error, we throw it
25-
throw error;
2650
}
51+
throw error;
2752
}
2853
}
2954

packages/ts-api-react/src/SessionContext.tsx

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export interface ContextInterface {
1717
clearSession: (clearApiHost?: boolean) => void;
1818
/** Remove session cookies. */
1919
clearCookies: (domain: string) => void;
20+
/** Clear all persisted session data and flag as stale. */
21+
clearInvalidSession: (cookieDomainOverride?: string) => void;
2022
/** Set SessionData in storage */
2123
setSession: (sessionData: SessionData) => void;
2224
/** Set session stale state */
@@ -99,6 +101,28 @@ export const clearCookies = (domain: string) => {
99101
deleteCookie("sessionid", domain);
100102
};
101103

104+
export const clearInvalidSession = (
105+
_storage: StorageManager,
106+
cookieDomainOverride?: string
107+
) => {
108+
if (typeof window === "undefined") {
109+
return;
110+
}
111+
try {
112+
clearSession(_storage, true);
113+
const cookieDomain =
114+
cookieDomainOverride ||
115+
_storage.safeGetValue(COOKIE_DOMAIN_KEY) ||
116+
undefined;
117+
if (cookieDomain) {
118+
clearCookies(cookieDomain);
119+
}
120+
setSessionStale(_storage, true);
121+
} catch (error) {
122+
console.error("Failed to clear invalid session", error);
123+
}
124+
};
125+
102126
export const getConfig = (
103127
_storage: StorageManager,
104128
domain?: string
@@ -178,10 +202,7 @@ export const updateCurrentUser = async (
178202
customClearSession
179203
? customClearSession
180204
: () => {
181-
// This function gets called when the dapper getCurrentUser gets 401 as a response
182-
clearSession(_storage, false);
183-
// We want to clear the sessionid cookie if it's invalid.
184-
clearCookies(_storage.safeGetValue(COOKIE_DOMAIN_KEY) || "");
205+
clearInvalidSession(_storage);
185206
}
186207
);
187208
const currentUser = await dapper.getCurrentUser();
@@ -247,6 +268,13 @@ export const getSessionContext = (
247268
clearCookies(domain);
248269
};
249270

271+
const _clearInvalidSession = (cookieDomainOverride?: string) => {
272+
clearInvalidSession(
273+
_storage,
274+
cookieDomainOverride || cookieDomain || undefined
275+
);
276+
};
277+
250278
const _getConfig = (domain?: string): RequestConfig => {
251279
return getConfig(_storage, domain);
252280
};
@@ -289,6 +317,7 @@ export const getSessionContext = (
289317
return {
290318
clearSession: _clearSession,
291319
clearCookies: _clearCookies,
320+
clearInvalidSession: _clearInvalidSession,
292321
getConfig: _getConfig,
293322
runSessionValidationCheck: _runSessionValidationCheck,
294323
updateCurrentUser: _updateCurrentUser,

packages/ts-api-react/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export {
33
CURRENT_USER_KEY,
44
setSession,
55
clearSession,
6+
clearInvalidSession,
67
getConfig,
78
runSessionValidationCheck,
89
storeCurrentUser,

0 commit comments

Comments
 (0)