fix(auth): treat expired JWTs as no identity, improve logout and token verification flow, and bump deps
This commit is contained in:
15
.playwright-mcp/console-2026-03-03T21-47-08-122Z.log
Normal file
15
.playwright-mcp/console-2026-03-03T21-47-08-122Z.log
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[ 916ms] [ERROR] method: >>getCombinedMetrics<< got an ERROR: "Valid identity required" with data {} @ http://localhost:3000/bundle.js:15
|
||||||
|
[ 972ms] [ERROR] method: >>getConfiguration<< got an ERROR: "Valid identity required" with data {} @ http://localhost:3000/bundle.js:15
|
||||||
|
[ 973ms] [ERROR] method: >>getRecentLogs<< got an ERROR: "Valid identity required" with data {} @ http://localhost:3000/bundle.js:15
|
||||||
|
[ 990ms] K2
|
||||||
|
[ 1024ms] [ERROR] Error while trying to use the following icon from the Manifest: http://localhost:3000/assetbroker/manifest/icon-144x144.png (Download error or resource isn't a valid image) @ http://localhost:3000/overview:0
|
||||||
|
[ 37030ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: @ http://localhost:3000/typedserver/devtools:16227
|
||||||
|
[ 37031ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/typedserver/devtools:16251
|
||||||
|
[ 37923ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedserver/devtools:16227
|
||||||
|
[ 37923ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/typedserver/devtools:16251
|
||||||
|
[ 39699ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedserver/devtools:16227
|
||||||
|
[ 39699ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/typedserver/devtools:16251
|
||||||
|
[ 44287ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedserver/devtools:16227
|
||||||
|
[ 44288ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/typedserver/devtools:16251
|
||||||
|
[ 53685ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedserver/devtools:16227
|
||||||
|
[ 53685ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/typedserver/devtools:16251
|
||||||
90
.playwright-mcp/console-2026-03-03T22-02-52-436Z.log
Normal file
90
.playwright-mcp/console-2026-03-03T22-02-52-436Z.log
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
[ 1146ms] [ERROR] Error while trying to use the following icon from the Manifest: http://localhost:3000/assetbroker/manifest/icon-144x144.png (Download error or resource isn't a valid image) @ http://localhost:3000/overview:0
|
||||||
|
[ 26151ms] [WARNING] FontAwesome icon not found: circle-check @ http://localhost:3000/bundle.js:1203
|
||||||
|
[ 257684ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: @ http://localhost:3000/bundle.js:38066
|
||||||
|
[ 257684ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: @ http://localhost:3000/typedserver/devtools:16227
|
||||||
|
[ 257684ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/bundle.js:38066
|
||||||
|
[ 257685ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/typedserver/devtools:16251
|
||||||
|
[ 258151ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 258500ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedserver/devtools:16227
|
||||||
|
[ 258500ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/typedserver/devtools:16251
|
||||||
|
[ 258568ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/bundle.js:38066
|
||||||
|
[ 258568ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/bundle.js:38066
|
||||||
|
[ 259149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 260149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 260245ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/bundle.js:38066
|
||||||
|
[ 260245ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/bundle.js:38066
|
||||||
|
[ 260324ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedserver/devtools:16227
|
||||||
|
[ 260324ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/typedserver/devtools:16251
|
||||||
|
[ 261149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 262149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 263149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 263917ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedserver/devtools:16227
|
||||||
|
[ 263917ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/typedserver/devtools:16251
|
||||||
|
[ 264149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 264781ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/bundle.js:38066
|
||||||
|
[ 264781ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/bundle.js:38066
|
||||||
|
[ 265169ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 266149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 267149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 268149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 269149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 270149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 271149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 272149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 272565ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedserver/devtools:16227
|
||||||
|
[ 272565ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/typedserver/devtools:16251
|
||||||
|
[ 273149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 273647ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/bundle.js:38066
|
||||||
|
[ 273647ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/bundle.js:38066
|
||||||
|
[ 274149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 275149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 276149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 277149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 278149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 279149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 280149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 281149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 282149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 283149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 284149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 285149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 286149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 287149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 288150ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 289149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 290149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 290179ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/bundle.js:38066
|
||||||
|
[ 290179ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/bundle.js:38066
|
||||||
|
[ 291147ms] [ERROR] WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedserver/devtools:16227
|
||||||
|
[ 291147ms] [ERROR] TypedSocket WebSocket error: Event @ http://localhost:3000/typedserver/devtools:16251
|
||||||
|
[ 291149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 292149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 293149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 294149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 295149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 296149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 297149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 298149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 299149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 300149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 301149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 302149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 303149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 304149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 305149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 306149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 307149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 308149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 309149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 310149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 311149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 312150ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 313149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 314149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 315149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 316149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 317149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 318150ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 319149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 320149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
|
[ 321149ms] [ERROR] Failed to load resource: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/typedrequest:0
|
||||||
BIN
.playwright-mcp/page-2026-03-03T21-48-01-361Z.png
Normal file
BIN
.playwright-mcp/page-2026-03-03T21-48-01-361Z.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
.playwright-mcp/page-2026-03-03T22-07-21-001Z.png
Normal file
BIN
.playwright-mcp/page-2026-03-03T22-07-21-001Z.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
10
changelog.md
10
changelog.md
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-03-04 - 11.0.1 - fix(auth)
|
||||||
|
treat expired JWTs as no identity, improve logout and token verification flow, and bump deps
|
||||||
|
|
||||||
|
- App: getActionContext now treats expired JWTs as null to avoid using stale identities for requests.
|
||||||
|
- Logout action always clears local login state; server-side adminLogout is attempted only when a valid identity exists.
|
||||||
|
- Dashboard: verify persisted JWT with server (verifyIdentity) on startup; if verification fails, clear state and show login.
|
||||||
|
- Auto-refresh: on combined refresh failure, detect auth-related errors (invalid/unauthorized/401), dispatch logout and reload to force re-login.
|
||||||
|
- Deps: bumped devDependencies @git.zone/tstest (^3.2.0) and @git.zone/tswatch (^3.2.5); added runtime dependency @push.rocks/lik (^6.2.2).
|
||||||
|
- Tests/artifacts: added Playwright console logs and page screenshots (test artifacts) to the commit.
|
||||||
|
|
||||||
## 2026-03-03 - 11.0.0 - BREAKING CHANGE(opsserver)
|
## 2026-03-03 - 11.0.0 - BREAKING CHANGE(opsserver)
|
||||||
Require authentication for OpsServer endpoints, split handlers into authenticated view/admin routers, and make identity required on many TypedRequest interfaces
|
Require authentication for OpsServer endpoints, split handlers into authenticated view/admin routers, and make identity required on many TypedRequest interfaces
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@
|
|||||||
"@git.zone/tsbuild": "^4.1.2",
|
"@git.zone/tsbuild": "^4.1.2",
|
||||||
"@git.zone/tsbundle": "^2.9.0",
|
"@git.zone/tsbundle": "^2.9.0",
|
||||||
"@git.zone/tsrun": "^2.0.1",
|
"@git.zone/tsrun": "^2.0.1",
|
||||||
"@git.zone/tstest": "^3.1.8",
|
"@git.zone/tstest": "^3.2.0",
|
||||||
"@git.zone/tswatch": "^3.2.0",
|
"@git.zone/tswatch": "^3.2.5",
|
||||||
"@types/node": "^25.3.3"
|
"@types/node": "^25.3.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
"@apiclient.xyz/cloudflare": "^7.1.0",
|
"@apiclient.xyz/cloudflare": "^7.1.0",
|
||||||
"@design.estate/dees-catalog": "^3.43.3",
|
"@design.estate/dees-catalog": "^3.43.3",
|
||||||
"@design.estate/dees-element": "^2.1.6",
|
"@design.estate/dees-element": "^2.1.6",
|
||||||
|
"@push.rocks/lik": "^6.2.2",
|
||||||
"@push.rocks/projectinfo": "^5.0.2",
|
"@push.rocks/projectinfo": "^5.0.2",
|
||||||
"@push.rocks/qenv": "^6.1.3",
|
"@push.rocks/qenv": "^6.1.3",
|
||||||
"@push.rocks/smartacme": "^9.1.3",
|
"@push.rocks/smartacme": "^9.1.3",
|
||||||
|
|||||||
2622
pnpm-lock.yaml
generated
2622
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/dcrouter',
|
name: '@serve.zone/dcrouter',
|
||||||
version: '11.0.0',
|
version: '11.0.1',
|
||||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/dcrouter',
|
name: '@serve.zone/dcrouter',
|
||||||
version: '11.0.0',
|
version: '11.0.1',
|
||||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,9 +238,12 @@ interface IActionContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getActionContext = (): IActionContext => {
|
const getActionContext = (): IActionContext => {
|
||||||
return {
|
const identity = loginStatePart.getState().identity;
|
||||||
identity: loginStatePart.getState().identity,
|
// Treat expired JWTs as no identity — prevents stale persisted sessions from firing requests
|
||||||
};
|
if (identity && identity.expiresAt && identity.expiresAt < Date.now()) {
|
||||||
|
return { identity: null };
|
||||||
|
}
|
||||||
|
return { identity };
|
||||||
};
|
};
|
||||||
|
|
||||||
// Login Action
|
// Login Action
|
||||||
@@ -271,24 +274,23 @@ export const loginAction = loginStatePart.createAction<{
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Logout Action
|
// Logout Action — always clears state, even if identity is expired/missing
|
||||||
export const logoutAction = loginStatePart.createAction(async (statePartArg) => {
|
export const logoutAction = loginStatePart.createAction(async (statePartArg) => {
|
||||||
const context = getActionContext();
|
const context = getActionContext();
|
||||||
if (!context.identity) return statePartArg.getState();
|
|
||||||
|
|
||||||
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
// Try to notify server, but don't block logout if identity is missing/expired
|
||||||
interfaces.requests.IReq_AdminLogout
|
if (context.identity) {
|
||||||
>('/typedrequest', 'adminLogout');
|
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||||
|
interfaces.requests.IReq_AdminLogout
|
||||||
try {
|
>('/typedrequest', 'adminLogout');
|
||||||
await typedRequest.fire({
|
try {
|
||||||
identity: context.identity,
|
await typedRequest.fire({ identity: context.identity });
|
||||||
});
|
} catch (error) {
|
||||||
} catch (error) {
|
console.error('Logout error:', error);
|
||||||
console.error('Logout error:', error);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear login state regardless
|
// Always clear login state
|
||||||
return {
|
return {
|
||||||
identity: null,
|
identity: null,
|
||||||
isLoggedIn: false,
|
isLoggedIn: false,
|
||||||
@@ -1338,6 +1340,12 @@ async function dispatchCombinedRefreshAction() {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Combined refresh failed:', error);
|
console.error('Combined refresh failed:', error);
|
||||||
|
// If the error looks like an auth failure (invalid JWT), force re-login
|
||||||
|
const errMsg = String(error);
|
||||||
|
if (errMsg.includes('invalid') || errMsg.includes('unauthorized') || errMsg.includes('401')) {
|
||||||
|
await loginStatePart.dispatchAction(logoutAction, null);
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as plugins from '../plugins.js';
|
import * as plugins from '../plugins.js';
|
||||||
import * as appstate from '../appstate.js';
|
import * as appstate from '../appstate.js';
|
||||||
|
import * as interfaces from '../../dist_ts_interfaces/index.js';
|
||||||
import { appRouter } from '../router.js';
|
import { appRouter } from '../router.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -218,13 +219,27 @@ export class OpsDashboard extends DeesElement {
|
|||||||
// Handle initial state - check if we have a stored session that's still valid
|
// Handle initial state - check if we have a stored session that's still valid
|
||||||
const loginState = appstate.loginStatePart.getState();
|
const loginState = appstate.loginStatePart.getState();
|
||||||
if (loginState.identity?.jwt) {
|
if (loginState.identity?.jwt) {
|
||||||
// Verify JWT hasn't expired
|
|
||||||
if (loginState.identity.expiresAt > Date.now()) {
|
if (loginState.identity.expiresAt > Date.now()) {
|
||||||
// JWT still valid, restore logged-in state
|
// Client-side expiry looks valid — verify with server (keypair may have changed)
|
||||||
this.loginState = loginState;
|
try {
|
||||||
await simpleLogin.switchToSlottedContent();
|
const verifyRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||||
await appstate.statsStatePart.dispatchAction(appstate.fetchAllStatsAction, null);
|
interfaces.requests.IReq_VerifyIdentity
|
||||||
await appstate.configStatePart.dispatchAction(appstate.fetchConfigurationAction, null);
|
>('/typedrequest', 'verifyIdentity');
|
||||||
|
const response = await verifyRequest.fire({ identity: loginState.identity });
|
||||||
|
if (response.valid) {
|
||||||
|
// JWT confirmed valid by server
|
||||||
|
this.loginState = loginState;
|
||||||
|
await simpleLogin.switchToSlottedContent();
|
||||||
|
await appstate.statsStatePart.dispatchAction(appstate.fetchAllStatsAction, null);
|
||||||
|
await appstate.configStatePart.dispatchAction(appstate.fetchConfigurationAction, null);
|
||||||
|
} else {
|
||||||
|
// Server rejected the JWT — clear state, show login
|
||||||
|
await appstate.loginStatePart.dispatchAction(appstate.logoutAction, null);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Server unreachable or error — clear state, show login
|
||||||
|
await appstate.loginStatePart.dispatchAction(appstate.logoutAction, null);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// JWT expired, clear the stored state
|
// JWT expired, clear the stored state
|
||||||
await appstate.loginStatePart.dispatchAction(appstate.logoutAction, null);
|
await appstate.loginStatePart.dispatchAction(appstate.logoutAction, null);
|
||||||
|
|||||||
Reference in New Issue
Block a user