Skip to content

Commit e7e945f

Browse files
committed
Show proper error on 409 due to auth error
1 parent 0956c17 commit e7e945f

4 files changed

Lines changed: 64 additions & 3 deletions

File tree

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
appearance="filled"
2828
variation="primary"
2929
v-bind="logoutButtonsAttrs"
30+
@click="lowAssuranceLevelError && handleLogout()"
3031
>
3132
{{ navigateToLoginText }}
3233
</oc-button>
@@ -41,7 +42,9 @@ import {
4142
queryItemAsString,
4243
useConfigStore,
4344
useRouteQuery,
44-
useThemeStore
45+
useRouter,
46+
useThemeStore,
47+
useAuthService
4548
} from '@ownclouders/web-pkg'
4649
4750
export default defineComponent({
@@ -50,7 +53,13 @@ export default defineComponent({
5053
const themeStore = useThemeStore()
5154
const { currentTheme } = storeToRefs(themeStore)
5255
const configStore = useConfigStore()
56+
const authService = useAuthService()
57+
const router = useRouter()
5358
const redirectUrlQuery = useRouteQuery('redirectUrl')
59+
const reasonQuery = useRouteQuery('reason')
60+
const lowAssuranceLevelError = computed(
61+
() => queryItemAsString(unref(reasonQuery)) === 'lowAssuranceLevel'
62+
)
5463
5564
const { $gettext } = useGettext()
5665
@@ -59,17 +68,33 @@ export default defineComponent({
5968
const logoImg = computed(() => currentTheme.value.logo.login)
6069
6170
const cardTitle = computed(() => {
71+
if (unref(lowAssuranceLevelError)) {
72+
return $gettext('Cannot log in')
73+
}
6274
return $gettext('Not logged in')
6375
})
6476
const cardHint = computed(() => {
77+
if (unref(lowAssuranceLevelError)) {
78+
return $gettext(
79+
'Please login to your CERN account using the CERN credentials instead of a guest account.'
80+
)
81+
}
6582
return $gettext(
6683
'This could be because of a routine safety log out, or because your account is either inactive or not yet authorized for use. Please try logging in after a while or seek help from your Administrator.'
6784
)
6885
})
6986
const navigateToLoginText = computed(() => {
7087
return $gettext('Log in again')
7188
})
89+
const handleLogout = async () => {
90+
await authService.logoutUser()
91+
await router.push({ name: 'login' })
92+
}
93+
7294
const logoutButtonsAttrs = computed(() => {
95+
if (unref(lowAssuranceLevelError)) {
96+
return { type: 'button' }
97+
}
7398
const redirectUrl = queryItemAsString(unref(redirectUrlQuery))
7499
if (configStore.options.loginUrl) {
75100
const configLoginURL = new URL(encodeURI(configStore.options.loginUrl))
@@ -99,7 +124,9 @@ export default defineComponent({
99124
footerSlogan,
100125
navigateToLoginText,
101126
accessDeniedHelpUrl,
102-
logoutButtonsAttrs
127+
logoutButtonsAttrs,
128+
handleLogout,
129+
lowAssuranceLevelError
103130
}
104131
}
105132
})

packages/web-runtime/src/router/setupAuthGuard.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ export const setupAuthGuard = (router: Router) => {
4747

4848
if (isUserContextRequired(router, to)) {
4949
if (!authStore.userContextReady) {
50+
if (authService.lowAssuranceError) {
51+
return { name: 'accessDenied', query: { reason: 'lowAssuranceLevel' } }
52+
}
5053
if (unref(isDelegatingAuthentication)) {
5154
return { path: '/web-oidc-callback' }
5255
}

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ErrorResponse } from 'oidc-client-ts'
12
import { UserManager } from './userManager'
23
import { PublicLinkManager } from './publicLinkManager'
34
import {
@@ -44,6 +45,7 @@ export class AuthService implements AuthServiceInterface {
4445
private accessTokenExpiryThreshold = 10
4546

4647
public hasAuthErrorOccurred: boolean
48+
public lowAssuranceError: boolean
4749

4850
public initialize(
4951
configStore: ConfigStore,
@@ -60,6 +62,7 @@ export class AuthService implements AuthServiceInterface {
6062
this.clientService = clientService
6163
this.router = router
6264
this.hasAuthErrorOccurred = false
65+
this.lowAssuranceError = false
6366
this.ability = ability
6467
this.language = language
6568
this.userStore = userStore
@@ -165,6 +168,10 @@ export class AuthService implements AuthServiceInterface {
165168
try {
166169
await this.userManager.updateContext(user.access_token, fetchUserData)
167170
} catch (e) {
171+
if (this.isLowAssuranceLevelError(e)) {
172+
this.lowAssuranceError = true
173+
return
174+
}
168175
console.error(e)
169176
await this.handleAuthError(unref(this.router.currentRoute))
170177
}
@@ -227,6 +234,10 @@ export class AuthService implements AuthServiceInterface {
227234
this.tokenTimerInitialized = true
228235
}
229236
} catch (e) {
237+
if (this.isLowAssuranceLevelError(e)) {
238+
this.lowAssuranceError = true
239+
return
240+
}
230241
console.error(e)
231242
await this.handleAuthError(unref(this.router.currentRoute))
232243
}
@@ -269,6 +280,10 @@ export class AuthService implements AuthServiceInterface {
269280
...(redirectRoute.query && { query: redirectRoute.query })
270281
})
271282
} catch (e) {
283+
if (this.isLowAssuranceLevelError(e)) {
284+
this.lowAssuranceError = true
285+
return this.router.push({ name: 'accessDenied', query: { reason: 'lowAssuranceLevel' } })
286+
}
272287
console.warn('error during authentication:', e)
273288
return this.handleAuthError(unref(this.router.currentRoute))
274289
}
@@ -329,6 +344,10 @@ export class AuthService implements AuthServiceInterface {
329344
this.hasAuthErrorOccurred = true
330345
}
331346

347+
private isLowAssuranceLevelError(e: unknown): boolean {
348+
return e instanceof ErrorResponse && e.error === 'low_assurance_level'
349+
}
350+
332351
public async resolvePublicLink(
333352
token: string,
334353
passwordRequired: boolean,

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,15 @@ export class UserManager extends OidcUserManager {
187187

188188
private async fetchUserInfo() {
189189
const graphClient = this.clientService.graphAuthenticated
190-
const [graphUser, roles] = await Promise.all([graphClient.users.getMe(), this.fetchRoles()])
190+
let graphUser: Awaited<ReturnType<typeof graphClient.users.getMe>>, roles: SettingsBundle[]
191+
try {
192+
;[graphUser, roles] = await Promise.all([graphClient.users.getMe(), this.fetchRoles()])
193+
} catch (e) {
194+
if (e?.response?.status === 409) {
195+
throw new ErrorResponse({ error: 'low_assurance_level' } as any)
196+
}
197+
throw e
198+
}
191199
const role = await this.fetchRole({ graphUser, roles })
192200

193201
this.userStore.setUser({
@@ -307,6 +315,10 @@ export class UserManager extends OidcUserManager {
307315
user.access_token = revaToken
308316
user.expires_at = claims.exp
309317
} catch (e) {
318+
// We do not want to fail/raise exception here, even on 409.
319+
// If we get a 409 we still want the user to be persisted, so that
320+
// we can terminate the session in the SSO as well (on logout).
321+
// The 409 will be catched later.
310322
console.error('Failed to get reva token, continue with sso one', e)
311323
}
312324
// end

0 commit comments

Comments
 (0)