feat(auth): shared cross-node rate-limit + session store via cache service (ADR-0069 D2)#2572
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 10 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…rvice (ADR-0069 D2) Multi-node deployments rate-limited per-process — better-auth's default rateLimit store is in-memory, so each node counted independently and an attacker could rotate nodes to bypass the limit. - new cacheSecondaryStorage(cache): ICacheService → better-auth SecondaryStorage (get maps undefined→null; set forwards TTL; delete). - AuthManager: accept secondaryStorage; when present, wire it into the betterAuth config AND flip rateLimit.storage to 'secondary-storage' so counters go to the shared store (works with or without an explicit rateLimit config block). - AuthPlugin: bind the kernel `cache` service as secondaryStorage when registered (shared across nodes iff the cache is — Redis adapter in a cluster, memory single-node). Logs a warning when no cache service is present so a multi-node deploy isn't silently per-process (ADR-0049 honesty). Atomicity note (in code + changeset): ICacheService has no atomic increment, so the get→set counter path can slightly over-count under high concurrency — acceptable for a rate limiter and strictly better than independent per-node counters; a future cache adapter exposing INCR can add an increment method for exact counting. Tests: adapter round-trip / miss→null / TTL / delete; auth-manager flips rateLimit.storage to secondary-storage (with and without an explicit rateLimit block). plugin-auth 6 files green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT
…tered)
The kernel registers `cache` as an ASYNC service, so the sync
`ctx.getService('cache')` throws "Service 'cache' is async - use await"
at AuthPlugin.init — breaking bootstrap (caught by the dogfood gate,
which boots the real kernel; unit tests don't). Resolve via
`getServiceAsync` wrapped in try/catch so an absent/not-ready cache
falls to the per-process warning path instead of crashing boot.
Verified: dogfood 36 files / 181 tests green; plugin-auth units green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT
) Reconcile the ADR-0069 implementation matrix now that the shared secondaryStorage-backed rate-limit store has landed in this PR — moves it from 'Remaining P2' to 'landed', leaving only per-org IP ranges (#2571) as the remaining P2 gap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT
os-zhuang
force-pushed
the
claude/adr-0069-p2-shared-ratelimit
branch
from
July 4, 2026 10:27
07bfd0b to
a8f57a4
Compare
18 tasks
os-zhuang
marked this pull request as ready for review
July 4, 2026 10:39
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概述
补上 ADR-0069 D2 的多节点缺口(P2)。这是你选定的部署形态(多节点 + 应用层限流)对应的那一项。
问题:better-auth 默认 rateLimit store 是进程内内存。多节点部署下每个节点各算各的计数,攻击者轮流打不同节点就能绕过限流——一个真实的多节点安全洞。
设计选择(评估后)
评估发现:runtime 里那套同步
RateLimiter(security/rate-limit.ts)根本没有调用点(死代码),实际 auth 限流走的是 better-auth 原生 rateLimit。better-auth 支持storage: 'secondary-storage'+secondaryStorage(KV)。框架 cache service 已有 Redis adapter。选择:把 better-auth
secondaryStorage接到框架 cache service(而非新增 DB 对象/表或硬接 Redis):变更
cacheSecondaryStorage(cache)——ICacheService→ better-authSecondaryStorage(get把undefined→null;set转发 TTL;delete)。AuthManager:接受secondaryStorage;存在时注入 betterAuth config 并把rateLimit.storage翻成'secondary-storage'(有无显式 rateLimit 块都工作)。AuthPlugin:cache service 存在时绑为 secondaryStorage;不存在时 warn。原子性说明(代码 + changeset 已诚实标注):
ICacheService无原子自增,高并发下 get→set 计数路径可能轻微超计——对限流可接受,且严格优于「各节点独立计数」;未来 cache adapter 暴露 INCR 可加increment方法做精确计数。验证
文档协调
本 PR 刻意不碰 ADR-0069(状态矩阵在评审中的 #2570 里)以避免冲突;两者合并后,ADR-0069 实现矩阵里「remaining P2: shared-store rate limiting」这条应移除——我会在后合并的那个 PR rebase 时顺手改掉。
剩余 P2(已开 issue,不在本 PR)
allowed_ip_ranges) — ADR-0069 D5 #2571(带「收紧 vs 替换」设计决策,我推荐 tighten-only/交集语义,避免提权面)。本次上线是多节点但非按租户锁 IP,故推后。关联
allowed_ip_ranges) — ADR-0069 D5 #2571(per-org IP,deferred)🤖 Generated with Claude Code
https://claude.ai/code/session_014y5kiH3aPLWtRRRGcVrXcT
Generated by Claude Code