Skip to content

Commit 156b6ff

Browse files
committed
fix(api): implement production-ready cors in error handler
The errorHandler middleware was not adding CORS headers to error responses in a production-ready way. This caused browsers to block client-side applications from reading the response body, resulting in generic network errors instead of specific API error messages. This change refactors the errorHandler to use an environment-aware helper function. It now checks for a `CORS_ALLOWED_ORIGIN` environment variable for production and falls back to allowing any `localhost` origin for development. This aligns the error response behavior with the main CORS middleware and the project's documentation, fixing the bug.
1 parent 95ac0af commit 156b6ff

File tree

2 files changed

+26
-9
lines changed

2 files changed

+26
-9
lines changed

lib/src/config/environment_config.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,11 @@ abstract final class EnvironmentConfig {
7979
/// The value is read from the `ENV` environment variable.
8080
/// Defaults to 'production' if the variable is not set.
8181
static String get environment => _env['ENV'] ?? 'production';
82+
83+
/// Retrieves the allowed CORS origin from the environment.
84+
///
85+
/// The value is read from the `CORS_ALLOWED_ORIGIN` environment variable.
86+
/// This is used to configure CORS for production environments.
87+
/// Returns `null` if the variable is not set.
88+
static String? get corsAllowedOrigin => _env['CORS_ALLOWED_ORIGIN'];
8289
}

lib/src/middlewares/error_handler.dart

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import 'dart:io';
55

66
import 'package:dart_frog/dart_frog.dart';
7+
import 'package:ht_api/src/config/environment_config.dart';
78
import 'package:ht_shared/ht_shared.dart';
89
import 'package:json_annotation/json_annotation.dart';
910

@@ -108,15 +109,24 @@ Response _jsonErrorResponse({
108109
HttpHeaders.contentTypeHeader: 'application/json',
109110
};
110111

111-
// Add CORS headers to error responses to allow the client to read them.
112-
// This logic mirrors the behavior of `shelf_cors_headers` for development.
113-
final origin = context.request.headers['Origin'];
114-
if (origin != null) {
115-
// A simple check for localhost development environments.
116-
// For production, this should be a more robust check against a list
117-
// of allowed origins from environment variables.
118-
if (Uri.tryParse(origin)?.host == 'localhost') {
119-
headers[HttpHeaders.accessControlAllowOriginHeader] = origin;
112+
// Add CORS headers to error responses. This logic is environment-aware.
113+
// In production, it uses a specific origin from `CORS_ALLOWED_ORIGIN`.
114+
// In development (if the variable is not set), it allows any localhost.
115+
final requestOrigin = context.request.headers['Origin'];
116+
if (requestOrigin != null) {
117+
final allowedOrigin = EnvironmentConfig.corsAllowedOrigin;
118+
119+
var isOriginAllowed = false;
120+
if (allowedOrigin != null) {
121+
// Production: Check against the specific allowed origin.
122+
isOriginAllowed = (requestOrigin == allowedOrigin);
123+
} else {
124+
// Development: Allow any localhost origin.
125+
isOriginAllowed = (Uri.tryParse(requestOrigin)?.host == 'localhost');
126+
}
127+
128+
if (isOriginAllowed) {
129+
headers[HttpHeaders.accessControlAllowOriginHeader] = requestOrigin;
120130
headers[HttpHeaders.accessControlAllowMethodsHeader] =
121131
'GET, POST, PUT, DELETE, OPTIONS';
122132
headers[HttpHeaders.accessControlAllowHeadersHeader] =

0 commit comments

Comments
 (0)