Skip to content

Commit a1791e5

Browse files
committed
fix(session): avoid touching sessions after logout
1 parent 6b69493 commit a1791e5

2 files changed

Lines changed: 103 additions & 0 deletions

File tree

server/src/middlewares/last-seen.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ const AUTH_PATH_PREFIXES = [
4343
'/admin/',
4444
];
4545

46+
const SELF_TERMINATING_PATHS = new Set([
47+
'/api/magic-sessionmanager/logout',
48+
'/api/magic-sessionmanager/logout-all',
49+
]);
50+
4651
/**
4752
* @param {string} path
4853
* @returns {boolean}
@@ -52,6 +57,10 @@ function isAuthEndpoint(path) {
5257
return AUTH_PATH_PREFIXES.some((prefix) => path.startsWith(prefix));
5358
}
5459

60+
function isSelfTerminatingEndpoint(path) {
61+
return SELF_TERMINATING_PATHS.has(path);
62+
}
63+
5564
/**
5665
* Creates the last-seen Koa middleware.
5766
*
@@ -115,6 +124,10 @@ module.exports = ({ strapi, sessionService }) => {
115124

116125
await next();
117126

127+
if (isSelfTerminatingEndpoint(ctx.path)) {
128+
return;
129+
}
130+
118131
try {
119132
await sessionService.touch({
120133
userId: userDocId,

tests/last-seen.test.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
'use strict';
2+
3+
const assert = require('node:assert/strict');
4+
const test = require('node:test');
5+
6+
const createLastSeenMiddleware = require('../server/src/middlewares/last-seen');
7+
const { invalidateSettingsCache } = require('../server/src/utils/settings-loader');
8+
9+
const token = 'a'.repeat(80);
10+
11+
function createStrapi(session) {
12+
return {
13+
config: {
14+
get() {
15+
return {};
16+
},
17+
},
18+
store: () => ({
19+
get: async () => null,
20+
}),
21+
documents: () => ({
22+
findFirst: async () => session,
23+
}),
24+
log: {
25+
debug() {},
26+
info() {},
27+
warn() {},
28+
},
29+
};
30+
}
31+
32+
function createCtx(path) {
33+
return {
34+
path,
35+
state: { user: { documentId: 'user-doc-id' } },
36+
request: {
37+
headers: { authorization: `Bearer ${token}` },
38+
},
39+
unauthorized(message) {
40+
this.status = 401;
41+
this.body = { error: { message } };
42+
},
43+
};
44+
}
45+
46+
test('last-seen does not touch a session after self-terminating logout routes', async () => {
47+
for (const path of ['/api/magic-sessionmanager/logout', '/api/magic-sessionmanager/logout-all']) {
48+
invalidateSettingsCache();
49+
50+
let touchCalls = 0;
51+
const middleware = createLastSeenMiddleware({
52+
strapi: createStrapi({ documentId: 'session-doc-id', isActive: true }),
53+
sessionService: {
54+
touch: async () => {
55+
touchCalls++;
56+
},
57+
},
58+
});
59+
60+
const ctx = createCtx(path);
61+
await middleware(ctx, async () => {
62+
ctx.body = { terminated: true };
63+
});
64+
65+
assert.equal(touchCalls, 0, path);
66+
}
67+
});
68+
69+
test('last-seen still touches sessions after ordinary authenticated requests', async () => {
70+
invalidateSettingsCache();
71+
72+
const touched = [];
73+
const middleware = createLastSeenMiddleware({
74+
strapi: createStrapi({ documentId: 'session-doc-id', isActive: true }),
75+
sessionService: {
76+
touch: async (payload) => {
77+
touched.push(payload);
78+
},
79+
},
80+
});
81+
82+
const ctx = createCtx('/api/articles');
83+
await middleware(ctx, async () => {
84+
ctx.body = { ok: true };
85+
});
86+
87+
assert.deepEqual(touched, [
88+
{ userId: 'user-doc-id', sessionId: 'session-doc-id' },
89+
]);
90+
});

0 commit comments

Comments
 (0)