Skip to content

Commit 316cde3

Browse files
committed
feat: [OCISDEV-249] add acr verification to admin settings
Add a check to the admin settings app to verify the ACR value. If the ACR value is not the one required, the user is redirected to the login page.
1 parent 5fd9f48 commit 316cde3

File tree

5 files changed

+61
-11
lines changed

5 files changed

+61
-11
lines changed

dev/docker/ocis.web.config.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"metadata_url": "https://host.docker.internal:9200/.well-known/openid-configuration",
66
"authority": "https://host.docker.internal:9200",
77
"client_id": "web",
8-
"response_type": "code"
8+
"response_type": "code",
9+
"scope": "openid profile email acr"
910
},
1011
"options": {
1112
"contextHelpersReadMore": true

packages/web-app-admin-settings/src/index.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
AppNavigationItem,
1111
defineWebApplication,
1212
useAbility,
13+
useAuthService,
1314
useUserStore
1415
} from '@ownclouders/web-pkg'
1516
import { RouteRecordRaw } from 'vue-router'
@@ -22,7 +23,7 @@ function $gettext(msg: string) {
2223

2324
const appId = 'admin-settings'
2425

25-
function getAvailableRoute(ability: Ability) {
26+
function getNextAvailableRoute(ability: Ability) {
2627
if (ability.can('read-all', 'Setting')) {
2728
return { name: 'admin-settings-general' }
2829
}
@@ -42,6 +43,12 @@ function getAvailableRoute(ability: Ability) {
4243
throw Error('Insufficient permissions')
4344
}
4445

46+
async function requireAcr(acrValue: string, redirectUrl: string) {
47+
// TODO: check capabilities
48+
const authService = useAuthService()
49+
await authService.requireAcr(acrValue, redirectUrl)
50+
}
51+
4552
export const routes = ({ $ability }: { $ability: Ability }): RouteRecordRaw[] => [
4653
{
4754
path: '/',
@@ -53,10 +60,13 @@ export const routes = ({ $ability }: { $ability: Ability }): RouteRecordRaw[] =>
5360
path: '/general',
5461
name: 'admin-settings-general',
5562
component: General,
56-
beforeEnter: (to, from, next) => {
63+
beforeEnter: async (to, from, next) => {
5764
if (!$ability.can('read-all', 'Setting')) {
58-
return next(getAvailableRoute($ability))
65+
return next(getNextAvailableRoute($ability))
5966
}
67+
68+
await requireAcr('advanced', to.fullPath)
69+
6070
next()
6171
},
6272
meta: {
@@ -68,10 +78,13 @@ export const routes = ({ $ability }: { $ability: Ability }): RouteRecordRaw[] =>
6878
path: '/users',
6979
name: 'admin-settings-users',
7080
component: Users,
71-
beforeEnter: (to, from, next) => {
81+
beforeEnter: async (to, from, next) => {
7282
if (!$ability.can('read-all', 'Account')) {
73-
return next(getAvailableRoute($ability))
83+
return next(getNextAvailableRoute($ability))
7484
}
85+
86+
await requireAcr('advanced', to.fullPath)
87+
7588
next()
7689
},
7790
meta: {
@@ -83,10 +96,13 @@ export const routes = ({ $ability }: { $ability: Ability }): RouteRecordRaw[] =>
8396
path: '/groups',
8497
name: 'admin-settings-groups',
8598
component: Groups,
86-
beforeEnter: (to, from, next) => {
99+
beforeEnter: async (to, from, next) => {
87100
if (!$ability.can('read-all', 'Group')) {
88-
return next(getAvailableRoute($ability))
101+
return next(getNextAvailableRoute($ability))
89102
}
103+
104+
await requireAcr('advanced', to.fullPath)
105+
90106
next()
91107
},
92108
meta: {
@@ -98,10 +114,13 @@ export const routes = ({ $ability }: { $ability: Ability }): RouteRecordRaw[] =>
98114
path: '/spaces',
99115
name: 'admin-settings-spaces',
100116
component: Spaces,
101-
beforeEnter: (to, from, next) => {
117+
beforeEnter: async (to, from, next) => {
102118
if (!$ability.can('read-all', 'Drive')) {
103-
return next(getAvailableRoute($ability))
119+
return next(getNextAvailableRoute($ability))
104120
}
121+
122+
await requireAcr('advanced', to.fullPath)
123+
105124
next()
106125
},
107126
meta: {

packages/web-pkg/src/composables/authContext/useAuthService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface AuthServiceInterface {
66
signinSilent(): Promise<unknown>
77
logoutUser(): Promise<void | NavigationFailure>
88
getRefreshToken(): Promise<string>
9+
requireAcr(acrValue: string, redirectUrl: string): Promise<void>
910
}
1011

1112
export const useAuthService = (): AuthServiceInterface => {

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,31 @@ export class AuthService implements AuthServiceInterface {
384384
console.debug('[authService:handleDelegatedTokenUpdate] - going to update the access_token')
385385
return this.userManager.updateContext(event.data, false)
386386
}
387+
388+
/**
389+
* Redirects to the login page if the user is not authenticated or if the ACR value is not the one required.
390+
*
391+
* @param acrValue - The ACR value to require.
392+
* @param redirectUrl - The URL to redirect to after login.
393+
*
394+
* @throws {Error} In cases of wrong authentication.
395+
*/
396+
public async requireAcr(acrValue: string, redirectUrl: string) {
397+
const user = await this.userManager.getUser()
398+
399+
if (!user || user.expired) {
400+
this.userManager.setPostLoginRedirectUrl(redirectUrl)
401+
return this.userManager.signinRedirect({ acr_values: acrValue })
402+
}
403+
404+
const { acr } = user.profile
405+
if (acr === acrValue) {
406+
return
407+
}
408+
409+
this.userManager.setPostLoginRedirectUrl(redirectUrl)
410+
return this.userManager.signinRedirect({ acr_values: acrValue })
411+
}
387412
}
388413

389414
export const authService = new AuthService()

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,17 @@ export class UserManager extends OidcUserManager {
7373
client_id: '',
7474

7575
// we trigger the token renewal manually via a timer running in a web worker
76-
automaticSilentRenew: false
76+
automaticSilentRenew: false,
77+
78+
// Omit acr
79+
filterProtocolClaims: ['nbf', 'jti', 'auth_time', 'nonce', 'amr', 'azp', 'at_hash']
7780
}
7881

7982
if (options.configStore.isOIDC) {
8083
Object.assign(openIdConfig, {
8184
scope: 'openid profile',
8285
loadUserInfo: false,
86+
acr_values: 'regular',
8387
...options.configStore.openIdConnect,
8488
...(options.configStore.openIdConnect.metadata_url && {
8589
metadataUrl: options.configStore.openIdConnect.metadata_url

0 commit comments

Comments
 (0)