How to use cookieCache alongside Organization Plugin? #576

Closed
opened 2026-03-13 07:54:11 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @ilrein on GitHub (Jan 20, 2025).

My app uses an orgGuard which prevents users from accessing the dashboard unless you have an active organization. It redirects you to an onboarding flow instead. After, it creates an org, and assigns it to the user:

    const organization = await auth.api.createOrganization({
      body: {
        name: payload.teamName,
        slug: slugify(payload.teamName),
        userId: user.id,
        teamId: payload.teamId,
      },
    });

    if (!organization) {
      return c.json({ error: "Failed to create organization" }, 500);
    }

    auth.api.setActiveOrganization({
      headers: c.req.raw.headers,
      body: {
        organizationId: organization.id,
      },
    });

This all works perfectly, except when I enable cookieCache. Then, the frontend, when it refetches the session, seems to read from the previous session, rather than the newly updated one. How can I get the best of both worlds here?

Originally created by @ilrein on GitHub (Jan 20, 2025). My app uses an `orgGuard` which prevents users from accessing the dashboard unless you have an active organization. It redirects you to an onboarding flow instead. After, it creates an org, and assigns it to the user: ``` const organization = await auth.api.createOrganization({ body: { name: payload.teamName, slug: slugify(payload.teamName), userId: user.id, teamId: payload.teamId, }, }); if (!organization) { return c.json({ error: "Failed to create organization" }, 500); } auth.api.setActiveOrganization({ headers: c.req.raw.headers, body: { organizationId: organization.id, }, }); ``` This all works perfectly, except when I enable cookieCache. Then, the frontend, when it refetches the session, seems to read from the previous session, rather than the newly updated one. How can I get the best of both worlds here?
Author
Owner

@kdcokenny commented on GitHub (May 16, 2025):

Not cookieCache but here's my secondaryStorage implementation for the getFullOrganization GET request:

...
		hooks: {
			before: async (data) => {
				// We have to cast the type of data because as of better-auth v1.2.5 their inferred
				// types are incorrect.
				const {
					path,
					query,
					headers,
					context: {
						options: { secondaryStorage },
					},
				} = data as {
					path: string;
					query: Record<string, string | undefined>;
					headers: Headers;
					context: {
						options: {
							secondaryStorage: {
								get: (k: string) => Promise<string | null>;
								set: (k: string, v: string) => Promise<void>;
								delete: (k: string) => Promise<void>;
							};
						};
						returned: unknown;
					};
				};
				switch (path) {
					case "/organization/get-full-organization": {
						// We use this hook to cache the organization data in KV storage
						// https://github.com/better-auth/better-auth/issues/1251
						const { organizationSlug } = query as {
							organizationSlug: string;
						};

						const cachedOrganization = await secondaryStorage.get(
							`full-organization-${organizationSlug}`,
						);

						if (cachedOrganization) return JSON.parse(cachedOrganization);
						break;
					}
				}
			},
			after: async (data) => {
				// We have to cast the type of data because as of better-auth v1.2.5 their inferred
				// types are incorrect.
				const {
					path,
					context: {
						returned,
						options: { secondaryStorage },
					},
				} = data as {
					path: string;
					context: {
						options: {
							secondaryStorage: {
								get: (k: string) => Promise<string | null>;
								set: (k: string, v: string) => Promise<void>;
								delete: (k: string) => Promise<void>;
							};
						};
						returned: Organization | { message: string };
					};
				};

				switch (path) {
					// We use this hook to cache the organization data in KV storage
					// https://github.com/better-auth/better-auth/issues/1251
					case "/organization/get-full-organization": {
						// break out if error
						if ("message" in returned) break;

						const organization = returned as Organization;
						secondaryStorage.set(
							`full-organization-${organization.slug}`,
							JSON.stringify(organization),
						);
						break;
					}
					// We use this hook to cache the organization data in KV storage
					// https://github.com/better-auth/better-auth/issues/1251
					case "/organization/update": {
						// break out if error
						if ("message" in returned) break;
						const organization = returned as Organization;

						await secondaryStorage.delete(
							`full-organization-${organization.slug}`,
						);
						break;
					}
					// We use this hook to cache the organization data in KV storage
					// https://github.com/better-auth/better-auth/issues/1251
					case "/organization/delete": {
						// break out if error
						if ("message" in returned) break;

						const organization = returned as Organization;
						await secondaryStorage.delete(
							`full-organization-${organization.slug}`,
						);
						break;
					}
				}
				return data;
			},
		},
