Skip to main content
Permission management with Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC) support.

Features

  • Role-Based Access Control (RBAC)
  • Attribute-Based Access Control (ABAC) with context
  • Functional permission conditions
  • Token-based permission storage
  • Adapter pattern for custom permission systems

Installation

import { createPermissionsPlugin } from '@vuetify/v0'

const app = createApp(App)
app.use(createPermissionsPlugin({
  permissions: {
    admin: [
      ['read', 'users'],
      ['write', 'users'],
      ['delete', 'users'],
    ],
    editor: [
      ['read', 'posts'],
      ['write', 'posts'],
    ],
  },
}))

Basic Usage

<script setup lang="ts">
import { usePermissions } from '@vuetify/v0'
import { ref } from 'vue'

const permissions = usePermissions()
const userRole = ref('editor')

const canEditPosts = computed(() => 
  permissions.can(userRole.value, 'write', 'posts')
)
</script>

<template>
  <div>
    <button v-if="canEditPosts">Edit Post</button>
  </div>
</template>

API Reference

createPermissionsPlugin()

Creates a permissions plugin.
options
PermissionPluginOptions
Plugin configuration
permissions
Record<ID, Permission[]>
Record of roles and their permissionsEach permission is a tuple: [actions, subjects, condition?]
  • actions: String or array of action names (e.g., ‘read’, ‘write’)
  • subjects: String or array of subject names (e.g., ‘users’, ‘posts’)
  • condition: Optional boolean or function for conditional permissions
Example:
{
  admin: [
    [['read', 'write', 'delete'], ['users', 'posts']],
  ],
  editor: [
    ['write', 'posts', (ctx) => ctx.isOwner],
  ],
}
adapter
PermissionAdapter
Custom permission adapter (defaults to Vuetify0PermissionAdapter)
namespace
string
default:"'v0:permissions'"
The namespace for the permissions context

PermissionContext

size
number
Number of registered permission entries
can
(id: ID, action: string, subject: string, context?: Record<string, any>) => boolean
Check if a role has permission to perform an action on a subjectParameters:
  • id: Role identifier (e.g., ‘admin’, ‘editor’)
  • action: Action name (e.g., ‘read’, ‘write’, ‘delete’)
  • subject: Subject name (e.g., ‘users’, ‘posts’)
  • context: Optional context object for conditional permissions
Returns: true if permission is granted, false otherwiseExamples:
permissions.can('admin', 'read', 'users') // true
permissions.can('editor', 'delete', 'users') // false
permissions.can('owner', 'edit', 'posts', { isOwner: true }) // true
get
(path: string) => PermissionTicket | undefined
Get a permission entry by pathPath format: role.action.subjectExample:
const ticket = permissions.get('admin.read.users')
console.log(ticket.value) // true or function
has
(path: string) => boolean
Check if a permission entry exists

Permission Formats

Simple RBAC

Basic role-based permissions:
app.use(createPermissionsPlugin({
  permissions: {
    admin: [
      ['read', 'users'],
      ['write', 'users'],
      ['delete', 'users'],
    ],
    viewer: [
      ['read', 'users'],
    ],
  },
}))

// Usage
const permissions = usePermissions()
permissions.can('admin', 'delete', 'users') // true
permissions.can('viewer', 'delete', 'users') // false

Multiple Actions

permissions: {
  editor: [
    [['read', 'write'], 'posts'],
  ],
}

// Both actions are granted
permissions.can('editor', 'read', 'posts') // true
permissions.can('editor', 'write', 'posts') // true

Multiple Subjects

permissions: {
  viewer: [
    ['read', ['posts', 'comments', 'users']],
  ],
}

// Can read all subjects
permissions.can('viewer', 'read', 'posts') // true
permissions.can('viewer', 'read', 'comments') // true
permissions.can('viewer', 'read', 'users') // true

Matrix Permissions

permissions: {
  admin: [
    [['read', 'write', 'delete'], ['users', 'posts', 'comments']],
  ],
}

// All combinations are granted
permissions.can('admin', 'read', 'users') // true
permissions.can('admin', 'write', 'posts') // true
permissions.can('admin', 'delete', 'comments') // true

