Skip to content

Commit cc89be2

Browse files
committed
fix(auth): improve token validation & logging
- Added extensive logging - Improved error handling - Fixed sub claim type check
1 parent 910f1b7 commit cc89be2

File tree

3 files changed

+83
-26
lines changed

3 files changed

+83
-26
lines changed

lib/src/middlewares/authentication_middleware.dart

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,37 @@ import 'package:ht_shared/ht_shared.dart';
1717
Middleware authenticationProvider() {
1818
return (handler) {
1919
return (context) async {
20-
// Read the AuthTokenService provided by earlier middleware
21-
final tokenService = context.read<AuthTokenService>();
20+
print('[AuthMiddleware] Entered.'); // Log 1: Entry
21+
AuthTokenService tokenService;
22+
try {
23+
print('[AuthMiddleware] Attempting to read AuthTokenService...'); // Log 2: Before read
24+
tokenService = context.read<AuthTokenService>();
25+
print('[AuthMiddleware] Successfully read AuthTokenService.'); // Log 3: After read
26+
} catch (e, s) {
27+
print('[AuthMiddleware] FAILED to read AuthTokenService: $e\n$s'); // Log Error
28+
// Re-throw the error to be caught by the main error handler
29+
rethrow;
30+
}
2231
User? user; // Initialize user as null
2332

2433
// Extract the Authorization header
34+
print('[AuthMiddleware] Attempting to read Authorization header...'); // Log 4: Before header read
2535
final authHeader = context.request.headers['Authorization'];
36+
print('[AuthMiddleware] Authorization header value: $authHeader'); // Log 5: Header value
2637

2738
if (authHeader != null && authHeader.startsWith('Bearer ')) {
2839
// Extract the token string
2940
final token = authHeader.substring(7); // Length of 'Bearer '
41+
print('[AuthMiddleware] Extracted Bearer token.'); // Log 6: Token extracted
3042
try {
43+
print('[AuthMiddleware] Attempting to validate token...'); // Log 7: Before validate
3144
// Validate the token using the service
3245
user = await tokenService.validateToken(token);
46+
print('[AuthMiddleware] Token validation returned: ${user?.id ?? 'null'}'); // Log 8: After validate
3347
if (user != null) {
34-
print('Authentication successful for user: ${user.id}');
48+
print('[AuthMiddleware] Authentication successful for user: ${user.id}');
3549
} else {
36-
print('Invalid token provided.');
50+
print('[AuthMiddleware] Invalid token provided (validateToken returned null).');
3751
// Optional: Could throw UnauthorizedException here if *all* routes
3852
// using this middleware strictly require a valid token.
3953
// However, providing null allows routes to handle optional auth.
@@ -43,18 +57,19 @@ Middleware authenticationProvider() {
4357
print('Token validation failed: $e');
4458
// Let the error propagate if needed, or handle specific cases.
4559
// For now, we treat validation errors as resulting in no user.
46-
user = null;
47-
} catch (e) {
60+
user = null; // Keep user null if HtHttpException occurred
61+
} catch (e, s) {
4862
// Catch unexpected errors during validation
49-
print('Unexpected error during token validation: $e');
50-
user = null;
63+
print('[AuthMiddleware] Unexpected error during token validation: $e\n$s');
64+
user = null; // Keep user null if unexpected error occurred
5165
}
5266
} else {
53-
print('No valid Authorization header found.');
67+
print('[AuthMiddleware] No valid Bearer token found in header.');
5468
}
5569

5670
// Provide the User object (or null) into the context
5771
// This makes `context.read<User?>()` available downstream.
72+
print('[AuthMiddleware] Providing User (${user?.id ?? 'null'}) to context.'); // Log 9: Before provide
5873
return handler(context.provide<User?>(() => user));
5974
};
6075
};

lib/src/services/jwt_auth_token_service.dart

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -82,46 +82,83 @@ class JwtAuthTokenService implements AuthTokenService {
8282

8383
@override
8484
Future<User?> validateToken(String token) async {
85+
print('[validateToken] Attempting to validate token...');
8586
try {
8687
// Verify the token's signature and expiry
88+
print('[validateToken] Verifying token signature and expiry...');
8789
final jwt = JWT.verify(token, SecretKey(_secretKey));
90+
print('[validateToken] Token verified. Payload: ${jwt.payload}');
8891

8992
// Extract user ID from the subject claim
90-
final userId = jwt.payload['sub'] as String?;
91-
if (userId == null) {
92-
print('Token validation failed: Missing "sub" claim.');
93+
final subClaim = jwt.payload['sub'];
94+
print(
95+
'[validateToken] Extracted "sub" claim: $subClaim '
96+
'(Type: ${subClaim.runtimeType})',
97+
);
98+
99+
// Safely attempt to cast to String
100+
String? userId;
101+
if (subClaim is String) {
102+
userId = subClaim;
103+
print('[validateToken] "sub" claim successfully cast to String: $userId');
104+
} else if (subClaim != null) {
105+
print(
106+
'[validateToken] WARNING: "sub" claim is not a String. '
107+
'Attempting toString().',
108+
);
109+
// Handle potential non-string types if necessary, or throw error
110+
// For now, let's treat non-string sub as an error
111+
throw BadRequestException(
112+
'Malformed token: "sub" claim is not a String '
113+
'(Type: ${subClaim.runtimeType}).',
114+
);
115+
}
116+
117+
if (userId == null || userId.isEmpty) {
118+
print('[validateToken] Token validation failed: Missing or empty "sub" claim.');
93119
// Throw specific exception for malformed token
94120
throw const BadRequestException(
95-
'Malformed token: Missing subject claim.',
121+
'Malformed token: Missing or empty subject claim.',
96122
);
97123
}
98124

125+
print('[validateToken] Attempting to fetch user with ID: $userId');
99126
// Fetch the full user object from the repository
100127
// This ensures the user still exists and is valid
101128
final user = await _userRepository.read(userId);
102-
print('Token validated successfully for user ${user.id}');
129+
print('[validateToken] User repository read successful for ID: $userId');
130+
print('[validateToken] Token validated successfully for user ${user.id}');
103131
return user;
104-
} on JWTExpiredException {
105-
print('Token validation failed: Token expired.');
132+
} on JWTExpiredException catch (e, s) {
133+
print('[validateToken] CATCH JWTExpiredException: Token expired. $e\n$s');
106134
// Throw specific exception for expired token
107135
throw const UnauthorizedException('Token expired.');
108-
} on JWTInvalidException catch (e) {
109-
print('Token validation failed: Invalid token. Reason: ${e.message}');
136+
} on JWTInvalidException catch (e, s) {
137+
print(
138+
'[validateToken] CATCH JWTInvalidException: Invalid token. '
139+
'Reason: ${e.message}\n$s',
140+
);
110141
// Throw specific exception for invalid token signature/format
111142
throw UnauthorizedException('Invalid token: ${e.message}');
112-
} on JWTException catch (e) {
143+
} on JWTException catch (e, s) {
113144
// Use JWTException as the general catch-all
114-
print('Token validation failed: JWT Exception. Reason: ${e.message}');
145+
print(
146+
'[validateToken] CATCH JWTException: General JWT error. '
147+
'Reason: ${e.message}\n$s',
148+
);
115149
// Treat other JWT exceptions as invalid tokens
116150
throw UnauthorizedException('Invalid token: ${e.message}');
117-
} on HtHttpException catch (e) {
151+
} on HtHttpException catch (e, s) {
118152
// Handle errors from the user repository (e.g., user not found)
119-
print('Token validation failed: Error fetching user $e');
153+
print(
154+
'[validateToken] CATCH HtHttpException: Error fetching user. '
155+
'Type: ${e.runtimeType}, Message: $e\n$s',
156+
);
120157
// Re-throw repository exceptions directly for the error handler
121158
rethrow;
122-
} catch (e) {
159+
} catch (e, s) {
123160
// Catch unexpected errors during validation
124-
print('Unexpected error during token validation: $e');
161+
print('[validateToken] CATCH UNEXPECTED Exception: $e\n$s');
125162
// Wrap unexpected errors in a standard exception type
126163
throw OperationFailedException(
127164
'Token validation failed unexpectedly: $e',

routes/_middleware.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import 'dart:convert';
66
import 'dart:io';
77

88
import 'package:dart_frog/dart_frog.dart';
9-
// import 'package:ht_api/src/middlewares/authentication_middleware.dart';
9+
import 'package:ht_api/src/middlewares/authentication_middleware.dart';
1010
import 'package:ht_api/src/middlewares/error_handler.dart';
1111
import 'package:ht_api/src/registry/model_registry.dart';
1212
import 'package:ht_api/src/services/auth_service.dart';
@@ -180,25 +180,30 @@ Handler middleware(Handler handler) {
180180
// No initial user data fixture needed for auth flow typically
181181
),
182182
);
183+
print('[MiddlewareSetup] HtDataRepository<User> instantiated.'); // Added log
183184
// Email Repo (using InMemory)
184185
const emailRepository = HtEmailRepository(
185186
emailClient: HtEmailInMemoryClient(),
186187
);
188+
print('[MiddlewareSetup] HtEmailRepository instantiated.'); // Added log
187189
// Auth Services (using JWT and in-memory implementations)
188190
// Instantiate the new JWT service, passing its dependencies
189191
final authTokenService = JwtAuthTokenService(
190192
userRepository: userRepository,
191193
uuidGenerator: uuid,
192194
);
195+
print('[MiddlewareSetup] JwtAuthTokenService instantiated.'); // Added log
193196
final verificationCodeStorageService =
194197
InMemoryVerificationCodeStorageService();
198+
print('[MiddlewareSetup] InMemoryVerificationCodeStorageService instantiated.'); // Added log
195199
final authService = AuthService(
196200
userRepository: userRepository,
197201
authTokenService: authTokenService,
198202
verificationCodeStorageService: verificationCodeStorageService,
199203
emailRepository: emailRepository,
200204
uuidGenerator: uuid,
201205
);
206+
print('[MiddlewareSetup] AuthService instantiated.'); // Added log
202207

203208
// Chain the providers and other middleware
204209
return handler
@@ -240,7 +245,7 @@ Handler middleware(Handler handler) {
240245
// --- Core Middleware ---
241246
.use(requestLogger()) // Basic request logging
242247
// Apply authenticationProvider to make User? available downstream
243-
// .use(authenticationProvider())
248+
.use(authenticationProvider())
244249
// Error handler should generally be last to catch all upstream errors
245250
.use(errorHandler());
246251
}

0 commit comments

Comments
 (0)