@@ -11,24 +11,38 @@ import 'package:flutter_news_app_api_server_full_source_code/src/registry/model_
1111// Helper middleware for applying rate limiting to the data routes.
1212Middleware _dataRateLimiterMiddleware () {
1313 return (handler) {
14- return (context) {
15- final user = context.read <User >();
14+ return (context) async { // Made async because ipKeyExtractor is async
15+ final user = context.read <User ? >(); // Read nullable User
1616 final permissionService = context.read <PermissionService >();
1717
1818 // Users with the bypass permission are not rate-limited.
19- if (permissionService.hasPermission (
19+ if (user != null && permissionService.hasPermission (
2020 user,
2121 Permissions .rateLimitingBypass,
2222 )) {
2323 return handler (context);
2424 }
2525
26- // For all other users, apply the configured rate limit.
27- // The key is the user's ID, ensuring the limit is per-user.
26+ String ? key;
27+ // If user is null, it means it's a public route (as per _conditionalAuthenticationMiddleware)
28+ // In this case, use IP-based rate limiting.
29+ if (user == null ) {
30+ key = await ipKeyExtractor (context);
31+ } else {
32+ // Authenticated user: use user ID for rate limiting.
33+ key = user.id;
34+ }
35+
36+ // If a key cannot be extracted (e.g., no IP), bypass rate limiter.
37+ if (key == null || key.isEmpty) {
38+ return handler (context);
39+ }
40+
41+ // For all other users (or IPs for public routes), apply the configured rate limit.
2842 final rateLimitHandler = rateLimiter (
2943 limit: EnvironmentConfig .rateLimitDataApiLimit,
3044 window: EnvironmentConfig .rateLimitDataApiWindow,
31- keyExtractor: (context) async => context. read < User >().id,
45+ keyExtractor: (context) async => key, // Use the determined key
3246 )(handler);
3347
3448 return rateLimitHandler (context);
@@ -120,7 +134,9 @@ Middleware _conditionalAuthenticationMiddleware() {
120134 return requireAuthentication ()(handler)(context);
121135 } else {
122136 // If authentication is not required, simply pass the request through.
123- return handler (context);
137+ // Also provide a null User to the context for consistency,
138+ // so downstream middleware can always read User?
139+ return handler (context.provide <User ?>(() => null ));
124140 }
125141 };
126142 };
@@ -134,28 +150,31 @@ Handler middleware(Handler handler) {
134150 // the last .use() call in the chain represents the outermost middleware layer.
135151 // Therefore, the execution order for an incoming request is:
136152 //
137- // 1. `_conditionalAuthenticationMiddleware()`:
138- // - This runs first. It dynamically decides whether to apply
153+ // 1. `_modelValidationAndProviderMiddleware()`:
154+ // - This runs first. It validates the `?model=` query parameter and
155+ // provides the `ModelConfig` and `modelName` into the context.
156+ // - If model validation fails, it throws a `BadRequestException`.
157+ //
158+ // 2. `_conditionalAuthenticationMiddleware()`:
159+ // - This runs second. It dynamically decides whether to apply
139160 // `requireAuthentication()` based on the `ModelConfig` for the
140161 // requested model and HTTP method.
141162 // - If authentication is required and the user is not authenticated,
142163 // it throws an `UnauthorizedException`.
164+ // - If authentication is NOT required, it provides `User?` as `null`
165+ // to the context.
143166 //
144- // 2 . `_dataRateLimiterMiddleware()`:
145- // - This runs if authentication ( if required) passes .
146- // - It checks if the user has a bypass permission. If not, it applies
147- // the configured rate limit based on the user 's ID .
167+ // 3 . `_dataRateLimiterMiddleware()`:
168+ // - This runs third. It checks if the user has a bypass permission .
169+ // If not, it applies the configured rate limit based on the user's ID
170+ // (if authenticated) or the client 's IP address (if unauthenticated) .
148171 // - If the limit is exceeded, it throws a `ForbiddenException`.
149172 //
150- // 3. `_modelValidationAndProviderMiddleware()`:
151- // - This runs if rate limiting passes.
152- // - It validates the `?model=` query parameter and provides the
153- // `ModelConfig` and `modelName` into the context.
154- // - If model validation fails, it throws a `BadRequestException`.
155- //
156173 // 4. `authorizationMiddleware()`:
157- // - This runs if `_modelValidationAndProviderMiddleware()` passes.
158- // - It reads the `User`, `modelName`, and `ModelConfig` from the context.
174+ // - This runs fourth. It reads the `User` (which is guaranteed to be
175+ // non-null if authentication was required and passed) or `User?` (if
176+ // authentication was not required), `modelName`, and `ModelConfig`
177+ // from the context.
159178 // - It checks if the user has permission to perform the requested HTTP
160179 // method on the specified model based on the `ModelConfig` metadata.
161180 // - If authorization fails, it throws a ForbiddenException, caught by
@@ -172,7 +191,7 @@ Handler middleware(Handler handler) {
172191 //
173192 return handler
174193 .use (authorizationMiddleware ()) // Applied fourth (inner-most)
175- .use (_modelValidationAndProviderMiddleware ()) // Applied third
176- .use (_dataRateLimiterMiddleware ()) // Applied second
177- .use (_conditionalAuthenticationMiddleware ()); // Applied first (outermost)
194+ .use (_dataRateLimiterMiddleware ()) // Applied third
195+ .use (_conditionalAuthenticationMiddleware ()) // Applied second
196+ .use (_modelValidationAndProviderMiddleware ()); // Applied first (outermost)
178197}
0 commit comments