Agora

Lucid mixin

Add assignRole / can / getRoles sugar directly to your user model.

The optional hasPermissions mixin (exported from the @adonis-agora/authz/mixins subpath) adds RBAC sugar to a Lucid model. Every method delegates to the AuthzService and the store.

app/models/user.ts
import { compose } from '@adonisjs/core/helpers'
import { BaseModel } from '@adonisjs/lucid/orm'
import { hasPermissions } from '@adonis-agora/authz/mixins'
import { AuthzService } from '@adonis-agora/authz'
import app from '@adonisjs/core/services/app'

export default class User extends compose(
  BaseModel,
  hasPermissions(() => app.container.make(AuthzService)),
) {
  // ...your columns
}

The factory takes a resolver (sync or async) for the AuthzService. It needs the service class rather than the services/main singleton, because the write helpers (assignRole, givePermission, …) go through service.store, which the singleton does not carry.

Methods

const user = await User.findOrFail(1)

await user.assignRole('editor')
await user.assignRole('viewer', { tenantId: 'acme' })
await user.removeRole('editor')

await user.givePermission('billing.view')   // direct grant
await user.revokePermission('billing.view')

await user.getRoles()                        // string[]
await user.getPermissions()                  // string[] (role-derived ∪ direct)

await user.can('posts.edit')                 // wildcard-aware boolean
await user.hasRole('admin')

The mixin maps the model to a { type, id } reference using the configured resolveUserRef. The default reads this.id; set a type via resolveUserRef if you store multiple subject kinds.

Every method above is one query for one user. Over a list of users, getRoles() becomes one round trip per row — for that case declare a roles relation and preload it.

On this page