@@ -16,24 +16,97 @@ const pendingCodes = new Map();
1616// Maps client_id to client info
1717const registeredClients = new Map ( ) ;
1818
19+ // ============================================================================
20+ // Storage Limits and Cleanup Configuration
21+ // ============================================================================
22+
23+ const MAX_TOKEN_STORE_SIZE = 10000 ;
24+ const MAX_PENDING_CODES_SIZE = 1000 ;
25+ const MAX_REGISTERED_CLIENTS_SIZE = 1000 ;
26+ const TOKEN_EXPIRY_MS = 86400000 ; // 24 hours
27+ const CODE_EXPIRY_MS = 600000 ; // 10 minutes
28+ const CLIENT_EXPIRY_MS = 7 * 86400000 ; // 7 days
29+ const CLEANUP_INTERVAL_MS = 300000 ; // 5 minutes
30+
31+ // Cleanup interval reference (for cleanup on shutdown if needed)
32+ let cleanupIntervalId = null ;
33+
1934// ============================================================================
2035// OAuth Utilities
2136// ============================================================================
2237
23- /**
24- * Clean up expired tokens from tokenStore
25- * Called periodically to prevent unbounded memory growth
26- */
2738function cleanupExpiredTokens ( ) {
2839 const now = Date . now ( ) ;
29- const expiryMs = 86400000 ; // 24 hours
3040 for ( const [ token , data ] of tokenStore . entries ( ) ) {
31- if ( now - data . createdAt > expiryMs ) {
41+ if ( now - data . createdAt > TOKEN_EXPIRY_MS ) {
3242 tokenStore . delete ( token ) ;
3343 }
3444 }
3545}
3646
47+ function cleanupExpiredCodes ( ) {
48+ const now = Date . now ( ) ;
49+ for ( const [ code , data ] of pendingCodes . entries ( ) ) {
50+ if ( now - data . createdAt > CODE_EXPIRY_MS ) {
51+ pendingCodes . delete ( code ) ;
52+ }
53+ }
54+ }
55+
56+ function cleanupExpiredClients ( ) {
57+ const now = Date . now ( ) ;
58+ for ( const [ clientId , data ] of registeredClients . entries ( ) ) {
59+ if ( now - data . createdAt > CLIENT_EXPIRY_MS ) {
60+ registeredClients . delete ( clientId ) ;
61+ }
62+ }
63+ }
64+
65+ function enforceStorageLimits ( ) {
66+ if ( tokenStore . size > MAX_TOKEN_STORE_SIZE ) {
67+ const entries = [ ...tokenStore . entries ( ) ] . sort ( ( a , b ) => a [ 1 ] . createdAt - b [ 1 ] . createdAt ) ;
68+ const toRemove = entries . slice ( 0 , tokenStore . size - MAX_TOKEN_STORE_SIZE ) ;
69+ for ( const [ key ] of toRemove ) {
70+ tokenStore . delete ( key ) ;
71+ }
72+ }
73+ if ( pendingCodes . size > MAX_PENDING_CODES_SIZE ) {
74+ const entries = [ ...pendingCodes . entries ( ) ] . sort ( ( a , b ) => a [ 1 ] . createdAt - b [ 1 ] . createdAt ) ;
75+ const toRemove = entries . slice ( 0 , pendingCodes . size - MAX_PENDING_CODES_SIZE ) ;
76+ for ( const [ key ] of toRemove ) {
77+ pendingCodes . delete ( key ) ;
78+ }
79+ }
80+ if ( registeredClients . size > MAX_REGISTERED_CLIENTS_SIZE ) {
81+ const entries = [ ...registeredClients . entries ( ) ] . sort ( ( a , b ) => a [ 1 ] . createdAt - b [ 1 ] . createdAt ) ;
82+ const toRemove = entries . slice ( 0 , registeredClients . size - MAX_REGISTERED_CLIENTS_SIZE ) ;
83+ for ( const [ key ] of toRemove ) {
84+ registeredClients . delete ( key ) ;
85+ }
86+ }
87+ }
88+
89+ function runPeriodicCleanup ( ) {
90+ cleanupExpiredTokens ( ) ;
91+ cleanupExpiredCodes ( ) ;
92+ cleanupExpiredClients ( ) ;
93+ enforceStorageLimits ( ) ;
94+ }
95+
96+ export function startCleanupInterval ( ) {
97+ if ( ! cleanupIntervalId ) {
98+ cleanupIntervalId = setInterval ( runPeriodicCleanup , CLEANUP_INTERVAL_MS ) ;
99+ cleanupIntervalId . unref ?. ( ) ;
100+ }
101+ }
102+
103+ export function stopCleanupInterval ( ) {
104+ if ( cleanupIntervalId ) {
105+ clearInterval ( cleanupIntervalId ) ;
106+ cleanupIntervalId = null ;
107+ }
108+ }
109+
37110function generateToken ( ) {
38111 return randomBytes ( 32 ) . toString ( "hex" ) ;
39112}
@@ -545,20 +618,14 @@ export function handleOAuthToken(req, res) {
545618 } ) ;
546619}
547620
548- /**
549- * Validate Bearer token and return session info
550- * @param {string } token - Bearer token
551- * @returns {{sessionId: string, backendUrl: string} | null }
552- */
553621export function validateBearerToken ( token ) {
554622 const tokenData = tokenStore . get ( token ) ;
555623 if ( ! tokenData ) {
556624 return null ;
557625 }
558626
559- // Check token age (24 hour expiry)
560627 const tokenAge = Date . now ( ) - tokenData . createdAt ;
561- if ( tokenAge > 86400000 ) {
628+ if ( tokenAge > TOKEN_EXPIRY_MS ) {
562629 tokenStore . delete ( token ) ;
563630 return null ;
564631 }
@@ -583,3 +650,5 @@ export function isOAuthEndpoint(pathname) {
583650 pathname === "/oauth/token"
584651 ) ;
585652}
653+
654+ startCleanupInterval ( ) ;
0 commit comments