fix(plugin-multi-tenant): remove tenant cookie on logout (#10761)

### What?
- Removes the tenant cookie when the user logs out
- Prevents double redirect to globals when no tenant is selected

### Why?
There were a couple scenarios where the cookie and the tenant did not
match, ie if you logged into 1 tenant, and then out and then into
another tenant.
This commit is contained in:
Jarrod Flesch
2025-01-24 10:10:49 -05:00
committed by GitHub
parent d6ae07dec6
commit 22633a6de6
17 changed files with 223 additions and 128 deletions

View File

@@ -25,14 +25,16 @@ const Context = createContext<ContextType>({
export const TenantSelectionProviderClient = ({
children,
initialValue,
tenantCookie,
tenantOptions,
}: {
children: React.ReactNode
initialValue?: string
initialValue?: number | string
tenantCookie?: string
tenantOptions: OptionObject[]
}) => {
const [selectedTenantID, setSelectedTenantID] = React.useState<number | string>(
initialValue || SELECT_ALL,
const [selectedTenantID, setSelectedTenantID] = React.useState<number | string | undefined>(
initialValue,
)
const [preventRefreshOnChange, setPreventRefreshOnChange] = React.useState(false)
const { user } = useAuth()
@@ -49,11 +51,20 @@ export const TenantSelectionProviderClient = ({
document.cookie = 'payload-tenant=' + (value || '') + expires + '; path=/'
}, [])
const deleteCookie = React.useCallback(() => {
document.cookie = 'payload-tenant=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'
}, [])
const setTenant = React.useCallback<ContextType['setTenant']>(
({ id, refresh }) => {
if (id === undefined) {
setSelectedTenantID(SELECT_ALL)
setCookie(SELECT_ALL)
if (tenantOptions.length > 1) {
setSelectedTenantID(SELECT_ALL)
setCookie(SELECT_ALL)
} else {
setSelectedTenantID(tenantOptions[0]?.value)
setCookie(String(tenantOptions[0]?.value))
}
} else {
setSelectedTenantID(id)
setCookie(String(id))
@@ -62,7 +73,7 @@ export const TenantSelectionProviderClient = ({
router.refresh()
}
},
[setSelectedTenantID, setCookie, router, preventRefreshOnChange],
[setSelectedTenantID, setCookie, router, preventRefreshOnChange, tenantOptions],
)
React.useEffect(() => {
@@ -77,11 +88,26 @@ export const TenantSelectionProviderClient = ({
setTenant({ id: undefined, refresh: true })
}
}
}, [initialValue, setTenant, selectedTenantID, tenantOptions])
}, [tenantCookie, setTenant, selectedTenantID, tenantOptions, initialValue, setCookie])
React.useEffect(() => {
router.refresh()
}, [userID, router])
if (userID && !tenantCookie) {
// User is logged in, but does not have a tenant cookie, set it
setSelectedTenantID(initialValue)
setCookie(String(initialValue))
}
}, [userID, tenantCookie, initialValue, setCookie, router])
React.useEffect(() => {
if (!userID && tenantCookie) {
// User is not logged in, but has a tenant cookie, delete it
deleteCookie()
setSelectedTenantID(undefined)
} else if (userID) {
// User changed, refresh
router.refresh()
}
}, [userID, tenantCookie, deleteCookie, router])
return (
<span

View File

@@ -2,6 +2,8 @@ import type { OptionObject, Payload, User } from 'payload'
import { cookies as getCookies } from 'next/headers.js'
import { SELECT_ALL } from '../../constants.js'
import { findTenantOptions } from '../../queries/findTenantOptions.js'
import { TenantSelectionProviderClient } from './index.client.js'
type Args = {
@@ -22,16 +24,14 @@ export const TenantSelectionProvider = async ({
let tenantOptions: OptionObject[] = []
try {
const { docs: userTenants } = await payload.find({
collection: tenantsCollectionSlug,
depth: 0,
limit: 1000,
overrideAccess: false,
sort: useAsTitle,
const { docs } = await findTenantOptions({
limit: 0,
payload,
tenantsCollectionSlug,
useAsTitle,
user,
})
tenantOptions = userTenants.map((doc) => ({
tenantOptions = docs.map((doc) => ({
label: String(doc[useAsTitle]),
value: String(doc.id),
}))
@@ -40,10 +40,25 @@ export const TenantSelectionProvider = async ({
}
const cookies = await getCookies()
const tenantCookie = cookies.get('payload-tenant')?.value
let tenantCookie = cookies.get('payload-tenant')?.value
let initialValue = undefined
const isValidTenantCookie =
(tenantOptions.length > 1 && tenantCookie === SELECT_ALL) ||
tenantOptions.some((option) => option.value === tenantCookie)
if (isValidTenantCookie) {
initialValue = tenantCookie
} else {
tenantCookie = undefined
initialValue = tenantOptions.length > 1 ? SELECT_ALL : tenantOptions[0]?.value
}
return (
<TenantSelectionProviderClient initialValue={tenantCookie} tenantOptions={tenantOptions}>
<TenantSelectionProviderClient
initialValue={initialValue}
tenantCookie={tenantCookie}
tenantOptions={tenantOptions}
>
{children}
</TenantSelectionProviderClient>
)