@@ -5,6 +5,7 @@ use actix_web::{
55 Error ,
66} ;
77use futures_util:: future:: { ready, LocalBoxFuture , Ready } ;
8+ use subtle:: ConstantTimeEq ;
89
910pub struct ApiKeyMiddleware {
1011 api_key : String ,
@@ -56,16 +57,160 @@ where
5657 fn call ( & self , req : ServiceRequest ) -> Self :: Future {
5758 if let Some ( auth_header) = req. headers ( ) . get ( AUTHORIZATION ) {
5859 if let Ok ( auth_str) = auth_header. to_str ( ) {
59- if auth_str. to_lowercase ( ) == format ! ( "bearer {}" , self . api_key) {
60- let fut = self . service . call ( req) ;
61- return Box :: pin ( async move {
62- let res = fut. await ?;
63- Ok ( res)
64- } ) ;
60+ if auth_str. len ( ) > 7 {
61+ let ( scheme, key) = auth_str. split_at ( 7 ) ;
62+ if scheme. eq_ignore_ascii_case ( "Bearer " ) {
63+ let provided_key_bytes = key. as_bytes ( ) ;
64+ let expected_key_bytes = self . api_key . as_bytes ( ) ;
65+
66+ if provided_key_bytes. len ( ) == expected_key_bytes. len ( )
67+ && provided_key_bytes. ct_eq ( expected_key_bytes) . into ( )
68+ {
69+ let fut = self . service . call ( req) ;
70+ return Box :: pin ( async move {
71+ let res = fut. await ?;
72+ Ok ( res)
73+ } ) ;
74+ }
75+ }
6576 }
6677 }
6778 }
6879
6980 Box :: pin ( async move { Err ( ErrorUnauthorized ( "Invalid API key" ) ) } )
7081 }
7182}
83+
84+ #[ cfg( test) ]
85+ mod tests {
86+ use super :: * ;
87+ use actix_web:: { test, web, App , HttpResponse } ;
88+
89+ async fn test_handler ( ) -> HttpResponse {
90+ HttpResponse :: Ok ( ) . body ( "Success" )
91+ }
92+
93+ #[ actix_web:: test]
94+ async fn test_valid_api_key ( ) {
95+ let api_key = "test-api-key" ;
96+ let app = test:: init_service (
97+ App :: new ( )
98+ . wrap ( ApiKeyMiddleware :: new ( api_key. to_string ( ) ) )
99+ . route ( "/" , web:: get ( ) . to ( test_handler) ) ,
100+ )
101+ . await ;
102+
103+ let req = test:: TestRequest :: get ( )
104+ . uri ( "/" )
105+ . insert_header ( ( "Authorization" , "Bearer test-api-key" ) )
106+ . to_request ( ) ;
107+
108+ let resp = test:: call_service ( & app, req) . await ;
109+ assert ! ( resp. status( ) . is_success( ) ) ;
110+ }
111+
112+ #[ actix_web:: test]
113+ async fn test_invalid_api_key ( ) {
114+ let api_key = "test-api-key" ;
115+ let app = test:: init_service (
116+ App :: new ( )
117+ . wrap ( ApiKeyMiddleware :: new ( api_key. to_string ( ) ) )
118+ . route ( "/" , web:: get ( ) . to ( test_handler) ) ,
119+ )
120+ . await ;
121+
122+ let req = test:: TestRequest :: get ( )
123+ . uri ( "/" )
124+ . insert_header ( ( "Authorization" , "Bearer wrong-key" ) )
125+ . to_request ( ) ;
126+
127+ let resp = app. call ( req) . await ;
128+ assert ! ( resp. is_err( ) ) ;
129+ assert_eq ! ( resp. unwrap_err( ) . to_string( ) , "Invalid API key" ) ;
130+ }
131+
132+ #[ actix_web:: test]
133+ async fn test_missing_auth_header ( ) {
134+ let api_key = "test-api-key" ;
135+ let app = test:: init_service (
136+ App :: new ( )
137+ . wrap ( ApiKeyMiddleware :: new ( api_key. to_string ( ) ) )
138+ . route ( "/" , web:: get ( ) . to ( test_handler) ) ,
139+ )
140+ . await ;
141+
142+ let req = test:: TestRequest :: get ( ) . uri ( "/" ) . to_request ( ) ;
143+
144+ let resp = app. call ( req) . await ;
145+ assert ! ( resp. is_err( ) ) ;
146+ assert_eq ! ( resp. unwrap_err( ) . to_string( ) , "Invalid API key" ) ;
147+ }
148+
149+ #[ actix_web:: test]
150+ async fn test_lowercase_bearer_accepted ( ) {
151+ let api_key = "test-API-key" ;
152+ let app = test:: init_service (
153+ App :: new ( )
154+ . wrap ( ApiKeyMiddleware :: new ( api_key. to_string ( ) ) )
155+ . route ( "/" , web:: get ( ) . to ( test_handler) ) ,
156+ )
157+ . await ;
158+
159+ let req = test:: TestRequest :: get ( )
160+ . uri ( "/" )
161+ . insert_header ( ( "Authorization" , "bearer test-API-key" ) )
162+ . to_request ( ) ;
163+
164+ let resp = test:: call_service ( & app, req) . await ;
165+ assert ! ( resp. status( ) . is_success( ) ) ;
166+
167+ let req = test:: TestRequest :: get ( )
168+ . uri ( "/" )
169+ . insert_header ( ( "Authorization" , "bearer test-api-key" ) )
170+ . to_request ( ) ;
171+
172+ let resp = app. call ( req) . await ;
173+ assert ! ( resp. is_err( ) ) ;
174+ assert_eq ! ( resp. unwrap_err( ) . to_string( ) , "Invalid API key" ) ;
175+ }
176+
177+ #[ actix_web:: test]
178+ async fn test_mixed_case_api_key_rejected ( ) {
179+ let api_key = "test-api-key" ;
180+ let app = test:: init_service (
181+ App :: new ( )
182+ . wrap ( ApiKeyMiddleware :: new ( api_key. to_string ( ) ) )
183+ . route ( "/" , web:: get ( ) . to ( test_handler) ) ,
184+ )
185+ . await ;
186+
187+ let req = test:: TestRequest :: get ( )
188+ . uri ( "/" )
189+ . insert_header ( ( "Authorization" , "BeArEr test-API-key" ) )
190+ . to_request ( ) ;
191+
192+ let resp = app. call ( req) . await ;
193+ assert ! ( resp. is_err( ) ) ;
194+ assert_eq ! ( resp. unwrap_err( ) . to_string( ) , "Invalid API key" ) ;
195+ }
196+
197+ #[ actix_web:: test]
198+ async fn test_malformed_auth_header ( ) {
199+ let api_key = "test-api-key" ;
200+ let app = test:: init_service (
201+ App :: new ( )
202+ . wrap ( ApiKeyMiddleware :: new ( api_key. to_string ( ) ) )
203+ . route ( "/" , web:: get ( ) . to ( test_handler) ) ,
204+ )
205+ . await ;
206+
207+ let req = test:: TestRequest :: get ( )
208+ . uri ( "/" )
209+ . insert_header ( ( "Authorization" , "InvalidFormat" ) )
210+ . to_request ( ) ;
211+
212+ let resp = app. call ( req) . await ;
213+ assert ! ( resp. is_err( ) ) ;
214+ assert_eq ! ( resp. unwrap_err( ) . to_string( ) , "Invalid API key" ) ;
215+ }
216+ }
0 commit comments