Getting started
Install @adonis-agora/authz, run the migration, grant a permission and check it through Bouncer.
Install & configure
npm i @adonis-agora/authz
node ace configure @adonis-agora/authzconfigure registers the provider and commands, and publishes:
config/authz.ts— the store configuration;app/abilities/authz.ts— thecan/hasRoleBouncer abilities;- a Lucid migration for the RBAC tables.
Decide who creates the tables
The lucid store manages its own schema: with autoCreateSchema left at its
default it creates authz_roles, authz_permissions, authz_role_permission,
authz_user_role and authz_user_permission on first use. No users table is
touched — users are referenced polymorphically. You can skip straight to the
next step and it will work.
For production, take the tables under your own control instead — schema changes
then ship with your deploy rather than on first request. Turn the automatic
creation off and run the migration configure published:
stores: {
lucid: stores.lucid({ autoCreateSchema: false }),
}node ace migration:runBoth paths create the same tables, so switching later is safe — see Configuration.
Grant a permission
From the CLI:
node ace authz:grant editor posts.edit # permission → role
node ace authz:assign editor 42 # role → user (id 42)…or programmatically:
import { AuthzService } from '@adonis-agora/authz'
import app from '@adonisjs/core/services/app'
const service = await app.container.make(AuthzService)
await service.store.givePermissionToRole('editor', 'posts.edit')
await service.store.assignRole({ type: 'user', id: '42' }, 'editor')Writing to the store needs the service instance. For reading a decision anywhere in the app, import the singleton instead — no container call:
import authz from '@adonis-agora/authz/services/main'
await authz.can(user, 'posts.edit')
await authz.hasRole(user, 'editor')Check it through Bouncer
import { can, hasRole } from '#abilities/authz'
router.put('/posts/:id', async (ctx) => {
const post = await Post.findOrFail(ctx.params.id)
// boolean check (wildcards apply: posts.* ⊇ posts.edit)
if (await ctx.bouncer.allows('can', 'posts.edit', post)) {
// ...
}
// throwing check (HTTP 403 on deny)
await ctx.bouncer.authorize('hasRole', 'admin')
})In Edge templates:
@can('can', 'posts.edit')
<a href="/posts/{{ post.id }}/edit">Edit</a>
@endThe resource argument (post above) is accepted for ergonomic call sites,
but RBAC grants are model-less, so the default can ability does not inspect
it. Combine @adonis-agora/authz with a regular Bouncer policy when you need
per-record ownership checks.