Skip to content

▚▚ fix(organization): refresh session cookie cache after accepting an invitation - #11281

Open
breken-ai wants to merge 1 commit into
better-auth:mainfrom
breken-ai:fix/accept-invitation-cookie-cache
Open

▚▚ fix(organization): refresh session cookie cache after accepting an invitation#11281
breken-ai wants to merge 1 commit into
better-auth:mainfrom
breken-ai:fix/accept-invitation-cookie-cache

Conversation

@breken-ai

@breken-ai breken-ai commented Sep 14, 2026

Copy link
Copy Markdown

Closes #11275.

With session.cookieCache.enabled: true, POST /organization/accept-invitation updates the session row in the database but never re-signs the session cookie from that write. The session_data cookie keeps the pre-accept snapshot, so GET /get-session (without disableCookieCache) returns the old activeOrganizationId — usually null — until the cache TTL expires, even though the row already holds the accepted organization.

Root cause

In acceptInvitation, adapter.setActiveOrganization(...) was called bare and its returned session discarded, so setSessionCookie never ran for the organization write. The single-team branch did call setSessionCookie, but from setActiveTeam's result and before the organization write, so that cookie also carried the stale activeOrganizationId. The /organization/set-active endpoint already re-signs the cookie from the setActiveOrganization result; this endpoint did not.

Fix

  • packages/better-auth/src/plugins/organization/routes/crud-invites.ts: sign the session cookie once, after the last active-pointer write, from the session returned by setActiveOrganization. The team branch keeps its setActiveTeam call but drops its early cookie write, so a single Set-Cookie carries both activeTeamId and activeOrganizationId.
  • .changeset/accept-invitation-cookie-cache.md: better-auth patch.

Tests

Not changed

  • /organization/create also calls setActiveOrganization / setActiveTeam without re-signing the cookie (crud-org.ts:274, 286). Same class of gap, but outside this issue; left for a separate change.

▚▚ Shipped by breken — self-healing software. This one's on us. breken.ai


Summary by cubic

Fixes acceptInvitation leaving the session cookie cache stale when session.cookieCache.enabled is on. The endpoint wrote the accepted organization to the session row but never re-signed the session cookie, so cached GET /get-session reads kept returning the old activeOrganizationId until the cache TTL expired. The cookie is now signed once after the final active-pointer write, so both activeTeamId and activeOrganizationId are reflected immediately.

Bug Fixes

  • Single-team invitations no longer write the cookie early; setActiveTeam still runs, but the session returned by setActiveOrganization produces one Set-Cookie with both values.
  • Adds a regression test covering the cookie-cache flow and a better-auth patch changeset.
  • Leaves /organization/create with the same gap for a separate change.

Closes #11275.

Written for commit 2044b5f. Summary will update on new commits.

Review in cubic

…vitation

`acceptInvitation` writes the accepted organization to the session row via
`adapter.setActiveOrganization` but never re-signs the session cookie from
that result. With `session.cookieCache.enabled`, every cached read
(`GET /get-session` without `disableCookieCache`) keeps returning the
pre-accept `activeOrganizationId` until the cache TTL expires, even though
the database is already updated. The single-team branch did sign the cookie,
but before the organization write, so it too carried the stale value.

Sign the cookie once, after the last active-pointer write, from the session
returned by `setActiveOrganization`, so both `activeTeamId` and
`activeOrganizationId` reach the cookie cache immediately.

Closes better-auth#11275

Claude-Session: https://claude.ai/code/session_0134ujLF81GyXsCByibLcYsz
@breken-ai
breken-ai requested review from a team as code owners September 14, 2026 02:04
@breken-ai
breken-ai requested review from gustavovalverde and removed request for a team September 14, 2026 02:04
@vercel

vercel Bot commented Sep 14, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the better-auth Team on Vercel.

A member of the Team first needs to authorize it.

@better-release

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

PR titles need to follow Conventional Commits format. It looks like your title needs a small adjustment.

Hint:

No release type found in pull request title "▚▚ fix(organization): refresh session cookie cache after accepting an invitation". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@better-release better-release Bot added the organization Org, teams, roles, permissions, admin, access control label Sep 14, 2026
@greptile-apps

greptile-apps Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR synchronizes the session cookie cache after invitation acceptance by signing one cookie from the final session returned by the active-organization update.

  • Removes the earlier cookie write from the single-team branch.
  • Signs the cached session after both active-pointer writes.
  • Adds a cookie-cache regression test for activeOrganizationId.
  • The regression test does not cover the changed team-enabled path or verify activeTeamId.

Confidence Score: 4/5

The implementation appears safe to merge, with a non-blocking test-coverage gap for the changed team-enabled cookie-cache path.

The final session update preserves the preceding active-team update and fixes the stale organization cache, but the regression test exercises only invitations without teams and therefore would not detect loss of activeTeamId.

Files Needing Attention: packages/better-auth/src/plugins/organization/routes/crud-invites.test.ts

Fix all with Greploop Fix All in Cursor Fix All in Codex Fix All in Claude Code

Reviews (1): Last reviewed commit: "fix(organization): refresh session cooki..." | Re-trigger Greptile

},
plugins: [organization()],
},
{ clientOptions: { plugins: [organizationClient()] } },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Team path remains untested

This regression test configures organization() without team support and creates the invitation without a teamId, so it never runs the changed single-team branch where the early cookie write was removed. It also checks only activeOrganizationId. As a result, a future regression that drops activeTeamId from the final cached session would not be caught. Please enable teams, accept an invitation for one team, and assert both cached active pointers.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Cursor Fix in Codex Fix in Claude Code

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/better-auth/src/plugins/organization/routes/crud-invites.test.ts">

<violation number="1" location="packages/better-auth/src/plugins/organization/routes/crud-invites.test.ts:923">
P2: Enable teams and accept a team invitation in this regression test, then assert both `activeOrganizationId` and `activeTeamId`. With team support disabled here, the test never exercises the changed team path or verifies that the final cached session preserves both active pointers.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

maxAge: 300,
},
},
plugins: [organization()],

@cubic-dev-ai cubic-dev-ai Bot Sep 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Enable teams and accept a team invitation in this regression test, then assert both activeOrganizationId and activeTeamId. With team support disabled here, the test never exercises the changed team path or verifies that the final cached session preserves both active pointers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/better-auth/src/plugins/organization/routes/crud-invites.test.ts, line 923:

<comment>Enable teams and accept a team invitation in this regression test, then assert both `activeOrganizationId` and `activeTeamId`. With team support disabled here, the test never exercises the changed team path or verifies that the final cached session preserves both active pointers.</comment>

<file context>
@@ -906,3 +906,79 @@ describe("invitation teamId must belong to the invitation's organization", async
+						maxAge: 300,
+					},
+				},
+				plugins: [organization()],
+			},
+			{ clientOptions: { plugins: [organizationClient()] } },
</file context>
Fix with cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

organization Org, teams, roles, permissions, admin, access control

Projects

None yet

Development

Successfully merging this pull request may close these issues.

organization.acceptInvitation never refreshes the session cookie after the active-organization write, leaving the cookie cache stale

1 participant