Skip to content

Commit 6680a9b

Browse files
committed
fix: backchannel logout react to sid
1 parent c49e381 commit 6680a9b

13 files changed

Lines changed: 69 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
@@ -848,6 +848,7 @@ export const registerSSEEventListeners = ({
848848
previewService,
849849
configStore,
850850
userStore,
851+
authStore,
851852
router
852853
}: {
853854
language: Language
@@ -859,6 +860,7 @@ export const registerSSEEventListeners = ({
859860
previewService: PreviewService
860861
configStore: ConfigStore
861862
userStore: UserStore
863+
authStore: AuthStore
862864
router: Router
863865
}): void => {
864866
const resourceQueue = new PQueue({
@@ -883,7 +885,8 @@ export const registerSSEEventListeners = ({
883885
previewService,
884886
language,
885887
router,
886-
resourceQueue
888+
resourceQueue,
889+
authStore
887890
} satisfies Partial<SseEventWrapperOptions>
888891

889892
clientService.sseAuthenticated.addEventListener(MESSAGE_TYPE.ITEM_RENAMED, (msg) =>
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
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 (!sseData.sessionid) {
5+
// Log out all clients when no session id is provided according to OIDC spec
6+
return router.push({ name: 'logout' })
7+
}
8+
9+
if (authStore.sessionId === sseData.sessionid) {
10+
return router.push({ name: 'logout' })
11+
}
512
}

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
@@ -239,6 +239,7 @@ export const bootstrapApp = async (configurationPath: string, appsReadyCallback:
239239
userStore,
240240
previewService,
241241
configStore,
242+
authStore,
242243
router
243244
})
244245
}

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
@@ -162,7 +162,7 @@ export class AuthService implements AuthServiceInterface {
162162

163163
console.debug(`New User Loaded`)
164164
try {
165-
await this.userManager.updateContext(user.access_token, fetchUserData)
165+
await this.userManager.updateContext(user.access_token, user.profile.sid, fetchUserData)
166166
} catch (e) {
167167
console.error(e)
168168
await this.handleAuthError(unref(this.router.currentRoute))
@@ -209,12 +209,15 @@ export class AuthService implements AuthServiceInterface {
209209

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

216219
try {
217-
await this.userManager.updateContext(accessToken, fetchUserData)
220+
await this.userManager.updateContext(accessToken, sessionId, fetchUserData)
218221

219222
if (!this.tokenTimerInitialized) {
220223
const user = await this.userManager.getUser()
@@ -245,15 +248,15 @@ export class AuthService implements AuthServiceInterface {
245248
/**
246249
* Sign in callback gets called from the IDP after initial login.
247250
*/
248-
public async signInCallback(accessToken?: string) {
251+
public async signInCallback(accessToken?: string, sessionId?: string) {
249252
try {
250253
if (
251254
this.configStore.options.embed.enabled &&
252255
this.configStore.options.embed.delegateAuthentication &&
253256
accessToken
254257
) {
255258
console.debug('[authService:signInCallback] - setting access_token and fetching user')
256-
await this.userManager.updateContext(accessToken, true)
259+
await this.userManager.updateContext(accessToken, sessionId, true)
257260

258261
// Setup a listener to handle token refresh
259262
console.debug('[authService:signInCallback] - adding listener to update-token event')
@@ -381,7 +384,7 @@ export class AuthService implements AuthServiceInterface {
381384
}
382385

383386
console.debug('[authService:handleDelegatedTokenUpdate] - going to update the access_token')
384-
return this.userManager.updateContext(event.data, false)
387+
return this.userManager.updateContext(event.data.accesssToken, event.data.sessionId, false)
385388
}
386389
}
387390

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)