-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathrefresh-session-cookie.ts
More file actions
75 lines (67 loc) · 2.61 KB
/
Copy pathrefresh-session-cookie.ts
File metadata and controls
75 lines (67 loc) · 2.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* Refresh the Better Auth session cookie cache after a mutation.
*
* Better Auth caches the session payload (session + user fields) in a signed
* cookie (`session.cookieCache`) so subsequent `/get-session` requests can be
* answered without hitting the database. When we mutate session-bearing rows
* directly (e.g. `sessionTable.activeOrganizationId`, `userTable.shouldOnboard`,
* `userTable.currentOnboardingStep`), the cookie cache becomes stale and
* downstream guards (RequireOnboarding) will read the OLD values until the
* cache expires, causing redirect loops.
*
* This helper re-reads the live session + user from the database via
* `ctx.context.internalAdapter.findSession` and re-issues the signed session
* cookie via Better Auth's `setSessionCookie` helper, which internally calls
* `setCookieCache`. After this runs, the very next request will see the fresh
* session values.
*
* Reference: better-auth v1.4.x `src/cookies/index.ts` (setSessionCookie /
* setCookieCache) and `src/api/routes/update-user.ts` which uses the same
* pattern after `internalAdapter.updateUser`.
*/
// ** import lib
import { setSessionCookie } from "better-auth/cookies";
// ** import logs
import { logger } from "@repo/logs";
// ** import types
import type { GenericEndpointContext } from "better-auth";
/**
* Re-read the session for the current request from the database and refresh
* the signed session cookie (including the cookie cache).
*
* Safe to call from any authenticated endpoint handler (`use: [sessionMiddleware]`).
* No-op if no session is attached to the request context.
*/
export async function refreshSessionCookie(
ctx: GenericEndpointContext,
): Promise<void> {
try {
const currentSession = ctx.context.session as
| { session?: { token?: string } }
| null
| undefined;
const sessionToken = currentSession?.session?.token;
if (!sessionToken) {
// No session on this request - nothing to refresh.
return;
}
const fresh = await ctx.context.internalAdapter.findSession(sessionToken);
if (!fresh) {
// Session was deleted concurrently - leave cookie alone, /get-session
// will clean it up on the next request.
return;
}
await setSessionCookie(ctx, {
session: fresh.session,
user: fresh.user,
});
} catch (error) {
// Never let cookie refresh failures break the mutation - log and continue.
// Worst case: cookie cache is stale for `cookieCache.maxAge` seconds.
logger.error(
`Failed to refresh session cookie cache: ${
error instanceof Error ? error.message : String(error)
}`,
);
}
}