[PR #8803] feat: enhance api-key rate limit system #25124

Open
opened 2026-04-15 22:43:58 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/8803
Author: @ping-maxwell
Created: 3/27/2026
Status: 🔄 Open

Base: nextHead: feat/improve-api-key-ratelimits


📝 Commits (4)

  • 5fbad86 init
  • 716531d Merge branch 'main' into feat/improve-api-key-ratelimits
  • c3f9e5d Merge branch 'main' into feat/improve-api-key-ratelimits
  • 84e04da Merge branch 'main' into feat/improve-api-key-ratelimits

📊 Changes

14 files changed (+260 additions, -37 deletions)

View changed files

📝 docs/content/docs/plugins/api-key/advanced.mdx (+17 -10)
📝 docs/content/docs/plugins/api-key/index.mdx (+4 -4)
📝 docs/content/docs/plugins/api-key/reference.mdx (+13 -7)
📝 packages/api-key/src/adapter.ts (+4 -0)
📝 packages/api-key/src/api-key.test.ts (+3 -0)
packages/api-key/src/rate-limit.test.ts (+135 -0)
📝 packages/api-key/src/rate-limit.ts (+28 -12)
📝 packages/api-key/src/routes/create-api-key.ts (+10 -2)
📝 packages/api-key/src/routes/get-api-key.ts (+7 -0)
📝 packages/api-key/src/routes/list-api-keys.ts (+7 -0)
📝 packages/api-key/src/routes/update-api-key.ts (+17 -2)
📝 packages/api-key/src/schema.ts (+9 -0)
📝 packages/api-key/src/types.ts (+5 -0)
📝 packages/api-key/tsconfig.json (+1 -0)

📄 Description

closes https://github.com/better-auth/better-auth/issues/6035

Summary

Fixes API key rate limiting so it matches documented “N requests per time window” expectations (issue #6035).

Previously, limiting used lastRequest updated on every successful validation, so the effective period slid with activity and tryAgainIn was tied to time since the last successful request. That differed from a fixed window starting at the first request in a period (and from “10 per day” style wording).

What changed

Fixed-window algorithm

  • Each limit is enforced over [rateLimitWindowStart, rateLimitWindowStart + rateLimitTimeWindow).
  • rateLimitWindowStart is set when a window begins and does not advance on each allowed request.
  • requestCount increments within that window; when it reaches rateLimitMax, further validations return RATE_LIMITED until the window ends.
  • tryAgainIn is the time remaining until rateLimitWindowStart + rateLimitTimeWindow (window end), not until “idle for timeWindow since last request.”
  • Window rollover uses elapsed >= rateLimitTimeWindow so the boundary at exactly one window length is handled correctly (replacing the old strict > behavior on “time since last request”).

New / updated data

  • rateLimitWindowStart (Date | null) added to the API key model and plugin schema; null until the first validation in a new period (or after a reset).
  • lastRequest remains for auditing and is still updated on allowed validations when rate limiting applies.
  • Secondary storage: serialize/deserialize rateLimitWindowStart in the API key JSON cache.

Update API key behavior

  • Changing rateLimitEnabled, rateLimitTimeWindow, or rateLimitMax clears rateLimitWindowStart and resets requestCount to 0 so the next validation starts a fresh window under the new settings.

Breaking change

  • Database: add a nullable rateLimitWindowStart column (or equivalent) for the apikey table to match the plugin schema. Existing rows with NULL get a new window on the next successful verification.

Migration / upgrade notes for users

  1. Run your usual Better Auth migration flow so the schema includes rateLimitWindowStart.
  2. Expect different rate-limit timing vs the old behavior (by design): limits are per fixed window from window start, not per rolling idle period from lastRequest.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/8803 **Author:** [@ping-maxwell](https://github.com/ping-maxwell) **Created:** 3/27/2026 **Status:** 🔄 Open **Base:** `next` ← **Head:** `feat/improve-api-key-ratelimits` --- ### 📝 Commits (4) - [`5fbad86`](https://github.com/better-auth/better-auth/commit/5fbad86330008e28ba611ad1eeccde4ef8f6feaf) init - [`716531d`](https://github.com/better-auth/better-auth/commit/716531d484a50584823b02878d00fce77d6fb9cf) Merge branch 'main' into feat/improve-api-key-ratelimits - [`c3f9e5d`](https://github.com/better-auth/better-auth/commit/c3f9e5d2fae8de8b8a58a40b612bd5d2e7b395cb) Merge branch 'main' into feat/improve-api-key-ratelimits - [`84e04da`](https://github.com/better-auth/better-auth/commit/84e04dae3e9b80fbd2d2b1fa6e63b678cee264d1) Merge branch 'main' into feat/improve-api-key-ratelimits ### 📊 Changes **14 files changed** (+260 additions, -37 deletions) <details> <summary>View changed files</summary> 📝 `docs/content/docs/plugins/api-key/advanced.mdx` (+17 -10) 📝 `docs/content/docs/plugins/api-key/index.mdx` (+4 -4) 📝 `docs/content/docs/plugins/api-key/reference.mdx` (+13 -7) 📝 `packages/api-key/src/adapter.ts` (+4 -0) 📝 `packages/api-key/src/api-key.test.ts` (+3 -0) ➕ `packages/api-key/src/rate-limit.test.ts` (+135 -0) 📝 `packages/api-key/src/rate-limit.ts` (+28 -12) 📝 `packages/api-key/src/routes/create-api-key.ts` (+10 -2) 📝 `packages/api-key/src/routes/get-api-key.ts` (+7 -0) 📝 `packages/api-key/src/routes/list-api-keys.ts` (+7 -0) 📝 `packages/api-key/src/routes/update-api-key.ts` (+17 -2) 📝 `packages/api-key/src/schema.ts` (+9 -0) 📝 `packages/api-key/src/types.ts` (+5 -0) 📝 `packages/api-key/tsconfig.json` (+1 -0) </details> ### 📄 Description closes https://github.com/better-auth/better-auth/issues/6035 ## Summary Fixes API key rate limiting so it matches documented “N requests per time window” expectations ([issue #6035](https://github.com/better-auth/better-auth/issues/6035)). Previously, limiting used `lastRequest` updated on **every** successful validation, so the effective period slid with activity and `tryAgainIn` was tied to time since the **last** successful request. That differed from a **fixed window** starting at the first request in a period (and from “10 per day” style wording). ## What changed ### Fixed-window algorithm - Each limit is enforced over **`[rateLimitWindowStart, rateLimitWindowStart + rateLimitTimeWindow)`**. - **`rateLimitWindowStart`** is set when a window begins and **does not** advance on each allowed request. - **`requestCount`** increments within that window; when it reaches **`rateLimitMax`**, further validations return `RATE_LIMITED` until the window ends. - **`tryAgainIn`** is the time remaining until **`rateLimitWindowStart + rateLimitTimeWindow`** (window end), not until “idle for `timeWindow` since last request.” - Window rollover uses **`elapsed >= rateLimitTimeWindow`** so the boundary at exactly one window length is handled correctly (replacing the old strict `>` behavior on “time since last request”). ### New / updated data - **`rateLimitWindowStart`** (`Date | null`) added to the API key model and plugin schema; `null` until the first validation in a new period (or after a reset). - **`lastRequest`** remains for auditing and is still updated on allowed validations when rate limiting applies. - **Secondary storage**: serialize/deserialize `rateLimitWindowStart` in the API key JSON cache. ### Update API key behavior - Changing **`rateLimitEnabled`**, **`rateLimitTimeWindow`**, or **`rateLimitMax`** clears **`rateLimitWindowStart`** and resets **`requestCount`** to `0` so the next validation starts a fresh window under the new settings. # Breaking change - **Database**: add a nullable **`rateLimitWindowStart`** column (or equivalent) for the **`apikey`** table to match the plugin schema. Existing rows with `NULL` get a new window on the next successful verification. ## Migration / upgrade notes for users 1. Run your usual Better Auth migration flow so the schema includes **`rateLimitWindowStart`**. 2. Expect **different** rate-limit timing vs the old behavior (by design): limits are per **fixed window from window start**, not per rolling idle period from **`lastRequest`**. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-15 22:43:58 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#25124