Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default defineConfig({
{ label: 'Email Service', link: '/api-server/features/email-service' },
{ label: 'Data Management', link: '/api-server/features/data-management-api' },
{ label: 'RBAC', link: '/api-server/features/rbac' },
{ label: 'Rate Limiting', link: '/api-server/features/rate-limiting' },
],
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The following table lists the most common error codes and their corresponding HT
| `403` | `forbidden` | The authenticated user does not have the necessary permissions to perform the requested action. |
| `404` | `notFound` | The requested resource (e.g., an item with a specific ID) could not be found. |
| `409` | `conflict` | The request could not be completed due to a conflict with the current state of the resource. |
| `429` | `forbidden` | The request was rejected because the client has exceeded the rate limit. |
| `500` | `serverError` | A generic error occurred on the server. |
| `500` | `operationFailed` | A specific, known operation failed on the server for an unexpected reason. |
| `500` | `unknownError` | An unexpected and unhandled error occurred on the server. |
Expand Down
1 change: 1 addition & 0 deletions src/content/docs/api-server/architecture/middleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Here is the typical execution order for a request to a protected data endpoint l

3. **Data Route Middleware (`/routes/api/v1/data/_middleware.dart`)**
- **Require Authentication:** Checks if a `User` object exists in the context. If not, it immediately aborts the request with a `401 Unauthorized` error.
- **Rate Limiting:** If the user is not an admin, it applies a rate limit based on the user's ID. If the limit is exceeded, it aborts with a `429 Too Many Requests` error.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's an inconsistency in the documentation regarding which roles bypass rate limiting. This file states that only admin users bypass the limit, while the new rate-limiting.mdx page says that both admin and publisher roles bypass it.

Assuming rate-limiting.mdx has the correct information, this line should be updated to include the publisher role for consistency.

    -   **Rate Limiting:** If the user is not an admin or publisher, it applies a rate limit based on the user's ID. If the limit is exceeded, it aborts with a `429 Too Many Requests` error.

- **Model Validation:** Validates the `?model=` query parameter and injects the corresponding `ModelConfig` into the context.
- **Authorization:** Checks if the authenticated user has the required permissions to perform the requested action on the specified model. If not, it aborts with a `403 Forbidden` error.

Expand Down
4 changes: 4 additions & 0 deletions src/content/docs/api-server/features/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ The API server is packed with features designed for a modern, scalable news appl
Explore the granular permission system that secures the API.
[Read more →](/docs/api-server/features/rbac)
</Card>
<Card title="Rate Limiting" icon="timer">
Learn how the API prevents abuse with built-in request rate limiting.
[Read more &rarr;](/docs/api-server/features/rate-limiting)
</Card>
</CardGrid>
46 changes: 46 additions & 0 deletions src/content/docs/api-server/features/rate-limiting.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: 'Feature: Rate Limiting'
description: An overview of the API server's built-in rate limiting to prevent abuse and ensure stability.
---

import { Aside } from '@astrojs/starlight/components';

To ensure the stability and security of the API, the server includes a built-in rate-limiting feature. This system automatically tracks the number of requests from a given source and blocks further requests if they exceed a configured limit within a specific time window.

This is crucial for preventing abuse, such as brute-force attacks on the authentication endpoints or overly aggressive polling of the data API.

### How It Works

The rate-limiting mechanism is implemented as a middleware that runs on specific routes. It uses the client's IP address as the primary identifier for tracking requests.

There are two distinct rate-limiting configurations applied to different parts of the API:

1. **Sensitive Endpoint Limiting:**
- **Target:** The `/api/v1/auth/request-code` endpoint.
- **Purpose:** This endpoint is more sensitive as it triggers an email to be sent. The rate limit here is intentionally strict to prevent spamming users with verification codes.
- **Default Limit:** 3 requests per 24 hours.

2. **General Data API Limiting:**
- **Target:** All endpoints under `/api/v1/data`.
- **Purpose:** This provides a generous limit for general application usage while still protecting the server from excessively frequent requests from a single client.
- **Default Limit:** 1000 requests per 60 minutes.

<Aside type="note" title="Bypassing Limits">
Users with the `admin` or `publisher` role automatically bypass all rate limits, ensuring that administrative tasks are never blocked.
</Aside>

### Configuration

These limits are configurable via environment variables, allowing you to adjust them for your specific production needs. For details, see the [Configure Environment Variables](/docs/api-server/guides/configure-environment-variables) guide.

### Error Response

When a client exceeds a rate limit, the API will respond with an HTTP `429 Too Many Requests` status code and the following error payload:

```json
{
"error": {
"code": "forbidden",
"message": "You have made too many requests. Please try again later."
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,11 @@ These variables have default values but can be customized for your environment.

#### `JWT_EXPIRY_HOURS`
The duration, in hours, for which a JWT is valid.
- **Default:** `1` (1 hour)
- **Default:** `720` (1 month)

<Aside type="caution" title="Cost & User Experience Implications">
Lowering this value will require users to sign in more often. Each sign-in sends a verification code via email, which may incur costs from your email provider (e.g., SendGrid). The default value of one month is recommended to balance security with a smooth user experience and predictable costs.
</Aside>

#### `CORS_ALLOWED_ORIGIN`
The specific origin URL of your web client (e.g., the Flutter Web Dashboard) that is allowed to make requests to this API. This is **required for production** to prevent Cross-Origin Resource Sharing (CORS) errors.
Expand All @@ -125,3 +129,21 @@ For more details on CORS, see the [Configure CORS](/docs/api-server/guides/confi
The base URL for the SendGrid API.
- **Default:** `https://api.sendgrid.com`
- **EU Accounts:** If your SendGrid account is based in the EU, you may need to set this to `https://api.eu.sendgrid.com`.

#### `RATE_LIMIT_REQUEST_CODE_LIMIT`
The number of times a user can request a sign-in code within the defined window.
- **Default:** `3`

<Aside type="caution" title="Security & Cost Implications">
This limit is a critical security measure to prevent abuse of the email sign-in system. Increasing this value could allow a malicious actor to trigger a large number of emails to a specific address, potentially incurring significant costs from your email provider. It is strongly recommended to keep this value low.
</Aside>

#### `RATE_LIMIT_REQUEST_CODE_WINDOW_HOURS`
The time window, in hours, for the sign-in code request limit.
- **Default:** `24`

#### `RATE_LIMIT_DATA_API_LIMIT`
The number of general data API requests a user can make within the defined window.
- **Default:** `1000`

#### `RATE_LIMIT_DATA_API_WINDOW_MINUTES`