@@ -93,6 +93,65 @@ export class OAuthResource extends Resource {
9393 return false ;
9494 }
9595
96+ /**
97+ * Check if a request is allowed to access debug endpoints
98+ * Uses IP allowlist for security (defaults to localhost only)
99+ *
100+ * @param request - The incoming request
101+ * @param logger - Optional logger for access tracking
102+ * @returns true if access is allowed, false otherwise
103+ */
104+ static checkDebugAccess ( request : Request , logger ?: Logger ) : boolean {
105+ // Get IP allowlist from environment variable or use localhost-only default
106+ // Use ?? to allow empty string (which denies all access)
107+ const DEBUG_ALLOWED_IPS = process . env . DEBUG_ALLOWED_IPS ?? '127.0.0.1,::1' ;
108+ const allowedIps = DEBUG_ALLOWED_IPS . split ( ',' ) . map ( ( ip ) => ip . trim ( ) ) ;
109+ const clientIp = request . ip || '' ;
110+
111+ // Check if client IP matches any allowed IP
112+ let ipAllowed = false ;
113+ for ( const allowed of allowedIps ) {
114+ // Exact match
115+ if ( allowed === clientIp ) {
116+ ipAllowed = true ;
117+ break ;
118+ }
119+ // Simple prefix match for CIDR-like patterns (e.g., "10.0.0." matches "10.0.0.1")
120+ if ( allowed . endsWith ( '.' ) && clientIp . startsWith ( allowed ) ) {
121+ ipAllowed = true ;
122+ break ;
123+ }
124+ }
125+
126+ // Log access attempt
127+ if ( ipAllowed ) {
128+ logger ?. info ?.( 'OAuth debug endpoint accessed' , {
129+ ip : clientIp ,
130+ } ) ;
131+ } else {
132+ logger ?. warn ?.( 'OAuth debug endpoint access denied - unauthorized IP' , {
133+ ip : clientIp ,
134+ allowedIps,
135+ } ) ;
136+ }
137+
138+ return ipAllowed ;
139+ }
140+
141+ /**
142+ * Build forbidden response for unauthorized debug access
143+ */
144+ static forbiddenResponse ( ) : any {
145+ return {
146+ status : 403 ,
147+ body : {
148+ error : 'Access forbidden' ,
149+ message : 'Debug endpoints are only accessible from allowed IPs.' ,
150+ hint : 'Set DEBUG_ALLOWED_IPS environment variable to allow access from your IP. Defaults to localhost only (127.0.0.1,::1).' ,
151+ } ,
152+ } ;
153+ }
154+
96155 /**
97156 * Build the standard 404 response
98157 */
@@ -209,6 +268,13 @@ export class OAuthResource extends Resource {
209268 return OAuthResource . notFoundResponse ( ) ;
210269 }
211270
271+ // If debug mode is enabled and this is a debug-only route, check IP allowlist
272+ if ( debugMode && OAuthResource . isDebugOnlyRoute ( route ) ) {
273+ if ( ! OAuthResource . checkDebugAccess ( request , logger ) ) {
274+ return OAuthResource . forbiddenResponse ( ) ;
275+ }
276+ }
277+
212278 // Special case: /oauth/test without provider
213279 if ( providerName === 'test' && ! action ) {
214280 return handleTestPage ( logger ) ;
0 commit comments