diff --git a/uds-auth/lib/index.js b/uds-auth/lib/index.js index 203c95d9..671793d4 100644 --- a/uds-auth/lib/index.js +++ b/uds-auth/lib/index.js @@ -1694,6 +1694,31 @@ async function initServices(ctx, config) { sessionBridge: _sessionBridge, }) + // Revalidate QR-authenticated sessions against the UDS department profile. + // The session store still enforces the independent seven-day absolute limit. + const udsSessionCheckTimer = setInterval(async () => { + let sessions + try { + sessions = await _sessionStore.listUdsSessionsForValidation() + } catch (err) { + ctx.logger?.warn?.('[uds-auth] failed to enumerate UDS sessions for validation:', err?.message || err) + return + } + for (const session of sessions) { + try { + if (!session.token || !await _authMiddleware.verifyEmpNoAndToken(session.empNo, session.token)) { + await _sessionStore.revokeSession(session.sessionId) + ctx.logger?.info?.('[uds-auth] UDS session revoked after profile validation failed for %s', session.empNo) + } + } catch (err) { + await _sessionStore.revokeSession(session.sessionId).catch(() => {}) + ctx.logger?.info?.('[uds-auth] UDS session revoked after profile validation error for %s: %s', session.empNo, err?.message || err) + } + } + }, 6 * 60 * 60 * 1000) + udsSessionCheckTimer.unref?.() + ctx.effect?.(() => () => clearInterval(udsSessionCheckTimer), 'uds-auth: UDS session validation') + _apiHandlers = createApiHandlers( { session: INTERNAL.session }, _sessionStore, diff --git a/uds-auth/lib/session/memory-store.js b/uds-auth/lib/session/memory-store.js index 7eef8693..121505d7 100644 --- a/uds-auth/lib/session/memory-store.js +++ b/uds-auth/lib/session/memory-store.js @@ -280,6 +280,20 @@ export class MemoryStore extends SessionStore { return this._publicRecord(this._purgeExpired(id)) } + async listUdsSessionsForValidation() { + const sessions = [] + for (const [sessionId, record] of this._sessions) { + const row = this._purgeExpired(sessionId) + if (!row || row.kind !== 'uds') continue + sessions.push({ + sessionId: row.sessionId, + empNo: row.empNo, + token: typeof row.userData?.token === 'string' ? row.userData.token : '', + }) + } + return sessions + } + getBySessionIdSync(sessionId) { const id = String(sessionId || '').trim() if (!id) return null diff --git a/uds-auth/lib/session/store.js b/uds-auth/lib/session/store.js index 01255ba5..6fb655ff 100644 --- a/uds-auth/lib/session/store.js +++ b/uds-auth/lib/session/store.js @@ -27,6 +27,11 @@ export class SessionStore { throw new Error('SessionStore#getBySessionId must be implemented') } + /** Return active UDS sessions and their upstream token for periodic validation. */ + async listUdsSessionsForValidation() { + throw new Error('SessionStore#listUdsSessionsForValidation must be implemented') + } + async touch(sessionId, ttlSeconds) { throw new Error('SessionStore#touch must be implemented') }