N
Nuxt6mo ago
ægteemil

Route guards for user permissions

Hey. I want to add navigation guards that check if a user has a required permissions before nagivation to a given page, or any sub-pages of it. Right now I have a somewhat crude implementation, and I'm curious if there's a better way of handling it.
if (to.fullPath.startsWith('/customers') && currentUserPermissions?.includes(Permission.CUSTOMER_READ))
return true;

if (to.fullPath.startsWith('/sales') && currentUserPermissions?.includes(Permission.SALES_READ))
return true;

// .. more

return abortNavigation(
createError({
statusCode: 403,
message: 'You do not have permissions to access this page.',
}),
);
if (to.fullPath.startsWith('/customers') && currentUserPermissions?.includes(Permission.CUSTOMER_READ))
return true;

if (to.fullPath.startsWith('/sales') && currentUserPermissions?.includes(Permission.SALES_READ))
return true;

// .. more

return abortNavigation(
createError({
statusCode: 403,
message: 'You do not have permissions to access this page.',
}),
);
3 Replies
Mähh
Mähh6mo ago
Depends on your architectur 🙂 But your way is easy & understandable to everyone whos checking your code. If you want to abstract that part in the middleware itself and e.g. want to place the required permissions directly into page components, you could do something like:
<script setup lang="ts">
definePageMeta({
acl: ['user:view']
})
</script>

<template>
<div>
</div>
</template>
<script setup lang="ts">
definePageMeta({
acl: ['user:view']
})
</script>

<template>
<div>
</div>
</template>
While your middleware (e.g. acl.global.ts) could look like:
export default defineNuxtRouteMiddleware((to, from) => {
// get your current user permissions
const userPermissions = ['user:edit', 'user:view']
const requiredPermissions = to.meta.acl

if (!requiredPermissions || requiredPermissions.length === 0) {
return
}

const granted = requiredPermissions.every((permission) => userPermissions.includes(permission))

if (!granted) {
return abortNavigation({
statusCode: 403,
statusMessage: 'Forbidden',
})
}
})
export default defineNuxtRouteMiddleware((to, from) => {
// get your current user permissions
const userPermissions = ['user:edit', 'user:view']
const requiredPermissions = to.meta.acl

if (!requiredPermissions || requiredPermissions.length === 0) {
return
}

const granted = requiredPermissions.every((permission) => userPermissions.includes(permission))

if (!granted) {
return abortNavigation({
statusCode: 403,
statusMessage: 'Forbidden',
})
}
})
Disadvantage of that way is, that you do not have permissions based on the url path and needs to repeat yourself on subpages of e.g. /customers and /customers/{id} in the definePageMeta To solve that you could work with NestedPages. But keep in mind: with more layers and abstraction, its more complicated to understand for other developers how your code and permissions works, which permission is required to visit pages. in case of type-safety you can add in your index.d.ts
declare module '#app' {
interface PageMeta {
acl?: string[]
}
}

export {}
declare module '#app' {
interface PageMeta {
acl?: string[]
}
}

export {}
ægteemil
ægteemilOP6mo ago
Thanks! This is what I was looking for, but on second though I think it might be prettier with my own solution, since I'd have to place the page meta-data on all sub-pages as well, right?
Mähh
Mähh6mo ago
as long as you do not work with nested pages: yes.
Want results from more Discord servers?
Add your server