Skip to content

Commit e125161

Browse files
authored
fix: backchannel logout react to sid (#1969) (#2060)
* fix: backchannel logout react to sid * only call sse logout when the event's sessionId matches the active one (cherry picked from commit f4c1972)
1 parent 4c89b2e commit e125161

13 files changed

Lines changed: 64 additions & 22 deletions

File tree

packages/web-pkg/src/composables/piniaStores/auth.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { PublicLinkType } from '@opencloud-eu/web-client'
44

55
export const useAuthStore = defineStore('auth', () => {
66
const accessToken = ref<string>()
7+
const sessionId = ref<string>()
78
const idpContextReady = ref(false)
89
const userContextReady = ref(false)
910
const publicLinkToken = ref<string>()
@@ -14,6 +15,9 @@ export const useAuthStore = defineStore('auth', () => {
1415
const setAccessToken = (value: string) => {
1516
accessToken.value = value
1617
}
18+
const setSessionId = (value: string) => {
19+
sessionId.value = value
20+
}
1721
const setIdpContextReady = (value: boolean) => {
1822
idpContextReady.value = value
1923
}
@@ -34,6 +38,7 @@ export const useAuthStore = defineStore('auth', () => {
3438

3539
const clearUserContext = () => {
3640
setAccessToken(null)
41+
setSessionId(null)
3742
setIdpContextReady(null)
3843
setUserContextReady(null)
3944
}
@@ -49,6 +54,7 @@ export const useAuthStore = defineStore('auth', () => {
4954

5055
return {
5156
accessToken,
57+
sessionId,
5258
idpContextReady,
5359
userContextReady,
5460
publicLinkToken,
@@ -57,6 +63,7 @@ export const useAuthStore = defineStore('auth', () => {
5763
publicLinkContextReady,
5864

5965
setAccessToken,
66+
setSessionId,
6067
setIdpContextReady,
6168
setUserContextReady,
6269
setPublicLinkContext,

packages/web-runtime/src/container/bootstrap.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,7 @@ export const registerSSEEventListeners = ({
807807
previewService,
808808
configStore,
809809
userStore,
810+
authStore,
810811
router
811812
}: {
812813
language: Language
@@ -818,6 +819,7 @@ export const registerSSEEventListeners = ({
818819
previewService: PreviewService
819820
configStore: ConfigStore
820821
userStore: UserStore
822+
authStore: AuthStore
821823
router: Router
822824
}): void => {
823825
const resourceQueue = new PQueue({
@@ -842,7 +844,8 @@ export const registerSSEEventListeners = ({
842844
previewService,
843845
language,
844846
router,
845-
resourceQueue
847+
resourceQueue,
848+
authStore
846849
} satisfies Partial<SseEventWrapperOptions>
847850

848851
clientService.sseAuthenticated.addEventListener(MESSAGE_TYPE.ITEM_RENAMED, (msg) =>
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { SSEEventOptions } from './types'
22

3-
export const onSSEBackchannelLogoutEvent = ({ router }: SSEEventOptions) => {
4-
return router.push({ name: 'logout' })
3+
export const onSSEBackchannelLogoutEvent = ({ router, authStore, sseData }: SSEEventOptions) => {
4+
if (authStore.sessionId === sseData.sessionid) {
5+
return router.push({ name: 'logout' })
6+
}
57
}

packages/web-runtime/src/container/sse/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { z } from 'zod'
22
import {
3+
AuthStore,
34
ClientService,
45
ConfigStore,
56
MessageStore,
@@ -19,7 +20,8 @@ export const eventSchema = z.object({
1920
spaceid: z.string().optional(),
2021
initiatorid: z.string().optional(),
2122
etag: z.string().optional(),
22-
affecteduserids: z.array(z.string()).optional().nullable()
23+
affecteduserids: z.array(z.string()).optional().nullable(),
24+
sessionid: z.string().optional()
2325
})
2426

2527
export type EventSchemaType = z.infer<typeof eventSchema>
@@ -31,6 +33,7 @@ export interface SSEEventOptions {
3133
messageStore: MessageStore
3234
sharesStore: SharesStore
3335
configStore: ConfigStore
36+
authStore: AuthStore
3437
clientService: ClientService
3538
previewService: PreviewService
3639
router: Router

packages/web-runtime/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ export const bootstrapApp = async (configurationPath: string, appsReadyCallback:
231231
userStore,
232232
previewService,
233233
configStore,
234+
authStore,
234235
router
235236
})
236237
}

packages/web-runtime/src/pages/oidcCallback.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const handleRequestedTokenEvent = (event: MessageEvent): void => {
5757
}
5858
5959
console.debug('[page:oidcCallback:handleRequestedTokenEvent] - received delegated access_token')
60-
authService.signInCallback(event.data.data.access_token)
60+
authService.signInCallback(event.data.data.access_token, event.data.data.session_id)
6161
}
6262
6363
onMounted(() => {

packages/web-runtime/src/services/auth/authService.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export class AuthService implements AuthServiceInterface {
164164
`New User Loaded. access_token: ${user.access_token}, refresh_token: ${user.refresh_token}`
165165
)
166166
try {
167-
await this.userManager.updateContext(user.access_token, fetchUserData)
167+
await this.userManager.updateContext(user.access_token, user.profile.sid, fetchUserData)
168168
} catch (e) {
169169
console.error(e)
170170
await this.handleAuthError(unref(this.router.currentRoute))
@@ -211,12 +211,15 @@ export class AuthService implements AuthServiceInterface {
211211

212212
// relevant for page reload: token is already in userStore
213213
// no userLoaded event and no signInCallback gets triggered
214-
const accessToken = await this.userManager.getAccessToken()
214+
const user = await this.userManager.getUser()
215+
const accessToken = user?.access_token
216+
const sessionId = user?.profile?.sid
217+
215218
if (accessToken) {
216219
console.debug('[authService:initializeContext] - updating context with saved access_token')
217220

218221
try {
219-
await this.userManager.updateContext(accessToken, fetchUserData)
222+
await this.userManager.updateContext(accessToken, sessionId, fetchUserData)
220223

221224
if (!this.tokenTimerInitialized) {
222225
const user = await this.userManager.getUser()
@@ -247,15 +250,15 @@ export class AuthService implements AuthServiceInterface {
247250
/**
248251
* Sign in callback gets called from the IDP after initial login.
249252
*/
250-
public async signInCallback(accessToken?: string) {
253+
public async signInCallback(accessToken?: string, sessionId?: string) {
251254
try {
252255
if (
253256
this.configStore.options.embed.enabled &&
254257
this.configStore.options.embed.delegateAuthentication &&
255258
accessToken
256259
) {
257260
console.debug('[authService:signInCallback] - setting access_token and fetching user')
258-
await this.userManager.updateContext(accessToken, true)
261+
await this.userManager.updateContext(accessToken, sessionId, true)
259262

260263
// Setup a listener to handle token refresh
261264
console.debug('[authService:signInCallback] - adding listener to update-token event')
@@ -383,7 +386,7 @@ export class AuthService implements AuthServiceInterface {
383386
}
384387

385388
console.debug('[authService:handleDelegatedTokenUpdate] - going to update the access_token')
386-
return this.userManager.updateContext(event.data, false)
389+
return this.userManager.updateContext(event.data.accesssToken, event.data.sessionId, false)
387390
}
388391
}
389392

packages/web-runtime/src/services/auth/userManager.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ export class UserManager extends OidcUserManager {
132132
return user?.access_token
133133
}
134134

135+
async getSessionId(): Promise<string | null> {
136+
const user = await this.getUser()
137+
return user?.profile?.sid
138+
}
139+
135140
async removeUser(unloadReason: UnloadReason = 'logout') {
136141
this._unloadReason = unloadReason
137142
await super.removeUser()
@@ -155,14 +160,15 @@ export class UserManager extends OidcUserManager {
155160
}
156161
}
157162

158-
updateContext(accessToken: string, fetchUserData: boolean) {
163+
updateContext(accessToken: string, sessionId: string, fetchUserData: boolean) {
159164
const userKnown = !!this.userStore.user
160165
const accessTokenChanged = this.authStore.accessToken !== accessToken
161166
if (!accessTokenChanged) {
162167
return this.updateAccessTokenPromise
163168
}
164169

165170
this.authStore.setAccessToken(accessToken)
171+
this.authStore.setSessionId(sessionId)
166172

167173
this.updateAccessTokenPromise = (async () => {
168174
if (!fetchUserData) {

packages/web-runtime/tests/unit/container/sse/files.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
ClientService,
33
PreviewService,
4+
useAuthStore,
45
useConfigStore,
56
useMessages,
67
useResourcesStore,
@@ -442,6 +443,7 @@ const getMocks = ({
442443
const userStore = useUserStore()
443444
const sharesStore = useSharesStore()
444445
const configStore = useConfigStore()
446+
const authStore = useAuthStore()
445447
const clientService = mockDeep<ClientService>({ initiatorId: 'local1' })
446448
const previewService = mockDeep<PreviewService>()
447449
const router = mockDeep<Router>()
@@ -458,6 +460,7 @@ const getMocks = ({
458460
userStore,
459461
sharesStore,
460462
configStore,
463+
authStore,
461464
clientService,
462465
previewService,
463466
resourceQueue,

packages/web-runtime/tests/unit/container/sse/helpers.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createTestingPinia } from '@opencloud-eu/web-test-helpers'
33
import {
44
ClientService,
55
PreviewService,
6+
useAuthStore,
67
useConfigStore,
78
useMessages,
89
useResourcesStore,
@@ -63,6 +64,7 @@ const getMocks = ({
6364
const userStore = useUserStore()
6465
const configStore = useConfigStore()
6566
const sharesStore = useSharesStore()
67+
const authStore = useAuthStore()
6668
const clientService = mockDeep<ClientService>()
6769
const previewService = mockDeep<PreviewService>()
6870
const router = mockDeep<Router>()
@@ -77,6 +79,7 @@ const getMocks = ({
7779
userStore,
7880
sharesStore,
7981
configStore,
82+
authStore,
8083
clientService,
8184
previewService,
8285
resourceQueue,

0 commit comments

Comments
 (0)