Conditional Permissions (ABAC)

Boolean Conditions

permissions: {
  moderator: [
    ['delete', 'comments', true], // Always allowed
    ['delete', 'posts', false],   // Never allowed
  ],
}

Function Conditions

permissions: {
  owner: [
    ['edit', 'posts', (ctx) => ctx.isOwner === true],
    ['delete', 'posts', (ctx) => ctx.isOwner === true],
  ],
}

// Usage
const permissions = usePermissions()
const post = { authorId: 123 }
const currentUserId = 123

permissions.can('owner', 'edit', 'posts', { 
  isOwner: post.authorId === currentUserId 
}) // true

permissions.can('owner', 'edit', 'posts', { 
  isOwner: false 
}) // false

Complex Context

permissions: {
  editor: [
    ['edit', 'posts', (ctx) => {
      return ctx.userId === ctx.postAuthorId ||
             ctx.userRoles.includes('senior-editor')
    }],
  ],
}

// Usage
permissions.can('editor', 'edit', 'posts', {
  userId: 123,
  postAuthorId: 456,
  userRoles: ['editor', 'senior-editor'],
}) // true (senior editor)

Examples

Role-Based UI

<script setup lang="ts">
import { usePermissions } from '@vuetify/v0'
import { ref, computed } from 'vue'

const permissions = usePermissions()
const userRole = ref('editor')

const canCreatePost = computed(() =>
  permissions.can(userRole.value, 'write', 'posts')
)

const canDeletePost = computed(() =>
  permissions.can(userRole.value, 'delete', 'posts')
)
</script>

<template>
  <div>
    <button v-if="canCreatePost">Create Post</button>
    <button v-if="canDeletePost">Delete Post</button>
  </div>
</template>

Ownership Check

<script setup lang="ts">
import { usePermissions } from '@vuetify/v0'
import { ref } from 'vue'

const permissions = usePermissions()
const currentUser = ref({ id: 123, role: 'editor' })
const post = ref({ id: 1, authorId: 123 })

const canEdit = computed(() => {
  return permissions.can(
    currentUser.value.role,
    'edit',
    'posts',
    { isOwner: post.value.authorId === currentUser.value.id }
  )
})
</script>

<template>
  <article>
    <h1>{{ post.title }}</h1>
    <button v-if="canEdit">Edit</button>
  </article>
</template>

Multi-Role User

<script setup lang="ts">
import { usePermissions } from '@vuetify/v0'
import { ref } from 'vue'

const permissions = usePermissions()
const userRoles = ref(['editor', 'moderator'])

const canPerformAction = (action: string, subject: string) => {
  return userRoles.value.some(role =>
    permissions.can(role, action, subject)
  )
}
</script>

<template>
  <div>
    <button v-if="canPerformAction('write', 'posts')">Edit Post</button>
    <button v-if="canPerformAction('delete', 'comments')">Delete Comment</button>
  </div>
</template>

Advanced Usage

Custom Adapter

import type { PermissionAdapter } from '@vuetify/v0'

class CaslAdapter implements PermissionAdapter {
  constructor(private ability: any) {}
  
  can(id: ID, action: string, subject: string, context: any, tokens: any) {
    return this.ability.can(action, subject)
  }
}

app.use(createPermissionsPlugin({
  adapter: new CaslAdapter(ability),
}))

Dynamic Permissions

const permissions = usePermissions()

// Get permission ticket
const ticket = permissions.get('admin.read.users')

if (ticket) {
  console.log('Value:', ticket.value)
  
  // For function conditions
  if (typeof ticket.value === 'function') {
    const allowed = ticket.value({ userId: 123 })
    console.log('Allowed:', allowed)
  }
}

Hierarchical Roles

const roleHierarchy = {
  admin: ['editor', 'moderator', 'viewer'],
  editor: ['viewer'],
  moderator: ['viewer'],
  viewer: [],
}

const hasPermission = (userRole: string, action: string, subject: string) => {
  const roles = [userRole, ...roleHierarchy[userRole]]
  return roles.some(role => permissions.can(role, action, subject))
}

Build docs developers (and LLMs) love