import { policies } from '#core/policies/main'import * as abilities from '#core/abilities/main'import { Bouncer } from '@adonisjs/bouncer'import type { HttpContext } from '@adonisjs/core/http'import type { NextFn } from '@adonisjs/core/types/http'export default class InitializeBouncerMiddleware { async handle(ctx: HttpContext, next: NextFn) { // Create bouncer instance with current user ctx.bouncer = new Bouncer( () => ctx.auth.user || null, abilities, policies ).setContainerResolver(ctx.containerResolver) // Share bouncer helpers with Edge templates if ('view' in ctx) { ctx.view.share(ctx.bouncer.edgeHelpers) } return next() }}
The middleware makes the bouncer available via ctx.bouncer in all controllers and @can directives in templates.
The starter kit demonstrates policy usage throughout the UsersController:
app/users/controllers/users_controller.ts
import type { HttpContext } from '@adonisjs/core/http'import User from '#users/models/user'import UserPolicy from '#users/policies/user_policy'export default class UsersController { public async index({ bouncer, inertia }: HttpContext) { // Check if user can view the list await bouncer.with(UserPolicy).authorize('viewList') const users = await User.query().preload('role') return inertia.render('users/index', { users }) } public async update({ bouncer, params, request, response }: HttpContext) { const user = await User.findOrFail(params.id) // Check if user can update this specific user await bouncer.with(UserPolicy).authorize('update', user) const payload = await request.validateUsing(editUserValidator) user.merge(payload) await user.save() return response.redirect().toRoute('users.index') } public async destroy({ bouncer, params, response }: HttpContext) { const user = await User.findOrFail(params.id) // Check if user can delete this user await bouncer.with(UserPolicy).authorize('delete', user) await user.delete() return response.redirect().toRoute('users.index') }}
import { BasePolicy } from '@adonisjs/bouncer'import { AuthorizerResponse } from '@adonisjs/bouncer/types'import User from '#users/models/user'export default class ImpersonatePolicy extends BasePolicy { create(currentUser: User, user: User): AuthorizerResponse { // Only admins can impersonate, and they can't impersonate themselves return currentUser.isAdmin && currentUser.id !== user.id }}
export default class CommentPolicy extends BasePolicy { async update(user: User, comment: Comment): AuthorizerResponse { // Load the post relationship await comment.load('post') // Allow if user is the comment author or the post author return ( user.id === comment.userId || user.id === comment.post.authorId || user.isAdmin ) }}