@@ -32,14 +32,27 @@ pub async fn require_jwt_authentication(
3232 let token = auth_value
3333 . strip_prefix ( "Bearer " )
3434 . map ( |auth| auth. to_owned ( ) ) ;
35- let claims = decode :: < TokenClaims > (
35+ let claims = match decode :: < TokenClaims > (
3636 & token. unwrap_or_default ( ) ,
3737 & DecodingKey :: from_secret ( jwt_token. as_ref ( ) ) ,
3838 & Validation :: default ( ) ,
39- )
40- . map_err ( |_| Status :: unauthenticated ( "No valid auth token claims found" ) )
41- . unwrap ( )
42- . claims ;
39+ ) {
40+ Ok ( token_data) => token_data. claims ,
41+ Err ( jwt_error) => {
42+ let error_message = match jwt_error. kind ( ) {
43+ jsonwebtoken:: errors:: ErrorKind :: ExpiredSignature => "Token expired" ,
44+ jsonwebtoken:: errors:: ErrorKind :: InvalidToken => "Invalid token" ,
45+ jsonwebtoken:: errors:: ErrorKind :: InvalidSignature => "Invalid token signature" ,
46+ _ => "Token validation failed" ,
47+ } ;
48+
49+ let json_error = ErrorResponse {
50+ status : false ,
51+ message : format ! ( "Authentication failed: {}" , error_message) ,
52+ } ;
53+ return Err ( ( StatusCode :: UNAUTHORIZED , Json ( json_error) ) ) ;
54+ }
55+ } ;
4356
4457 let file_exist = true ;
4558 let logged_in_user = LoggedInUser {
@@ -121,3 +134,125 @@ pub async fn require_jwt_authentication(
121134 //
122135 // Ok(next.run(req).await)
123136}
137+
138+ #[ cfg( test) ]
139+ mod tests {
140+ use super :: * ;
141+ use axum:: body:: Body ;
142+ use axum:: http:: { Request , StatusCode } ;
143+ use axum:: middleware:: Next ;
144+ use axum:: response:: Response ;
145+ use jsonwebtoken:: { encode, EncodingKey , Header } ;
146+ use std:: env;
147+ use chrono:: { Duration , Utc } ;
148+
149+ // Helper function to create a test token
150+ fn create_test_token ( expired : bool ) -> String {
151+ let now = Utc :: now ( ) ;
152+ let exp = if expired {
153+ ( now - Duration :: minutes ( 10 ) ) . timestamp ( ) as usize // Expired 10 minutes ago
154+ } else {
155+ ( now + Duration :: minutes ( 60 ) ) . timestamp ( ) as usize // Valid for 60 minutes
156+ } ;
157+
158+ let claims = TokenClaims {
159+ sub : "test-user-id" . to_string ( ) ,
160+ name : "Test User" . to_string ( ) ,
161+ email : "test@example.com" . to_string ( ) ,
162+ admin_user_model : crate :: models:: admin_user_model:: AdminUserModel :: default ( ) ,
163+ iat : now. timestamp ( ) as usize ,
164+ exp,
165+ } ;
166+
167+ let secret = env:: var ( "AVORED_JWT_SECRET" ) . unwrap_or_else ( |_| "test-secret" . to_string ( ) ) ;
168+ encode (
169+ & Header :: default ( ) ,
170+ & claims,
171+ & EncodingKey :: from_secret ( secret. as_bytes ( ) ) ,
172+ )
173+ . unwrap ( )
174+ }
175+
176+ // Mock next function for testing
177+ async fn mock_next ( _req : Request < Body > ) -> Result < Response < Body > , std:: convert:: Infallible > {
178+ Ok ( Response :: builder ( )
179+ . status ( StatusCode :: OK )
180+ . body ( Body :: from ( "success" ) )
181+ . unwrap ( ) )
182+ }
183+
184+ #[ tokio:: test]
185+ async fn test_expired_token_should_return_unauthorized ( ) {
186+ // Set up environment
187+ env:: set_var ( "AVORED_JWT_SECRET" , "test-secret" ) ;
188+
189+ // Create request with expired token
190+ let expired_token = create_test_token ( true ) ;
191+ let req = Request :: builder ( )
192+ . header ( "authorization" , format ! ( "Bearer {}" , expired_token) )
193+ . body ( Body :: empty ( ) )
194+ . unwrap ( ) ;
195+
196+ // Create mock next function
197+ let next = Next :: new ( mock_next) ;
198+
199+ // Call the middleware
200+ let result = require_jwt_authentication ( req, next) . await ;
201+
202+ // After the fix, this should return Err with StatusCode::UNAUTHORIZED
203+ match result {
204+ Ok ( _) => panic ! ( "Expected error for expired token, but got success" ) ,
205+ Err ( ( status, json_response) ) => {
206+ assert_eq ! ( status, StatusCode :: UNAUTHORIZED , "Expected 401 Unauthorized for expired token" ) ;
207+ // Verify the error message contains information about token expiry
208+ let error_msg = format ! ( "{:?}" , json_response) ;
209+ assert ! ( error_msg. contains( "Token expired" ) || error_msg. contains( "Authentication failed" ) ,
210+ "Error message should indicate token expiry: {}" , error_msg) ;
211+ }
212+ }
213+ }
214+
215+ #[ tokio:: test]
216+ async fn test_valid_token_should_succeed ( ) {
217+ // Set up environment
218+ env:: set_var ( "AVORED_JWT_SECRET" , "test-secret" ) ;
219+
220+ // Create request with valid token
221+ let valid_token = create_test_token ( false ) ;
222+ let req = Request :: builder ( )
223+ . header ( "authorization" , format ! ( "Bearer {}" , valid_token) )
224+ . body ( Body :: empty ( ) )
225+ . unwrap ( ) ;
226+
227+ // Create mock next function
228+ let next = Next :: new ( mock_next) ;
229+
230+ // Call the middleware
231+ let result = require_jwt_authentication ( req, next) . await ;
232+
233+ // Should succeed with valid token
234+ assert ! ( result. is_ok( ) , "Expected success with valid token" ) ;
235+ }
236+
237+ #[ tokio:: test]
238+ async fn test_missing_token_should_return_bad_request ( ) {
239+ // Create request without authorization header
240+ let req = Request :: builder ( )
241+ . body ( Body :: empty ( ) )
242+ . unwrap ( ) ;
243+
244+ // Create mock next function
245+ let next = Next :: new ( mock_next) ;
246+
247+ // Call the middleware
248+ let result = require_jwt_authentication ( req, next) . await ;
249+
250+ // Should return error for missing token
251+ match result {
252+ Ok ( _) => panic ! ( "Expected error for missing token, but got success" ) ,
253+ Err ( ( status, _) ) => {
254+ assert_eq ! ( status, StatusCode :: BAD_REQUEST , "Expected 400 Bad Request for missing token" ) ;
255+ }
256+ }
257+ }
258+ }
0 commit comments