ae235d3b24
Settings / Profile / Devices listed every live session for the account, so a person who belongs to two workspaces saw all of them from either one, and "Log out all other devices" signed them out everywhere. Almost nothing in Twenty is account-wide, and a session belongs to the workspace its exchange selected, so neither the list nor the revocations are another workspace's business. ## What was wrong `currentUserSessions` called `findActiveSessionsForUser(user.id)` with no workspace filter. Both revoke paths were keyed on `userId` alone, so `revokeUserSession` could target another workspace's session by id and `revokeAllOtherUserSessions` cleared every workspace at once. Sweeping the other resolvers that take `@AuthUser()` turned up the same shape in the OAuth application authorizations added in #23678: `findActiveAuthorizationsForUser(userId)` and `revokeAuthorizationById({ authorizationId, userId })`. A grant made in one workspace was listed, and revocable, from another. Everything else already pairs `@AuthUser()` with `@AuthWorkspace()`. `client-config.resolver.ts` is the reference pattern. ## The fix Both resolvers now take the workspace from the auth context and pass it down, and the service methods are renamed to say so. `revokeAllSessionsForUser` keeps `workspaceId` optional on purpose: `auth.service.ts` uses it on password change, where clearing every workspace is the intended behaviour. Sessions with no workspace (the workspace-agnostic ones minted on the default subdomain, which exists to list workspaces and carry the auto-login window) now belong to no workspace's list and survive "log out all other devices". ## Verification - New integration spec built on Tim's membership of both apple and yc: the list stays disjoint, a cross-workspace revoke by id is refused and is a no-op, and revoking all other devices in one workspace leaves the other signed in - Mutation-checked by dropping the `workspaceId` from the query, which fails the isolation test while the two revoke tests still pass, confirming each assertion targets its own mechanism - 160 unit tests, 70 integration tests across the session and OAuth suites ## Also The devices button drops its danger accent for the plain small variant, matching Deactivate in `ObjectSettings.tsx`. ## Separate finding, not fixed here `request.ip` resolves to an internal address behind the Cloudflare / nginx chain, which is why every row in the screenshot that prompted this showed the same RFC1918 address. That is an ingress configuration issue rather than an application one. It does not affect ClickHouse audit logs, which store no IP, but it does affect the two OAuth rate limiters that key on `req.ip`. --- _Generated by [Claude Code](https://claude.ai/code/session_01C6nCVbcb5ZZrz67uvqvMWF)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23843?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->