You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(plugin-auth): resolve the kernel cache at counting time, not at init (#4772)
`AuthPlugin.init()` probed `getServiceAsync('cache')` and froze the answer for
the life of the process. It runs BEFORE `CacheServicePlugin` registers the
service (21ms earlier in a showcase cold start), so the probe resolved
`undefined` in deployments that have a cache configured — and the warning it
printed told the operator to provision Redis for a problem they did not have.
The misdiagnosis was the visible half. The real defect: better-auth is built
lazily but from the config captured at init, so the "no cache" conclusion was
permanent. Rate-limit counters never reached the shared store even after it
came up, meaning a multi-node deployment's limits were never enforced globally
(ADR-0069 D2 declared a capability the runtime did not deliver).
`createLazyCacheRateLimitStorage()` implements better-auth's
`rateLimit.customStorage` and resolves the `cache` service when a counter is
actually consumed — strictly after `kernel:ready`, therefore independent of
plugin start order. The warning is kept but now fires only when a counter
genuinely has nowhere shared to count, once per process; without a cache the
limit is still enforced, in-process (degraded, never disabled).
Deliberately `customStorage`, not `secondaryStorage`: the latter also moves the
session of record into the cache (`createSession` skips the `sys_session` row,
`findSession` answers from the snapshot without reading the database), which
silently disables the ADR-0069 D4 session controls — idle timeout, absolute max
and concurrent cap all revoke by writing that row. The cache is therefore no
longer auto-bound as `secondaryStorage`; `cacheSecondaryStorage` is exported for
a host that opts into that trade knowingly. Where the session of record belongs
is #4785.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Br2xsJsczFsTR9bvbh2Ny
|**P2** (D4/D5) | 🟡 **mostly implemented**| Session idle/absolute/concurrent (`enforceSessionControls`/`enforceConcurrentCap`), the **global** IP allow-list (`isClientIpAllowed`, `auth.allowed_ip_ranges`), and the **shared multi-node rate-limit + session store** (better-auth `secondaryStorage` bound to the kernel cache service via `cacheSecondaryStorage`; shared iff the cache is — Redis adapter in a cluster) are landed. **Remaining:** per-org `sys_organization.allowed_ip_ranges` (+ optional `sys_user.allowed_ip_ranges` override) — tracked in #2571. |
142
+
| **P2** (D4/D5) | 🟡 **mostly implemented** | Session idle/absolute/concurrent (`enforceSessionControls`/`enforceConcurrentCap`), the **global** IP allow-list (`isClientIpAllowed`, `auth.allowed_ip_ranges`), and the **shared multi-node rate-limit counters** (better-auth `rateLimit.customStorage` fed by the kernel cache service through `createLazyCacheRateLimitStorage`; shared iff the cache is — Redis adapter in a cluster) are landed. **Correction (#4772):** this row previously claimed a shared **session** store via `secondaryStorage` as landed. It was not: the binding was taken in `AuthPlugin.init()`, which runs *before* `CacheServicePlugin` registers `cache`, so it never fired in the standard composition — and the counters it was supposed to share never reached the cache either. The counters now ride `rateLimit.customStorage`, resolved at counting time. The **session** half is deliberately NOT auto-wired: better-auth answers `findSession` from a `secondaryStorage` snapshot without reading the database, while D4 above revokes by writing the `sys_session` row, so a cache-backed session store silently disables idle-timeout / absolute-max / concurrent-cap enforcement. `cacheSecondaryStorage` remains exported for a host that opts into that trade knowingly. **Remaining:** per-org `sys_organization.allowed_ip_ranges` (+ optional `sys_user.allowed_ip_ranges` override) — tracked in #2571; the session-store question — tracked in #4785. |
143
143
|**P2/P3** (D6) | 🟡 partial | Generic OIDC RP wired (`genericOAuth`/`sso`); admin OIDC **trust-list settings UI** still env/`sys_sso_provider`-only. |
144
144
|**P3** (SAML, broader social) | 🟡 partial |`@better-auth/sso` present (SAML now better-auth-native — see Addendum); broader settings-driven social providers pending. |
0 commit comments