Skip to content

Commit c977fa4

Browse files
committed
refactor: clarify middleware chain order
- Documented middleware chain - Added purpose of each middleware - Explained ordering importance
1 parent 73080d3 commit c977fa4

File tree

4 files changed

+108
-39
lines changed

4 files changed

+108
-39
lines changed

lib/src/middlewares/authentication_middleware.dart

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,34 +22,44 @@ Middleware authenticationProvider() {
2222
// Read the interface type
2323
AuthTokenService tokenService;
2424
try {
25-
print('[AuthMiddleware] Attempting to read AuthTokenService...'); // Log 2: Before read
25+
print(
26+
'[AuthMiddleware] Attempting to read AuthTokenService...'); // Log 2: Before read
2627
tokenService = context.read<AuthTokenService>();
27-
print('[AuthMiddleware] Successfully read AuthTokenService.'); // Log 3: After read
28+
print(
29+
'[AuthMiddleware] Successfully read AuthTokenService.'); // Log 3: After read
2830
} catch (e, s) {
29-
print('[AuthMiddleware] FAILED to read AuthTokenService: $e\n$s'); // Log Error
31+
print(
32+
'[AuthMiddleware] FAILED to read AuthTokenService: $e\n$s'); // Log Error
3033
// Re-throw the error to be caught by the main error handler
3134
rethrow;
3235
}
3336
User? user; // Initialize user as null
3437

3538
// Extract the Authorization header
36-
print('[AuthMiddleware] Attempting to read Authorization header...'); // Log 4: Before header read
39+
print(
40+
'[AuthMiddleware] Attempting to read Authorization header...'); // Log 4: Before header read
3741
final authHeader = context.request.headers['Authorization'];
38-
print('[AuthMiddleware] Authorization header value: $authHeader'); // Log 5: Header value
42+
print(
43+
'[AuthMiddleware] Authorization header value: $authHeader'); // Log 5: Header value
3944

4045
if (authHeader != null && authHeader.startsWith('Bearer ')) {
4146
// Extract the token string
4247
final token = authHeader.substring(7); // Length of 'Bearer '
43-
print('[AuthMiddleware] Extracted Bearer token.'); // Log 6: Token extracted
48+
print(
49+
'[AuthMiddleware] Extracted Bearer token.'); // Log 6: Token extracted
4450
try {
45-
print('[AuthMiddleware] Attempting to validate token...'); // Log 7: Before validate
51+
print(
52+
'[AuthMiddleware] Attempting to validate token...'); // Log 7: Before validate
4653
// Validate the token using the service
4754
user = await tokenService.validateToken(token);
48-
print('[AuthMiddleware] Token validation returned: ${user?.id ?? 'null'}'); // Log 8: After validate
55+
print(
56+
'[AuthMiddleware] Token validation returned: ${user?.id ?? 'null'}'); // Log 8: After validate
4957
if (user != null) {
50-
print('[AuthMiddleware] Authentication successful for user: ${user.id}');
58+
print(
59+
'[AuthMiddleware] Authentication successful for user: ${user.id}');
5160
} else {
52-
print('[AuthMiddleware] Invalid token provided (validateToken returned null).');
61+
print(
62+
'[AuthMiddleware] Invalid token provided (validateToken returned null).');
5363
// Optional: Could throw UnauthorizedException here if *all* routes
5464
// using this middleware strictly require a valid token.
5565
// However, providing null allows routes to handle optional auth.
@@ -62,7 +72,8 @@ Middleware authenticationProvider() {
6272
user = null; // Keep user null if HtHttpException occurred
6373
} catch (e, s) {
6474
// Catch unexpected errors during validation
65-
print('[AuthMiddleware] Unexpected error during token validation: $e\n$s');
75+
print(
76+
'[AuthMiddleware] Unexpected error during token validation: $e\n$s');
6677
user = null; // Keep user null if unexpected error occurred
6778
}
6879
} else {
@@ -71,7 +82,8 @@ Middleware authenticationProvider() {
7182

7283
// Provide the User object (or null) into the context
7384
// This makes `context.read<User?>()` available downstream.
74-
print('[AuthMiddleware] Providing User (${user?.id ?? 'null'}) to context.'); // Log 9: Before provide
85+
print(
86+
'[AuthMiddleware] Providing User (${user?.id ?? 'null'}) to context.'); // Log 9: Before provide
7587
return handler(context.provide<User?>(() => user));
7688
};
7789
};

lib/src/services/jwt_auth_token_service.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ class JwtAuthTokenService implements AuthTokenService {
101101
if (subClaim is String) {
102102
userId = subClaim;
103103
print(
104-
'[validateToken] "sub" claim successfully cast to String: $userId',);
104+
'[validateToken] "sub" claim successfully cast to String: $userId',
105+
);
105106
} else if (subClaim != null) {
106107
print(
107108
'[validateToken] WARNING: "sub" claim is not a String. '
@@ -117,7 +118,8 @@ class JwtAuthTokenService implements AuthTokenService {
117118

118119
if (userId == null || userId.isEmpty) {
119120
print(
120-
'[validateToken] Token validation failed: Missing or empty "sub" claim.',);
121+
'[validateToken] Token validation failed: Missing or empty "sub" claim.',
122+
);
121123
// Throw specific exception for malformed token
122124
throw const BadRequestException(
123125
'Malformed token: Missing or empty subject claim.',

lib/src/services/simple_auth_token_service.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,14 @@ class SimpleAuthTokenService implements AuthTokenService {
4545
}
4646

4747
try {
48-
print('[SimpleAuthTokenService] Attempting to read user from repository...');
48+
print(
49+
'[SimpleAuthTokenService] Attempting to read user from repository...');
4950
final user = await _userRepository.read(userId);
5051
print('[SimpleAuthTokenService] User read successful: ${user.id}');
5152
return user;
5253
} on NotFoundException {
53-
print('[SimpleAuthTokenService] Validation failed: User ID $userId not found.');
54+
print(
55+
'[SimpleAuthTokenService] Validation failed: User ID $userId not found.');
5456
// Return null if user not found, mimicking successful validation
5557
// of a token for a non-existent user. The middleware handles this.
5658
return null;

routes/_middleware.dart

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,9 @@ Handler middleware(Handler handler) {
195195
print('[MiddlewareSetup] JwtAuthTokenService instantiated.'); // Updated log
196196
final verificationCodeStorageService =
197197
InMemoryVerificationCodeStorageService();
198-
print('[MiddlewareSetup] InMemoryVerificationCodeStorageService instantiated.'); // Added log
198+
print(
199+
'[MiddlewareSetup] InMemoryVerificationCodeStorageService instantiated.',
200+
); // Added log
199201
final authService = AuthService(
200202
userRepository: userRepository,
201203
authTokenService: authTokenService,
@@ -205,11 +207,23 @@ Handler middleware(Handler handler) {
205207
);
206208
print('[MiddlewareSetup] AuthService instantiated.'); // Added log
207209

208-
// Chain the providers and other middleware
210+
// ==========================================================================
211+
// MIDDLEWARE CHAIN
212+
// ==========================================================================
213+
// IMPORTANT: The order of middleware matters significantly!
214+
// Middleware is applied in layers (like an onion). A request flows "in"
215+
// through the chain, hits the route handler, and the response flows "out".
216+
// Providers must be added *before* the middleware/handlers that read them.
217+
// Error handlers should typically be placed late in the "request" phase
218+
// (or early in the "response" phase) to catch errors from upstream.
219+
// ==========================================================================
209220
return handler
210-
// --- Request ID Provider ---
211-
// Generate a unique ID for each request and provide it via context.
212-
// Using the RequestId wrapper ensures type safety for context reads.
221+
// --- 1. Request ID Provider (Early Setup) ---
222+
// PURPOSE: Generates a unique ID (UUID v4) for each incoming request.
223+
// Provides `RequestId` instance via context.
224+
// ORDER: Placed *very early* so the ID is available for logging and
225+
// tracing throughout the entire request lifecycle in all
226+
// subsequent middleware and handlers.
213227
.use((innerHandler) {
214228
return (context) {
215229
final requestIdValue = uuid.v4();
@@ -218,35 +232,74 @@ Handler middleware(Handler handler) {
218232
return innerHandler(context.provide<RequestId>(() => requestId));
219233
};
220234
})
221-
// Provide the Model Registry Map
222-
.use(
223-
modelRegistryProvider,
224-
) // Uses the provider defined in model_registry.dart
225235

226-
// Provide each specific repository instance
236+
// --- 2. Model Registry Provider (Early Setup) ---
237+
// PURPOSE: Provides the `ModelRegistry` map for dynamic JSON
238+
// serialization/deserialization lookups.
239+
// ORDER: Needed by some repository clients or handlers dealing with
240+
// generic data types. Placed early, after RequestId.
241+
.use(modelRegistryProvider)
242+
243+
// --- 3. Repository Providers (Core Data Access) ---
244+
// PURPOSE: Provide singleton instances of all data repositories.
245+
// ORDER: These MUST be provided BEFORE any middleware or route handlers
246+
// that need to interact with data (e.g., AuthService,
247+
// authenticationProvider indirectly via AuthService/TokenService,
248+
// specific route logic).
227249
.use(provider<HtDataRepository<Headline>>((_) => headlineRepository))
228250
.use(provider<HtDataRepository<Category>>((_) => categoryRepository))
229251
.use(provider<HtDataRepository<Source>>((_) => sourceRepository))
230252
.use(provider<HtDataRepository<Country>>((_) => countryRepository))
253+
.use(provider<HtDataRepository<User>>(
254+
(_) => userRepository)) // Used by Auth services
231255
.use(provider<HtAppSettingsRepository>((_) => settingsRepository))
232-
// --- Provide Auth Dependencies ---
233-
.use(provider<HtDataRepository<User>>((_) => userRepository))
234-
.use(provider<HtEmailRepository>((_) => emailRepository))
235-
// Provide the AuthTokenService interface type
236-
.use(provider<AuthTokenService>((_) => authTokenService))
256+
.use(provider<HtEmailRepository>(
257+
(_) => emailRepository)) // Used by AuthService
258+
259+
// --- 4. Authentication Service Providers (Auth Logic Dependencies) ---
260+
// PURPOSE: Provide the core services needed for authentication logic.
261+
// ORDER: These MUST be provided BEFORE `authenticationProvider` and
262+
// any route handlers that perform authentication/authorization.
263+
// - `AuthTokenService` is read directly by `authenticationProvider`.
264+
// - `AuthService` uses several repositories and `AuthTokenService`.
265+
// - `VerificationCodeStorageService` is used by `AuthService`.
266+
// - `Uuid` is used by `AuthService` and `JwtAuthTokenService`.
267+
.use(provider<AuthTokenService>(
268+
(_) => authTokenService)) // Read by authenticationProvider
237269
.use(
238270
provider<VerificationCodeStorageService>(
239271
(_) => verificationCodeStorageService,
240272
),
241-
)
242-
.use(provider<AuthService>((_) => authService))
243-
// --- Provide UUID ---
244-
.use(provider<Uuid>((_) => uuid)) // Provide Uuid instance
273+
) // Read by AuthService
274+
.use(provider<AuthService>(
275+
(_) => authService)) // Reads other services/repos
276+
.use(provider<Uuid>((_) => uuid)) // Read by AuthService & TokenService
245277

246-
// --- Core Middleware ---
247-
// Apply authenticationProvider first (after providers)
278+
// --- 5. Authentication Middleware (User Context Population) ---
279+
// PURPOSE: Reads the `Authorization: Bearer <token>` header, validates
280+
// the token using `AuthTokenService`, and provides the
281+
// resulting `User?` object into the context.
282+
// ORDER: MUST come AFTER `AuthTokenService` is provided (which it reads).
283+
// Should come BEFORE any route handlers that need to know the
284+
// currently authenticated user (`context.read<User?>()`).
248285
.use(authenticationProvider())
249-
.use(requestLogger()) // Then basic request logging
250-
// Error handler should generally be last to catch all upstream errors
286+
287+
// --- 6. Request Logger (Logging) ---
288+
// PURPOSE: Logs details about the incoming request and outgoing response.
289+
// ORDER: Often placed late in the request phase / early in the response
290+
// phase. Placing it here logs the request *before* the handler
291+
// runs and the response *after* the handler (and error handler)
292+
// completes. Can access `RequestId` and potentially `User?`.
293+
.use(requestLogger())
294+
295+
// --- 7. Error Handler (Catch-All) ---
296+
// PURPOSE: Catches exceptions thrown by upstream middleware or route
297+
// handlers and converts them into standardized JSON error responses.
298+
// ORDER: MUST be placed *late* in the chain (typically last before the
299+
// actual handler is invoked by the framework, or first in the
300+
// response processing flow) so it can catch errors from
301+
// everything that came before it (providers, auth middleware,
302+
// route handlers). If placed too early, it won't catch errors
303+
// from middleware/handlers defined after it.
251304
.use(errorHandler());
252305
}

0 commit comments

Comments
 (0)