@@ -13,7 +13,10 @@ export interface APIKeyConfig {
1313 keys : string [ ] ;
1414
1515 /**
16- * Name of the header containing the API key
16+ * Name of the header containing the API key.
17+ * Note: Some SSE client libraries (including the standard EventSource API)
18+ * do not support custom headers. For these clients, the API key can also
19+ * be sent as a query parameter: ?api_key=<key> or ?apiKey=<key>
1720 * @default "X-API-Key"
1821 */
1922 headerName ?: string ;
@@ -78,8 +81,37 @@ export class APIKeyAuthProvider implements AuthProvider {
7881 }
7982 }
8083
84+ // Fallback: check Authorization Bearer header
85+ // Some clients (e.g., LangChain) may send API key as Bearer token
8186 if ( ! apiKey ) {
82- logger . debug ( `API Key header missing}` ) ;
87+ const authHeader = req . headers [ 'authorization' ] ;
88+ if ( authHeader && typeof authHeader === 'string' && authHeader . startsWith ( 'Bearer ' ) ) {
89+ const bearerToken = authHeader . slice ( 7 ) . trim ( ) ;
90+ if ( bearerToken ) {
91+ apiKey = bearerToken ;
92+ matchedHeader = 'Authorization Bearer' ;
93+ logger . debug ( 'API key found in Authorization Bearer header (fallback)' ) ;
94+ }
95+ }
96+ }
97+
98+ // Fallback: check query parameter for SSE clients that cannot send custom headers
99+ if ( ! apiKey && req . url ) {
100+ try {
101+ const url = new URL ( req . url , `http://${ req . headers . host || 'localhost' } ` ) ;
102+ const queryKey = url . searchParams . get ( 'api_key' ) || url . searchParams . get ( 'apiKey' ) ;
103+ if ( queryKey ) {
104+ apiKey = queryKey ;
105+ matchedHeader = 'query parameter' ;
106+ logger . debug ( 'API key found in query parameter (EventSource fallback)' ) ;
107+ }
108+ } catch {
109+ // URL parsing failed, skip query parameter check
110+ }
111+ }
112+
113+ if ( ! apiKey ) {
114+ logger . debug ( `API Key header missing` ) ;
83115 logger . debug ( `Available headers: ${ Object . keys ( req . headers ) . join ( ', ' ) } ` ) ;
84116 return false ;
85117 }
0 commit comments