@kdcokenny commented on GitHub (May 16, 2025): Not cookieCache but here's my secondaryStorage implementation for the getFullOrganization GET request: ```ts ... hooks: { before: async (data) => { // We have to cast the type of data because as of better-auth v1.2.5 their inferred // types are incorrect. const { path, query, headers, context: { options: { secondaryStorage }, }, } = data as { path: string; query: Record<string, string | undefined>; headers: Headers; context: { options: { secondaryStorage: { get: (k: string) => Promise<string | null>; set: (k: string, v: string) => Promise<void>; delete: (k: string) => Promise<void>; }; }; returned: unknown; }; }; switch (path) { case "/organization/get-full-organization": { // We use this hook to cache the organization data in KV storage // https://github.com/better-auth/better-auth/issues/1251 const { organizationSlug } = query as { organizationSlug: string; }; const cachedOrganization = await secondaryStorage.get( `full-organization-${organizationSlug}`, ); if (cachedOrganization) return JSON.parse(cachedOrganization); break; } } }, after: async (data) => { // We have to cast the type of data because as of better-auth v1.2.5 their inferred // types are incorrect. const { path, context: { returned, options: { secondaryStorage }, }, } = data as { path: string; context: { options: { secondaryStorage: { get: (k: string) => Promise<string | null>; set: (k: string, v: string) => Promise<void>; delete: (k: string) => Promise<void>; }; }; returned: Organization | { message: string }; }; }; switch (path) { // We use this hook to cache the organization data in KV storage // https://github.com/better-auth/better-auth/issues/1251 case "/organization/get-full-organization": { // break out if error if ("message" in returned) break; const organization = returned as Organization; secondaryStorage.set( `full-organization-${organization.slug}`, JSON.stringify(organization), ); break; } // We use this hook to cache the organization data in KV storage // https://github.com/better-auth/better-auth/issues/1251 case "/organization/update": { // break out if error if ("message" in returned) break; const organization = returned as Organization; await secondaryStorage.delete( `full-organization-${organization.slug}`, ); break; } // We use this hook to cache the organization data in KV storage // https://github.com/better-auth/better-auth/issues/1251 case "/organization/delete": { // break out if error if ("message" in returned) break; const organization = returned as Organization; await secondaryStorage.delete( `full-organization-${organization.slug}`, ); break; } } return data; }, }, ```
Author
Owner

@dosubot[bot] commented on GitHub (Aug 15, 2025):

Hi, @ilrein. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You reported that enabling cookieCache caused the frontend to fetch an outdated session after setting the active organization.
  • You noted that restricting dashboard access with orgGuard worked fine without cookieCache.
  • kdcokenny provided a detailed secondaryStorage implementation with hooks to cache organization data in KV storage.
  • This solution helps keep the session data up-to-date and addresses the stale session issue when using cookieCache.
  • The issue appears to be resolved with this approach.

Next Steps:

  • Please confirm if this solution is still relevant and working with the latest version of better-auth by commenting here.
  • If no further updates are provided, I will automatically close this issue in 7 days.

Thanks for your understanding and contribution!

@dosubot[bot] commented on GitHub (Aug 15, 2025): Hi, @ilrein. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary:** - You reported that enabling cookieCache caused the frontend to fetch an outdated session after setting the active organization. - You noted that restricting dashboard access with orgGuard worked fine without cookieCache. - kdcokenny provided a detailed secondaryStorage implementation with hooks to cache organization data in KV storage. - This solution helps keep the session data up-to-date and addresses the stale session issue when using cookieCache. - The issue appears to be resolved with this approach. **Next Steps:** - Please confirm if this solution is still relevant and working with the latest version of better-auth by commenting here. - If no further updates are provided, I will automatically close this issue in 7 days. Thanks for your understanding and contribution!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#576