Select next available account on signOut
This commit is contained in:
parent
58a1ee929b
commit
c235ca3fe7
|
@ -89,7 +89,7 @@ function NotificationBell () {
|
|||
function NavProfileMenu ({ me, dropNavKey }) {
|
||||
const { registration: swRegistration, togglePushSubscription } = useServiceWorker()
|
||||
const showModal = useShowModal()
|
||||
const { resetMultiAuthPointer } = useAccounts()
|
||||
const { multiAuthSignout } = useAccounts()
|
||||
return (
|
||||
<div className='position-relative'>
|
||||
<Dropdown className={styles.dropdown} align='end'>
|
||||
|
@ -131,6 +131,9 @@ function NavProfileMenu ({ me, dropNavKey }) {
|
|||
<Dropdown.Item onClick={() => showModal(onClose => <SwitchAccountDialog onClose={onClose} />)}>switch account</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
onClick={async () => {
|
||||
const status = await multiAuthSignout()
|
||||
// only signout if multiAuth did not find a next available account
|
||||
if (status === 201) return
|
||||
try {
|
||||
// order is important because we need to be logged in to delete push subscription on server
|
||||
const pushSubscription = await swRegistration?.pushManager.getSubscription()
|
||||
|
@ -141,7 +144,6 @@ function NavProfileMenu ({ me, dropNavKey }) {
|
|||
// don't prevent signout because of an unsubscription error
|
||||
console.error(err)
|
||||
}
|
||||
resetMultiAuthPointer()
|
||||
await signOut({ callbackUrl: '/' })
|
||||
}}
|
||||
>logout
|
||||
|
|
|
@ -18,16 +18,22 @@ export const AccountProvider = ({ children }) => {
|
|||
const [accounts, setAccounts] = useState([])
|
||||
const [isAnon, setIsAnon] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const updateAccountsFromCookie = useCallback(() => {
|
||||
try {
|
||||
const { multi_auth: multiAuthCookie } = cookie.parse(document.cookie)
|
||||
const accounts = multiAuthCookie
|
||||
? JSON.parse(b64Decode(multiAuthCookie))
|
||||
: me ? [{ id: me.id, name: me.name, photoId: me.photoId }] : []
|
||||
console.log(accounts)
|
||||
if (multiAuthCookie) console.log(JSON.parse(b64Decode(multiAuthCookie)))
|
||||
setAccounts(accounts)
|
||||
} catch (err) {
|
||||
console.error('error parsing cookies:', err)
|
||||
}
|
||||
}, [setAccounts])
|
||||
|
||||
useEffect(() => {
|
||||
updateAccountsFromCookie()
|
||||
}, [])
|
||||
|
||||
const addAccount = useCallback(user => {
|
||||
|
@ -38,9 +44,14 @@ export const AccountProvider = ({ children }) => {
|
|||
setAccounts(accounts => accounts.filter(({ id }) => id !== userId))
|
||||
}, [setAccounts])
|
||||
|
||||
const resetMultiAuthPointer = useCallback(() => {
|
||||
document.cookie = 'multi_auth.user-id='
|
||||
}, [])
|
||||
const multiAuthSignout = useCallback(async () => {
|
||||
// document.cookie = 'multi_auth.user-id='
|
||||
// switch to next available account
|
||||
const { status } = await fetch('/api/signout', { credentials: 'include' })
|
||||
console.log('multiAuthSignout rseponse', status)
|
||||
if (status === 201) updateAccountsFromCookie()
|
||||
return status
|
||||
}, [updateAccountsFromCookie])
|
||||
|
||||
useEffect(() => {
|
||||
// document not defined on server
|
||||
|
@ -49,7 +60,7 @@ export const AccountProvider = ({ children }) => {
|
|||
setIsAnon(multiAuthUserIdCookie === 'anonymous')
|
||||
}, [])
|
||||
|
||||
return <AccountContext.Provider value={{ accounts, addAccount, removeAccount, isAnon, setIsAnon, resetMultiAuthPointer }}>{children}</AccountContext.Provider>
|
||||
return <AccountContext.Provider value={{ accounts, addAccount, removeAccount, isAnon, setIsAnon, multiAuthSignout }}>{children}</AccountContext.Provider>
|
||||
}
|
||||
|
||||
export const useAccounts = () => useContext(AccountContext)
|
||||
|
@ -100,6 +111,7 @@ const Account = ({ account, className }) => {
|
|||
>
|
||||
<Image
|
||||
width='135' height='135' src={src} style={{ cursor: 'pointer' }} onClick={async () => {
|
||||
console.log('switching to account', account.id)
|
||||
document.cookie = `multi_auth.user-id=${account.id}; Path=/; Secure`
|
||||
await refreshMe()
|
||||
// order is important to prevent flashes of inconsistent data in switch account dialog
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import cookie from 'cookie'
|
||||
import { datePivot } from '../../lib/time'
|
||||
|
||||
/**
|
||||
* @param {NextApiRequest} req
|
||||
* @param {NextApiResponse} res
|
||||
* @return {void}
|
||||
*/
|
||||
export default (req, res) => {
|
||||
// is there a cookie pointer?
|
||||
const cookiePointerName = 'multi_auth.user-id'
|
||||
const userId = req.cookies[cookiePointerName]
|
||||
// is there a session?
|
||||
const sessionCookieName = '__Secure-next-auth.session-token'
|
||||
const sessionJWT = req.cookies[sessionCookieName]
|
||||
|
||||
if (!userId || !sessionJWT) {
|
||||
// no cookie pointer or no session cookie present. do nothing.
|
||||
res.status(404).end()
|
||||
return
|
||||
}
|
||||
|
||||
const cookies = []
|
||||
|
||||
const cookieOptions = {
|
||||
path: '/',
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
sameSite: 'lax',
|
||||
expires: datePivot(new Date(), { months: 1 })
|
||||
}
|
||||
// remove JWT pointed to by cookie pointer
|
||||
cookies.push(cookie.serialize(`multi_auth.${userId}`, '', { ...cookieOptions, expires: 0, maxAge: 0 }))
|
||||
|
||||
// update multi_auth cookie
|
||||
const oldMultiAuth = b64Decode(req.cookies.multi_auth)
|
||||
const newMultiAuth = oldMultiAuth.filter(({ id }) => id !== Number(userId))
|
||||
cookies.push(cookie.serialize('multi_auth', b64Encode(newMultiAuth), { ...cookieOptions, httpOnly: false }))
|
||||
|
||||
// switch to next available account
|
||||
if (!newMultiAuth.length) {
|
||||
// no next account available
|
||||
res.setHeader('Set-Cookie', cookies)
|
||||
res.status(204).end()
|
||||
return
|
||||
}
|
||||
|
||||
const newUserId = newMultiAuth[0].id
|
||||
const newUserJWT = req.cookies[`multi_auth.${newUserId}`]
|
||||
res.setHeader('Set-Cookie', [
|
||||
...cookies,
|
||||
cookie.serialize(cookiePointerName, newUserId, { ...cookieOptions, httpOnly: false }),
|
||||
cookie.serialize(sessionCookieName, newUserJWT, cookieOptions)
|
||||
])
|
||||
|
||||
res.status(201).end()
|
||||
}
|
||||
|
||||
const b64Encode = obj => Buffer.from(JSON.stringify(obj)).toString('base64')
|
||||
const b64Decode = s => JSON.parse(Buffer.from(s, 'base64'))
|
Loading…
Reference in New Issue