@@ -3,6 +3,7 @@ import 'package:ht_api/src/services/auth_token_service.dart';
3
3
import 'package:ht_api/src/services/token_blacklist_service.dart' ;
4
4
import 'package:ht_data_repository/ht_data_repository.dart' ;
5
5
import 'package:ht_shared/ht_shared.dart' ;
6
+ import 'package:logging/logging.dart' ;
6
7
import 'package:uuid/uuid.dart' ;
7
8
8
9
/// {@template jwt_auth_token_service}
@@ -23,13 +24,16 @@ class JwtAuthTokenService implements AuthTokenService {
23
24
required HtDataRepository <User > userRepository,
24
25
required TokenBlacklistService blacklistService,
25
26
required Uuid uuidGenerator,
27
+ required Logger log,
26
28
}) : _userRepository = userRepository,
27
29
_blacklistService = blacklistService,
28
- _uuid = uuidGenerator;
30
+ _uuid = uuidGenerator,
31
+ _log = log;
29
32
30
33
final HtDataRepository <User > _userRepository;
31
34
final TokenBlacklistService _blacklistService;
32
35
final Uuid _uuid;
36
+ final Logger _log;
33
37
34
38
// --- Configuration ---
35
39
@@ -76,10 +80,10 @@ class JwtAuthTokenService implements AuthTokenService {
76
80
expiresIn: _tokenExpiryDuration, // Redundant but safe
77
81
);
78
82
79
- print ('Generated JWT for user ${user .id }' );
83
+ _log. info ('Generated JWT for user ${user .id }' );
80
84
return token;
81
85
} catch (e) {
82
- print ('Error generating JWT for user ${user .id }: $e ' );
86
+ _log. severe ('Error generating JWT for user ${user .id }: $e ' );
83
87
// Map to a standard exception
84
88
throw OperationFailedException (
85
89
'Failed to generate authentication token: $e ' ,
@@ -89,18 +93,18 @@ class JwtAuthTokenService implements AuthTokenService {
89
93
90
94
@override
91
95
Future <User ?> validateToken (String token) async {
92
- print ('[validateToken] Attempting to validate token...' );
96
+ _log. finer ('[validateToken] Attempting to validate token...' );
93
97
try {
94
98
// Verify the token's signature and expiry
95
- print ('[validateToken] Verifying token signature and expiry...' );
99
+ _log. finer ('[validateToken] Verifying token signature and expiry...' );
96
100
final jwt = JWT .verify (token, SecretKey (_secretKey));
97
- print ('[validateToken] Token verified. Payload: ${jwt .payload }' );
101
+ _log. finer ('[validateToken] Token verified. Payload: ${jwt .payload }' );
98
102
99
103
// --- Blacklist Check ---
100
104
// Extract the JWT ID (jti) claim
101
105
final jti = jwt.payload['jti' ] as String ? ;
102
106
if (jti == null || jti.isEmpty) {
103
- print (
107
+ _log. warning (
104
108
'[validateToken] Token validation failed: Missing or empty "jti" claim.' ,
105
109
);
106
110
// Throw specific exception for malformed token
@@ -109,21 +113,21 @@ class JwtAuthTokenService implements AuthTokenService {
109
113
);
110
114
}
111
115
112
- print ('[validateToken] Checking blacklist for jti: $jti ' );
116
+ _log. finer ('[validateToken] Checking blacklist for jti: $jti ' );
113
117
final isBlacklisted = await _blacklistService.isBlacklisted (jti);
114
118
if (isBlacklisted) {
115
- print (
119
+ _log. warning (
116
120
'[validateToken] Token validation failed: Token is blacklisted (jti: $jti ).' ,
117
121
);
118
122
// Throw specific exception for blacklisted token
119
123
throw const UnauthorizedException ('Token has been invalidated.' );
120
124
}
121
- print ('[validateToken] Token is not blacklisted (jti: $jti ).' );
125
+ _log. finer ('[validateToken] Token is not blacklisted (jti: $jti ).' );
122
126
// --- End Blacklist Check ---
123
127
124
128
// Extract user ID from the subject claim ('sub')
125
129
final subClaim = jwt.payload['sub' ];
126
- print (
130
+ _log. finer (
127
131
'[validateToken] Extracted "sub" claim: $subClaim '
128
132
'(Type: ${subClaim .runtimeType })' ,
129
133
);
@@ -132,12 +136,12 @@ class JwtAuthTokenService implements AuthTokenService {
132
136
String ? userId;
133
137
if (subClaim is String ) {
134
138
userId = subClaim;
135
- print (
139
+ _log. finer (
136
140
'[validateToken] "sub" claim successfully cast to String: $userId ' ,
137
141
);
138
142
} else if (subClaim != null ) {
139
143
// Treat non-string sub as an error
140
- print (
144
+ _log. severe (
141
145
'[validateToken] ERROR: "sub" claim is not a String '
142
146
'(Type: ${subClaim .runtimeType }).' ,
143
147
);
@@ -148,7 +152,7 @@ class JwtAuthTokenService implements AuthTokenService {
148
152
}
149
153
150
154
if (userId == null || userId.isEmpty) {
151
- print (
155
+ _log. warning (
152
156
'[validateToken] Token validation failed: Missing or empty "sub" claim.' ,
153
157
);
154
158
// Throw specific exception for malformed token
@@ -157,27 +161,27 @@ class JwtAuthTokenService implements AuthTokenService {
157
161
);
158
162
}
159
163
160
- print ('[validateToken] Attempting to fetch user with ID: $userId ' );
164
+ _log. finer ('[validateToken] Attempting to fetch user with ID: $userId ' );
161
165
// Fetch the full user object from the repository
162
166
// This ensures the user still exists and is valid
163
167
final user = await _userRepository.read (id: userId);
164
- print ('[validateToken] User repository read successful for ID: $userId ' );
165
- print ('[validateToken] Token validated successfully for user ${user .id }' );
168
+ _log. finer ('[validateToken] User repository read successful for ID: $userId ' );
169
+ _log. info ('[validateToken] Token validated successfully for user ${user .id }' );
166
170
return user;
167
171
} on JWTExpiredException catch (e, s) {
168
- print ('[validateToken] CATCH JWTExpiredException: Token expired. $ e \n $ s ' );
172
+ _log. warning ('[validateToken] Token expired.' , e, s );
169
173
// Throw the standardized exception instead of rethrowing the specific one
170
174
throw const UnauthorizedException ('Token expired.' );
171
175
} on JWTInvalidException catch (e, s) {
172
- print (
176
+ _log. warning (
173
177
'[validateToken] CATCH JWTInvalidException: Invalid token. '
174
178
'Reason: ${e .message }\n $s ' ,
175
179
);
176
180
// Throw specific exception for invalid token signature/format
177
181
throw UnauthorizedException ('Invalid token: ${e .message }' );
178
182
} on JWTException catch (e, s) {
179
183
// Use JWTException as the general catch-all for other JWT issues
180
- print (
184
+ _log. warning (
181
185
'[validateToken] CATCH JWTException: General JWT error. '
182
186
'Reason: ${e .message }\n $s ' ,
183
187
);
@@ -186,15 +190,15 @@ class JwtAuthTokenService implements AuthTokenService {
186
190
} on HtHttpException catch (e, s) {
187
191
// Handle errors from the user repository (e.g., user not found)
188
192
// or blacklist check (if it threw HtHttpException)
189
- print (
193
+ _log. warning (
190
194
'[validateToken] CATCH HtHttpException: Error during validation. '
191
195
'Type: ${e .runtimeType }, Message: $e \n $s ' ,
192
196
);
193
197
// Re-throw repository/blacklist exceptions directly
194
198
rethrow ;
195
199
} catch (e, s) {
196
200
// Catch unexpected errors during validation
197
- print ('[validateToken] CATCH UNEXPECTED Exception: $ e \n $ s ' );
201
+ _log. severe ('[validateToken] CATCH UNEXPECTED Exception' , e, s );
198
202
// Wrap unexpected errors in a standard exception type
199
203
throw OperationFailedException (
200
204
'Token validation failed unexpectedly: $e ' ,
@@ -204,33 +208,33 @@ class JwtAuthTokenService implements AuthTokenService {
204
208
205
209
@override
206
210
Future <void > invalidateToken (String token) async {
207
- print ('[invalidateToken] Attempting to invalidate token...' );
211
+ _log. finer ('[invalidateToken] Attempting to invalidate token...' );
208
212
try {
209
213
// 1. Verify the token signature FIRST, but ignore expiry for blacklisting
210
214
// We want to blacklist even if it's already expired, to be safe.
211
- print ('[invalidateToken] Verifying token signature (ignoring expiry)...' );
215
+ _log. finer ('[invalidateToken] Verifying signature (ignoring expiry)...' );
212
216
final jwt = JWT .verify (
213
217
token,
214
218
SecretKey (_secretKey),
215
219
checkExpiresIn: false , // IMPORTANT: Don't fail if expired here
216
220
checkHeaderType: true , // Keep other standard checks
217
221
);
218
- print ('[invalidateToken] Token signature verified.' );
222
+ _log. finer ('[invalidateToken] Token signature verified.' );
219
223
220
224
// 2. Extract JTI (JWT ID)
221
225
final jti = jwt.payload['jti' ] as String ? ;
222
226
if (jti == null || jti.isEmpty) {
223
- print ('[invalidateToken] Failed: Missing or empty "jti" claim.' );
227
+ _log. warning ('[invalidateToken] Failed: Missing or empty "jti" claim.' );
224
228
throw const InvalidInputException (
225
229
'Cannot invalidate token: Missing or empty JWT ID (jti) claim.' ,
226
230
);
227
231
}
228
- print ('[invalidateToken] Extracted jti: $jti ' );
232
+ _log. finer ('[invalidateToken] Extracted jti: $jti ' );
229
233
230
234
// 3. Extract Expiry Time (exp)
231
235
final expClaim = jwt.payload['exp' ];
232
236
if (expClaim == null || expClaim is ! int ) {
233
- print ('[invalidateToken] Failed: Missing or invalid "exp" claim.' );
237
+ _log. warning ('[invalidateToken] Failed: Missing or invalid "exp" claim.' );
234
238
throw const InvalidInputException (
235
239
'Cannot invalidate token: Missing or invalid expiry (exp) claim.' ,
236
240
);
@@ -239,31 +243,31 @@ class JwtAuthTokenService implements AuthTokenService {
239
243
expClaim * 1000 ,
240
244
isUtc: true ,
241
245
);
242
- print ('[invalidateToken] Extracted expiry: $expiryDateTime ' );
246
+ _log. finer ('[invalidateToken] Extracted expiry: $expiryDateTime ' );
243
247
244
248
// 4. Add JTI to the blacklist
245
- print ('[invalidateToken] Adding jti $jti to blacklist...' );
249
+ _log. finer ('[invalidateToken] Adding jti $jti to blacklist...' );
246
250
await _blacklistService.blacklist (jti, expiryDateTime);
247
- print ('[invalidateToken] Token (jti: $jti ) successfully blacklisted.' );
251
+ _log. info ('[invalidateToken] Token (jti: $jti ) successfully blacklisted.' );
248
252
} on JWTException catch (e, s) {
249
253
// Catch errors during the initial verification (e.g., bad signature)
250
- print (
254
+ _log. warning (
251
255
'[invalidateToken] CATCH JWTException: Invalid token format/signature. '
252
256
'Reason: ${e .message }\n $s ' ,
253
257
);
254
258
// Treat as invalid input for invalidation purposes
255
259
throw InvalidInputException ('Invalid token format: ${e .message }' );
256
260
} on HtHttpException catch (e, s) {
257
261
// Catch errors from the blacklist service itself
258
- print (
262
+ _log. warning (
259
263
'[invalidateToken] CATCH HtHttpException: Error during blacklisting. '
260
264
'Type: ${e .runtimeType }, Message: $e \n $s ' ,
261
265
);
262
266
// Re-throw blacklist service exceptions
263
267
rethrow ;
264
268
} catch (e, s) {
265
269
// Catch unexpected errors
266
- print ('[invalidateToken] CATCH UNEXPECTED Exception: $ e \n $ s ' );
270
+ _log. severe ('[invalidateToken] CATCH UNEXPECTED Exception' , e, s );
267
271
throw OperationFailedException (
268
272
'Token invalidation failed unexpectedly: $e ' ,
269
273
);
0 commit comments