Summary
The documented configuration wires kv: env.KV, which becomes Better Auth's secondaryStorage. Better Auth's findSession reads secondary storage before the database and returns on a hit. Because Workers KV is eventually consistent, that means a signed out session can keep authenticating from another colo for up to 60 seconds.
The README warns about KV's 60 second minimum TTL breaking rate limiting. The same 60 second property applied to session reads has a more serious consequence, and I could not find it documented.
Why the ordering matters
From better-auth/dist/db/internal-adapter.mjs:
if (secondaryStorage) {
const sessionStringified = await secondaryStorage.get(token);
if (!sessionStringified &&
(!options.session?.storeSessionInDatabase ||
ctx.options.session?.preserveSessionInDatabase)) {
return null;
}
if (sessionStringified) {
// ... returns the session, database never consulted
}
}
Secondary storage short circuits the database on a hit. So the freshest store cannot correct the stale one; the stale one wins.
The KV side
Cloudflare documents KV as eventually consistent, with changes taking up to 60 seconds to propagate globally, and reads additionally served from a per colo edge cache whose default TTL is also 60 seconds. Their storage guidance positions KV for read heavy data that does not need immediate consistency.
Session revocation is the opposite of that: it is precisely a case where a delete must be visible immediately.
Sequence
- User signs out in colo A. Better Auth deletes the session row and the KV key.
- Colo B has the value cached, or has not received propagation.
- A request arrives at colo B.
findSession reads KV, gets a hit, returns the session.
- The request authenticates against a session the user revoked.
Window: up to roughly 60 seconds, per request colo.
Suggestion
Two options, either would help:
- Document it, alongside the existing rate limiting warning. Something like: KV secondary storage is not suitable when immediate session revocation is required, because it is consulted before the database.
- Let
kv opt out of session storage while still backing rate limits and verification tokens, so the useful parts of the integration do not force the session read path through an eventually consistent store.
I do not think there is a cacheTtl setting that fixes this, since the floor is 60 seconds either way, but I would be glad to be wrong.
Context
I hit this while moving a gateway off exactly this topology (KV first, database on a miss) after a review found the revocation window. The project now keeps sessions in D1 with no eventually consistent store in the read path, and pins the two settings that would reopen it, secondaryStorage and session.cookieCache, with regression tests.
Thanks for the package; the D1 and Hyperdrive wiring saved me real time even though I ended up not using the KV part.
Versions
better-auth-cloudflare 0.3.1
better-auth 1.7.2
Summary
The documented configuration wires
kv: env.KV, which becomes Better Auth'ssecondaryStorage. Better Auth'sfindSessionreads secondary storage before the database and returns on a hit. Because Workers KV is eventually consistent, that means a signed out session can keep authenticating from another colo for up to 60 seconds.The README warns about KV's 60 second minimum TTL breaking rate limiting. The same 60 second property applied to session reads has a more serious consequence, and I could not find it documented.
Why the ordering matters
From
better-auth/dist/db/internal-adapter.mjs:Secondary storage short circuits the database on a hit. So the freshest store cannot correct the stale one; the stale one wins.
The KV side
Cloudflare documents KV as eventually consistent, with changes taking up to 60 seconds to propagate globally, and reads additionally served from a per colo edge cache whose default TTL is also 60 seconds. Their storage guidance positions KV for read heavy data that does not need immediate consistency.
Session revocation is the opposite of that: it is precisely a case where a delete must be visible immediately.
Sequence
findSessionreads KV, gets a hit, returns the session.Window: up to roughly 60 seconds, per request colo.
Suggestion
Two options, either would help:
kvopt out of session storage while still backing rate limits and verification tokens, so the useful parts of the integration do not force the session read path through an eventually consistent store.I do not think there is a
cacheTtlsetting that fixes this, since the floor is 60 seconds either way, but I would be glad to be wrong.Context
I hit this while moving a gateway off exactly this topology (KV first, database on a miss) after a review found the revocation window. The project now keeps sessions in D1 with no eventually consistent store in the read path, and pins the two settings that would reopen it,
secondaryStorageandsession.cookieCache, with regression tests.Thanks for the package; the D1 and Hyperdrive wiring saved me real time even though I ended up not using the KV part.
Versions
better-auth-cloudflare0.3.1better-auth1.7.2