From 273693e8ad47b2aafb78b3675de617a1ae2b99ad Mon Sep 17 00:00:00 2001 From: zsh Date: Fri, 25 Sep 2026 17:38:37 +1200 Subject: [PATCH] Expire abandoned OIDC mobile login sessions --- server/auth/OidcAuthStrategy.js | 6 +++++- test/server/auth/OidcAuthStrategy.test.js | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/server/auth/OidcAuthStrategy.js b/server/auth/OidcAuthStrategy.js index 0997b7cf3..9cb78b257 100644 --- a/server/auth/OidcAuthStrategy.js +++ b/server/auth/OidcAuthStrategy.js @@ -1,3 +1,4 @@ +const { LRUCache } = require('lru-cache') const { Request, Response } = require('express') const passport = require('passport') const OpenIDClient = require('openid-client') @@ -15,7 +16,10 @@ class OidcAuthStrategy { this.strategy = null this.client = null // Map of openId sessions indexed by oauth2 state-variable - this.openIdAuthSession = new Map() + this.openIdAuthSession = new LRUCache({ + ttl: 10 * 60 * 1000, + ttlAutopurge: true + }) } /** diff --git a/test/server/auth/OidcAuthStrategy.test.js b/test/server/auth/OidcAuthStrategy.test.js index 6a84edac0..9d90e1423 100644 --- a/test/server/auth/OidcAuthStrategy.test.js +++ b/test/server/auth/OidcAuthStrategy.test.js @@ -75,3 +75,26 @@ describe('OidcAuthStrategy - isValidWebCallbackUrl', () => { expect(strategy.isValidWebCallbackUrl('http://books.example.com/login', req)).to.equal(false) }) }) + +describe('OidcAuthStrategy - openIdAuthSession', () => { + /** @type {OidcAuthStrategy} */ + let strategy + + beforeEach(() => { + global.RouterBasePath = '' + strategy = new OidcAuthStrategy() + }) + + it('configures a 10 minute TTL with autopurge so abandoned mobile logins are cleaned up', () => { + expect(strategy.openIdAuthSession.ttl).to.equal(10 * 60 * 1000) + expect(strategy.openIdAuthSession.ttlAutopurge).to.equal(true) + }) + + it('still supports the same set/has/get/delete usage as a plain Map', () => { + strategy.openIdAuthSession.set('some-state', { mobile_redirect_uri: 'myapp://callback' }) + expect(strategy.openIdAuthSession.has('some-state')).to.equal(true) + expect(strategy.openIdAuthSession.get('some-state')).to.deep.equal({ mobile_redirect_uri: 'myapp://callback' }) + strategy.openIdAuthSession.delete('some-state') + expect(strategy.openIdAuthSession.has('some-state')).to.equal(false) + }) +})