Skip to content

Commit b089c2b

Browse files
committed
fix: make session blocking opt-in with strictSessionEnforcement
BREAKING: Session blocking is now DISABLED by default! Changes: - Add `strictSessionEnforcement` config option (default: false) - Only block requests when: 1. strictSessionEnforcement is explicitly set to true AND 2. User has inactive sessions (was explicitly logged out) - If no sessions exist at all → allow through (bug/race condition) - Log warnings instead of blocking when strictMode is off - Apply same logic to JWT verify, middleware, and policy To enable strict blocking, add to config/plugins.js: ``` 'magic-sessionmanager': { config: { strictSessionEnforcement: true, }, }, ```
1 parent c991db8 commit b089c2b

3 files changed

Lines changed: 107 additions & 26 deletions

File tree

server/src/bootstrap.js

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,10 @@ async function registerSessionAwareAuthStrategy(strapi, log) {
607607
return decoded;
608608
}
609609

610+
// Get config - strictSessionEnforcement must be explicitly enabled to block
611+
const config = strapi.config.get('plugin::magic-sessionmanager') || {};
612+
const strictMode = config.strictSessionEnforcement === true;
613+
610614
// Now check if user has active session
611615
try {
612616
// Get user documentId
@@ -622,42 +626,80 @@ async function registerSessionAwareAuthStrategy(strapi, log) {
622626
userDocId = user?.documentId;
623627

624628
if (!userDocId) {
625-
return decoded; // Can't check session, allow through
629+
// Can't determine user - allow through (fail-open)
630+
strapi.log.debug('[magic-sessionmanager] [JWT] No documentId found, allowing through');
631+
return decoded;
626632
}
627633

628634
// Check for active sessions
629-
strapi.log.debug(`[magic-sessionmanager] [JWT] Checking sessions for user: ${userDocId}`);
630-
631635
const activeSessions = await strapi.documents(SESSION_UID).findMany({
632636
filters: {
633637
user: { documentId: userDocId },
634638
isActive: true,
635639
},
636640
limit: 1,
637-
populate: { user: { fields: ['documentId'] } },
638641
});
639642

640-
strapi.log.debug(`[magic-sessionmanager] [JWT] Found ${activeSessions?.length || 0} active sessions`);
643+
// If active session exists, all good
644+
if (activeSessions && activeSessions.length > 0) {
645+
return decoded;
646+
}
641647

642-
// If NO active sessions, return null (invalid token)
643-
if (!activeSessions || activeSessions.length === 0) {
644-
// Debug: Check if ANY sessions exist for this user (including inactive)
645-
const allSessions = await strapi.documents(SESSION_UID).findMany({
646-
filters: { user: { documentId: userDocId } },
647-
limit: 5,
648-
});
648+
// No active session found - check if ANY sessions exist (including inactive)
649+
const allSessions = await strapi.documents(SESSION_UID).findMany({
650+
filters: { user: { documentId: userDocId } },
651+
limit: 5,
652+
fields: ['isActive', 'lastActive'],
653+
});
654+
655+
const totalSessions = allSessions?.length || 0;
656+
const hasInactiveSessions = allSessions?.some(s => s.isActive === false);
657+
658+
// DECISION LOGIC:
659+
// 1. If user has inactive sessions = they were explicitly logged out → BLOCK (if strict)
660+
// 2. If user has NO sessions at all = session never created (bug/race condition) → ALLOW
661+
// 3. If strictMode is off → always ALLOW but log warning
662+
663+
if (!strictMode) {
664+
// Non-strict mode: Log warning but allow through
665+
if (totalSessions === 0) {
666+
strapi.log.warn(
667+
`[magic-sessionmanager] [JWT-WARN] No session found for user ${userDocId.substring(0, 8)}... (allowing - session may not have been created)`
668+
);
669+
} else if (hasInactiveSessions) {
670+
strapi.log.warn(
671+
`[magic-sessionmanager] [JWT-WARN] User ${userDocId.substring(0, 8)}... has ${totalSessions} inactive sessions but no active ones (allowing - strictMode off)`
672+
);
673+
}
674+
return decoded; // Allow through in non-strict mode
675+
}
676+
677+
// Strict mode enabled - more careful about blocking
678+
if (totalSessions === 0) {
679+
// No sessions at all - likely a bug or race condition, allow through
680+
strapi.log.warn(
681+
`[magic-sessionmanager] [JWT-ALLOW] No sessions exist for user ${userDocId.substring(0, 8)}... (allowing - possible race condition)`
682+
);
683+
return decoded;
684+
}
685+
686+
if (hasInactiveSessions) {
687+
// User has inactive sessions = explicitly logged out → BLOCK
649688
strapi.log.info(
650-
`[magic-sessionmanager] [JWT-BLOCKED] Valid JWT but no active session (user: ${userDocId.substring(0, 8)}..., total sessions: ${allSessions?.length || 0})`
689+
`[magic-sessionmanager] [JWT-BLOCKED] User ${userDocId.substring(0, 8)}... was logged out (${totalSessions} inactive sessions)`
651690
);
652-
return null; // This will cause auth to fail
691+
return null; // Block - user was explicitly logged out
653692
}
654693

655-
// Session exists - return decoded token
694+
// Edge case: sessions exist but none are active or inactive (shouldn't happen)
695+
strapi.log.warn(
696+
`[magic-sessionmanager] [JWT-ALLOW] Unexpected session state for user ${userDocId.substring(0, 8)}... (allowing)`
697+
);
656698
return decoded;
657699

658700
} catch (err) {
659-
strapi.log.debug('[magic-sessionmanager] [AUTH] Session check error:', err.message);
660-
// On error, allow through
701+
// On ANY error, allow through (fail-open for availability)
702+
strapi.log.warn('[magic-sessionmanager] [JWT] Session check error (allowing):', err.message);
661703
return decoded;
662704
}
663705
};

server/src/middlewares/last-seen.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ module.exports = ({ strapi, sessionService }) => {
9898
}
9999

100100
if (userDocId) {
101+
// Get config - strictSessionEnforcement must be explicitly enabled to block
102+
const config = strapi.config.get('plugin::magic-sessionmanager') || {};
103+
const strictMode = config.strictSessionEnforcement === true;
104+
101105
// Check if user has ANY active sessions
102106
const activeSessions = await strapi.documents(SESSION_UID).findMany({
103107
filters: {
@@ -107,10 +111,25 @@ module.exports = ({ strapi, sessionService }) => {
107111
limit: 1,
108112
});
109113

110-
// If user has NO active sessions, reject the request
114+
// If user has NO active sessions
111115
if (!activeSessions || activeSessions.length === 0) {
112-
strapi.log.info(`[magic-sessionmanager] [BLOCKED] Request blocked - session terminated or invalid (user: ${userDocId.substring(0, 8)}...)`);
113-
return ctx.unauthorized('All sessions have been terminated. Please login again.');
116+
// Check if user has ANY sessions at all
117+
const allSessions = await strapi.documents(SESSION_UID).findMany({
118+
filters: { user: { documentId: userDocId } },
119+
limit: 1,
120+
fields: ['isActive'],
121+
});
122+
123+
const hasInactiveSessions = allSessions?.some(s => s.isActive === false);
124+
125+
if (strictMode && hasInactiveSessions) {
126+
// Strict mode + user was explicitly logged out → BLOCK
127+
strapi.log.info(`[magic-sessionmanager] [BLOCKED] Session terminated (user: ${userDocId.substring(0, 8)}...)`);
128+
return ctx.unauthorized('Session has been terminated. Please login again.');
129+
}
130+
131+
// Non-strict mode or no sessions exist → Allow but log
132+
strapi.log.debug(`[magic-sessionmanager] [WARN] No active session for user ${userDocId.substring(0, 8)}... (allowing)`);
114133
}
115134

116135
// Store documentId for later use

server/src/policies/session-required.js

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ module.exports = async (policyContext, config, { strapi }) => {
3939
return true;
4040
}
4141

42+
// Get config - strictSessionEnforcement must be explicitly enabled to block
43+
const config = strapi.config.get('plugin::magic-sessionmanager') || {};
44+
const strictMode = config.strictSessionEnforcement === true;
45+
4246
// Check if user has ANY active session
4347
const activeSessions = await strapi.documents(SESSION_UID).findMany({
4448
filters: {
@@ -48,16 +52,32 @@ module.exports = async (policyContext, config, { strapi }) => {
4852
limit: 1,
4953
});
5054

51-
// If NO active sessions, reject the request
52-
if (!activeSessions || activeSessions.length === 0) {
55+
// If active session exists, allow through
56+
if (activeSessions && activeSessions.length > 0) {
57+
return true;
58+
}
59+
60+
// No active session - check if user was explicitly logged out
61+
const allSessions = await strapi.documents(SESSION_UID).findMany({
62+
filters: { user: { documentId: userDocId } },
63+
limit: 1,
64+
fields: ['isActive'],
65+
});
66+
67+
const hasInactiveSessions = allSessions?.some(s => s.isActive === false);
68+
69+
// Only block if strict mode AND user was explicitly logged out
70+
if (strictMode && hasInactiveSessions) {
5371
strapi.log.info(
54-
`[magic-sessionmanager] [POLICY-BLOCKED] JWT valid but no active session (user: ${userDocId.substring(0, 8)}...)`
72+
`[magic-sessionmanager] [POLICY-BLOCKED] Session terminated (user: ${userDocId.substring(0, 8)}...)`
5573
);
56-
5774
throw new errors.UnauthorizedError('Session terminated. Please login again.');
5875
}
59-
60-
// Session exists - allow through
76+
77+
// Non-strict mode or no sessions exist → Allow but log
78+
strapi.log.debug(
79+
`[magic-sessionmanager] [POLICY-WARN] No active session for user ${userDocId.substring(0, 8)}... (allowing)`
80+
);
6181
return true;
6282

6383
} catch (err) {

0 commit comments

Comments
 (0)