### Install and Configure AdonisJS Bouncer Source: https://context7.com/adonisjs/bouncer/llms.txt Install the Bouncer package and configure it within your AdonisJS application. This command publishes necessary stubs and registers the provider and middleware. ```bash node ace configure @adonisjs/bouncer ``` -------------------------------- ### Get Raw Authorization Response with execute() Source: https://context7.com/adonisjs/bouncer/llms.txt The `execute()` method returns a `Promise` object, providing `.authorized`, `.message`, `.status`, and `.translation` for detailed authorization outcomes. ```typescript import { Bouncer } from '@adonisjs/bouncer' import { AuthorizationResponse } from '@adonisjs/bouncer' class User { constructor(public id: number) {} } class Post { constructor(public authorId: number) {} } const editPost = Bouncer.ability((user: User, post: Post) => { if (user.id !== post.authorId) { return AuthorizationResponse.deny('You are not the author', 403) } return true }) const bouncer = new Bouncer(new User(1), { editPost }) const post = new Post(99) const result = await bouncer.execute('editPost', post) // result.authorized → false // result.message → 'You are not the author' // result.status → 403 ``` -------------------------------- ### execute() - Raw Authorization Response Source: https://context7.com/adonisjs/bouncer/llms.txt Get a detailed authorization response object, including `authorized` status, `message`, `status`, and `translation`. Useful for custom error handling or logging. ```APIDOC ## `bouncer.execute()` — Raw Authorization Response `execute()` returns `Promise` with the full response object, giving access to `.authorized`, `.message`, `.status`, and `.translation`. ```typescript import { Bouncer } from '@adonisjs/bouncer' import { AuthorizationResponse } from '@adonisjs/bouncer' class User { constructor(public id: number) {} } class Post { constructor(public authorId: number) {} } const editPost = Bouncer.ability((user: User, post: Post) => { if (user.id !== post.authorId) { return AuthorizationResponse.deny('You are not the author', 403) } return true }) const bouncer = new Bouncer(new User(1), { editPost }) const post = new Post(99) const result = await bouncer.execute('editPost', post) // result.authorized → false // result.message → 'You are not the author' // result.status → 403 ``` ``` -------------------------------- ### Generate Policy with make:policy Command Source: https://context7.com/adonisjs/bouncer/llms.txt Use the 'make:policy' Ace command to scaffold a new policy class. Specify the policy name and optionally associate it with a model and define initial action methods. ```bash # Generate PostPolicy with view, create, edit, delete actions node ace make:policy post --model=post --actions=view,create,edit,delete # Output: app/policies/post_policy.ts ``` -------------------------------- ### Bouncer Constructor Source: https://context7.com/adonisjs/bouncer/llms.txt Learn how to create a new Bouncer instance with user, abilities, and policies. Supports direct user references or lazy resolvers, and handles guest rules when the user is null. ```APIDOC ## `new Bouncer()` — Create a Bouncer Instance The `Bouncer` constructor accepts a user (or a lazy resolver function returning a user), an optional pre-defined abilities map, and an optional policies map. When the user is `null`, guest rules apply. ```typescript import { Bouncer } from '@adonisjs/bouncer' import { editPost, viewPost } from '#abilities/main' import PostPolicy from '#policies/post_policy' class User { constructor(public id: number) {} } class Post { constructor(public authorId: number, public isPublished: boolean) {} } const user = new User(42) const post = new Post(42, true) // With direct user reference and pre-registered abilities + policies const bouncer = new Bouncer( user, { editPost, viewPost }, { PostPolicy: () => import('#policies/post_policy'), } ) // With lazy user resolver (useful in middleware where auth resolves user lazily) const lazyBouncer = new Bouncer( () => user, // resolved on first use { editPost }, { PostPolicy: () => import('#policies/post_policy') } ) // Guest bouncer (null user) const guestBouncer = new Bouncer(null, { viewPost }) const canView = await guestBouncer.allows('viewPost', post) // depends on allowGuest flag ``` ``` -------------------------------- ### Create Bouncer Instance with User, Abilities, and Policies Source: https://context7.com/adonisjs/bouncer/llms.txt Instantiate Bouncer with a user, pre-defined abilities, and policies. Guest rules apply when the user is null. Supports direct user references or lazy resolvers. ```typescript import { Bouncer } from '@adonisjs/bouncer' import { editPost, viewPost } from '#abilities/main' import PostPolicy from '#policies/post_policy' class User { constructor(public id: number) {} } class Post { constructor(public authorId: number, public isPublished: boolean) {} } const user = new User(42) const post = new Post(42, true) // With direct user reference and pre-registered abilities + policies const bouncer = new Bouncer( user, { editPost, viewPost }, { PostPolicy: () => import('#policies/post_policy'), } ) // With lazy user resolver (useful in middleware where auth resolves user lazily) const lazyBouncer = new Bouncer( () => user, // resolved on first use { editPost }, { PostPolicy: () => import('#policies/post_policy') } ) // Guest bouncer (null user) const guestBouncer = new Bouncer(null, { viewPost }) const canView = await guestBouncer.allows('viewPost', post) // depends on allowGuest flag ``` -------------------------------- ### Using bouncer.with() for Policy-Based Authorization Source: https://context7.com/adonisjs/bouncer/llms.txt Demonstrates how to use `bouncer.with()` to create an authorizer for a given policy, either by its registered name or class reference. Chain `.allows()`, `.denies()`, or `.authorize()` to perform checks. ```typescript import { Bouncer, BasePolicy } from '@adonisjs/bouncer' import type { AuthorizerResponse } from '@adonisjs/bouncer/types' class User { constructor(public id: number, public isAdmin: boolean) {} } class Post { constructor(public authorId: number) {} } class PostPolicy extends BasePolicy { view (_user: User, post: Post): AuthorizerResponse { return post.authorId !== null } edit (user: User, post: Post): AuthorizerResponse { return user.isAdmin || user.id === post.authorId } delete (user: User, post: Post): AuthorizerResponse { return user.isAdmin } } const user = new User(1, false) const post = new Post(1) const bouncer = new Bouncer(user, undefined, { PostPolicy: () => import('./post_policy.js') as any, }) // By policy name (pre-registered) const canEdit = await bouncer.with('PostPolicy').allows('edit', post) // true const canDelete = await bouncer.with('PostPolicy').denies('delete', post) // true (not admin) // By class reference await bouncer.with(PostPolicy).authorize('edit', post) // no throw await bouncer.with(PostPolicy).authorize('delete', post) // throws E_AUTHORIZATION_FAILURE ``` -------------------------------- ### Defining Policies with BasePolicy, before, after, and decorators Source: https://context7.com/adonisjs/bouncer/llms.txt Shows how to extend `BasePolicy` to define authorization logic. Includes `before` and `after` hooks for short-circuiting or overriding results, and decorators like `@allowGuest` and `@action` for fine-grained control. ```typescript import { BasePolicy, AuthorizationResponse } from '@adonisjs/bouncer' import { allowGuest, action } from '@adonisjs/bouncer' import type { AuthorizerResponse } from '@adonisjs/bouncer/types' class User { constructor(public id: number, public isModerator: boolean) {} } class Post { constructor(public authorId: number, public isPublished: boolean) {} } export default class PostPolicy extends BasePolicy { /** * before() runs before every action. Return a value to short-circuit. * Return undefined/void to fall through to the action method. */ before(user: User | null, _action: string, post?: Post) { // Moderators bypass all checks if (user?.isModerator) return true // Guard against non-existent resource if (!post) return AuthorizationResponse.deny('Post not found', 404) } /** * after() can override the action result. Return undefined to keep original. */ after(_user: User | null, _action: string, result: AuthorizerResponse) { // Example: never allow deletion via API (always deny at the end) return result } @allowGuest() view (_user: User | null, post: Post): AuthorizerResponse { return post.isPublished } @action({ allowGuest: false }) edit (user: User, post: Post): AuthorizerResponse { return user.id === post.authorId } async delete (user: User, post: Post): Promise { const isOwner = user.id === post.authorId if (!isOwner) return AuthorizationResponse.deny('Cannot delete another user\'s post', 403) return true } } ``` -------------------------------- ### bouncer.with() - Policy Authorizer Source: https://context7.com/adonisjs/bouncer/llms.txt The `bouncer.with()` method returns a `PolicyAuthorizer` for a given policy class or pre-registered policy name. You can chain methods like `.allows()`, `.denies()`, `.authorize()`, or `.execute()` on the returned authorizer to perform authorization checks. ```APIDOC ## `bouncer.with()` — Policy-Based Authorization `bouncer.with()` returns a `PolicyAuthorizer` for a given policy class or pre-registered policy name. The policy instance is constructed fresh per check (or via the IoC container if one is set). Chain `.allows()`, `.denies()`, `.authorize()`, or `.execute()` on the returned authorizer. ### Example Usage: ```typescript // By policy name (pre-registered) const canEdit = await bouncer.with('PostPolicy').allows('edit', post) // true const canDelete = await bouncer.with('PostPolicy').denies('delete', post) // true (not admin) // By class reference await bouncer.with(PostPolicy).authorize('edit', post) // no throw await bouncer.with(PostPolicy).authorize('delete', post) // throws E_AUTHORIZATION_FAILURE ``` ``` -------------------------------- ### Using @allowGuest() and @action() Decorators Source: https://context7.com/adonisjs/bouncer/llms.txt Decorate policy methods to allow unauthenticated users. @allowGuest() is a shorthand for @action({ allowGuest: true }). Without these, null users are denied by default. ```typescript import { BasePolicy } from '@adonisjs/bouncer' import { allowGuest, action } from '@adonisjs/bouncer' import type { AuthorizerResponse } from '@adonisjs/bouncer/types' class User { constructor(public id: number) {} } class Post { constructor(public isPublished: boolean, public authorId: number) {} } class PostPolicy extends BasePolicy { @allowGuest() view(user: User | null, post: Post): AuthorizerResponse { // Guests can view published posts; logged-in users can also see their drafts return post.isPublished || (user !== null && user.id === post.authorId) } @action({ allowGuest: true }) list(_user: User | null): AuthorizerResponse { return true } // No decorator — null user is automatically denied create(user: User): AuthorizerResponse { return user !== null } } // Guest bouncer test const guestBouncer = new Bouncer(null) console.log(await guestBouncer.with(PostPolicy).allows('view', new Post(true, 1))) // true console.log(await guestBouncer.with(PostPolicy).allows('create')) // false (auto-denied) ``` -------------------------------- ### allows() / denies() - Boolean Permission Checks Source: https://context7.com/adonisjs/bouncer/llms.txt Check if a user is allowed or denied to perform an action. These methods return a Promise resolving to a boolean and do not throw exceptions. ```APIDOC ## `bouncer.allows()` / `bouncer.denies()` — Boolean Permission Checks `allows()` and `denies()` return `Promise` — no exception is thrown. Both accept either an ability name (string, from pre-registered abilities) or an ability reference object. ```typescript import { Bouncer } from '@adonisjs/bouncer' class User { constructor(public id: number) {} } class Post { constructor(public authorId: number) {} } const editPost = Bouncer.ability((user: User, post: Post) => user.id === post.authorId) const abilities = { editPost } const bouncer = new Bouncer(new User(1), abilities) const post = new Post(1) const otherPost = new Post(99) // By name (pre-registered ability) console.log(await bouncer.allows('editPost', post)) // true console.log(await bouncer.allows('editPost', otherPost)) // false // By reference console.log(await bouncer.denies(editPost, post)) // false console.log(await bouncer.denies(editPost, otherPost)) // true ``` ``` -------------------------------- ### Perform Boolean Permission Checks with allows() and denies() Source: https://context7.com/adonisjs/bouncer/llms.txt Use `allows()` and `denies()` for boolean permission checks. They return a Promise and accept ability names or references. No exceptions are thrown. ```typescript import { Bouncer } from '@adonisjs/bouncer' class User { constructor(public id: number) {} } class Post { constructor(public authorId: number) {} } const editPost = Bouncer.ability((user: User, post: Post) => user.id === post.authorId) const abilities = { editPost } const bouncer = new Bouncer(new User(1), abilities) const post = new Post(1) const otherPost = new Post(99) // By name (pre-registered ability) console.log(await bouncer.allows('editPost', post)) // true console.log(await bouncer.allows('editPost', otherPost)) // false // By reference console.log(await bouncer.denies(editPost, post)) // false console.log(await bouncer.denies(editPost, otherPost)) // true ``` -------------------------------- ### BasePolicy - Policy Class Base Source: https://context7.com/adonisjs/bouncer/llms.txt All policy classes extend `BasePolicy`, which provides the `actionsMetaData` static registry. Policies can optionally define `before()` and `after()` hooks to short-circuit or override action results. ```APIDOC ## `BasePolicy` — Policy Class Base All policy classes extend `BasePolicy`, which provides the `actionsMetaData` static registry used by the `@action` and `@allowGuest` decorators. Policies may optionally define `before(user, action, ...args)` and `after(user, action, result, ...args)` hooks to short-circuit or override action results. ### Hooks: - **`before(user, action, ...args)`**: Runs before every action. Return a value to short-circuit. Return `undefined`/`void` to fall through to the action method. - **`after(user, action, result, ...args)`**: Can override the action result. Return `undefined` to keep the original result. ### Example Usage: ```typescript import { BasePolicy, AuthorizationResponse } from '@adonisjs/bouncer' import { allowGuest, action } from '@adonisjs/bouncer' import type { AuthorizerResponse } from '@adonisjs/bouncer/types' class User { constructor(public id: number, public isModerator: boolean) {} } class Post { constructor(public authorId: number, public isPublished: boolean) {} } export default class PostPolicy extends BasePolicy { before(user: User | null, _action: string, post?: Post) { // Moderators bypass all checks if (user?.isModerator) return true // Guard against non-existent resource if (!post) return AuthorizationResponse.deny('Post not found', 404) } after(_user: User | null, _action: string, result: AuthorizerResponse) { // Example: never allow deletion via API (always deny at the end) return result } @allowGuest() view(_user: User | null, post: Post): AuthorizerResponse { return post.isPublished } @action({ allowGuest: false }) edit(user: User, post: Post): AuthorizerResponse { return user.id === post.authorId } async delete(user: User, post: Post): Promise { const isOwner = user.id === post.authorId if (!isOwner) return AuthorizationResponse.deny('Cannot delete another user\'s post', 403) return true } } ``` ``` -------------------------------- ### Define Abilities using Bouncer.define() Chain Source: https://context7.com/adonisjs/bouncer/llms.txt Use Bouncer.define() to create a chainable builder for abilities. This is recommended for pre-registering a central map of abilities. The builder returns a typed record of abilities. ```typescript import { Bouncer } from '@adonisjs/bouncer' class User { constructor(public id: number, public isAdmin: boolean) {} } class Post { constructor(public authorId: number) {} } class Comment { constructor(public userId: number) {} } export const { abilities } = Bouncer .define('editPost', (user: User, post: Post) => { return user.isAdmin || user.id === post.authorId }) .define('deletePost', (user: User, post: Post) => { return user.isAdmin || user.id === post.authorId }) .define('deleteComment', (user: User, comment: Comment) => { return user.isAdmin || user.id === comment.userId }) .define('viewDraft', { allowGuest: false }, (user: User, post: Post) => { return user.id === post.authorId }) // Use with a Bouncer instance const bouncer = new Bouncer(new User(1, false), abilities) const canEdit = await bouncer.allows('editPost', new Post(1)) // true const canDelete = await bouncer.allows('deletePost', new Post(2)) // false ``` -------------------------------- ### Creating Rich Authorization Responses Source: https://context7.com/adonisjs/bouncer/llms.txt Illustrates how to use `AuthorizationResponse` to return detailed authorization outcomes, including messages, HTTP status codes, and i18n translation keys. This allows for more granular control over authorization failures. ```typescript import { AuthorizationResponse } from '@adonisjs/bouncer' // Factory helpers const allowed = AuthorizationResponse.allow() // allowed.authorized → true const denied = AuthorizationResponse.deny('Insufficient permissions', 403) // denied.authorized → false // denied.message → 'Insufficient permissions' // denied.status → 403 // With i18n translation key (requires @adonisjs/i18n) const i18nDenied = AuthorizationResponse.deny() i18nDenied.t('errors.unauthorized', { resource: 'posts' }) // i18nDenied.translation → { identifier: 'errors.unauthorized', data: { resource: 'posts' } } // In a policy method class PostPolicy extends BasePolicy { edit(user: User, post: Post): AuthorizationResponse { if (user.id === post.authorId) return AuthorizationResponse.allow() return AuthorizationResponse.deny('Only the author can edit this post', 403) .t('errors.post_edit_denied', { postId: post.id }) as AuthorizationResponse } } ``` -------------------------------- ### Setting Up Authorization Event Emitter Source: https://context7.com/adonisjs/bouncer/llms.txt Set Bouncer.emitter to an AdonisJS-compatible event emitter to receive 'authorization:finished' events after checks. The BouncerProvider handles this automatically. ```typescript import { Bouncer } from '@adonisjs/bouncer' import emitter from '@adonisjs/core/services/emitter' // Set globally (done automatically by BouncerProvider) Bouncer.emitter = emitter // Listen for events emitter.on('authorization:finished', (event) => { console.log(`User ${event.user?.id} → action "${event.action}"`); console.log(` Result: ${event.response.authorized ? 'ALLOWED' : 'DENIED'}`); if (!event.response.authorized) { console.log(` Reason: ${event.response.message ?? 'no message'}`); console.log(` Params:`, event.parameters); } }) // Example: audit log integration emitter.on('authorization:finished', async (event) => { if (!event.response.authorized) { await AuditLog.create({ userId: event.user?.id ?? null, action: event.action, denied: true, reason: event.response.message, }) } }) ``` -------------------------------- ### Throw Authorization Check with authorize() Source: https://context7.com/adonisjs/bouncer/llms.txt Use `authorize()` for checks that should throw an `E_AUTHORIZATION_FAILURE` exception when access is denied. Ideal for route handlers to guard resources. ```typescript import router from '@adonisjs/core/services/router' import { Bouncer } from '@adonisjs/bouncer' const editPost = Bouncer.ability((user: User, post: Post) => user.id === post.authorId) router.put('/posts/:id', async ({ auth, params, response }) => { const user = auth.getUserOrFail() const post = await Post.findOrFail(params.id) const bouncer = new Bouncer(user, { editPost }) // Throws E_AUTHORIZATION_FAILURE (HTTP 403) if user is not the author await bouncer.authorize('editPost', post) // Proceeds only if authorized await post.merge(request.only(['title', 'body'])).save() return response.ok(post) }) ``` -------------------------------- ### authorize() - Throwing Authorization Check Source: https://context7.com/adonisjs/bouncer/llms.txt Perform an authorization check that throws an `E_AUTHORIZATION_FAILURE` exception if access is denied. Ideal for use in route handlers to protect resources. ```APIDOC ## `bouncer.authorize()` — Throwing Authorization Check `authorize()` performs the same check as `allows()` but throws `E_AUTHORIZATION_FAILURE` when access is denied. Use this inside route handlers to guard resources. ```typescript import router from '@adonisjs/core/services/router' import { Bouncer } from '@adonisjs/bouncer' const editPost = Bouncer.ability((user: User, post: Post) => user.id === post.authorId) router.put('/posts/:id', async ({ auth, params, response }) => { const user = auth.getUserOrFail() const post = await Post.findOrFail(params.id) const bouncer = new Bouncer(user, { editPost }) // Throws E_AUTHORIZATION_FAILURE (HTTP 403) if user is not the author await bouncer.authorize('editPost', post) // Proceeds only if authorized await post.merge(request.only(['title', 'body'])).save() return response.ok(post) }) ``` ``` -------------------------------- ### Using bouncer.deny() for Deny Responses Source: https://context7.com/adonisjs/bouncer/llms.txt Use bouncer.deny() as a convenience method to create AuthorizationResponse.deny() within ability callbacks, avoiding an extra import. ```typescript import { Bouncer } from '@adonisjs/bouncer' class User { constructor(public role: string) {} } class Document { constructor(public classification: string) {} } const viewDocument = Bouncer.ability((user: User, doc: Document) => { if (doc.classification === 'top-secret' && user.role !== 'admin') { // Using static method directly: return AuthorizationResponse.deny('Clearance level insufficient', 403) } return true }) // bouncer.deny() is equivalent within a Bouncer instance context const bouncer = new Bouncer(new User('user'), { viewDocument }) const result = bouncer.deny('Clearance level insufficient', 403) // result.authorized → false, result.message → '...', result.status → 403 ``` -------------------------------- ### Sharing Bouncer with Edge Views in a Controller Source: https://context7.com/adonisjs/bouncer/llms.txt In a controller, instantiate Bouncer with the authenticated user and relevant abilities/policies. Then, share the bouncer helper, specifically its edgeHelpers, with the view state to enable @can/@cannot tags. ```typescript // In a controller — share bouncer with Edge view import edge from 'edge.js' import { Bouncer } from '@adonisjs/bouncer' import { editPost } from '#abilities/main' router.get('/posts/:id', async ({ auth, params, view }) => { const user = auth.user const post = await Post.findOrFail(params.id) const bouncer = new Bouncer(user, { editPost }, { PostPolicy: () => import('#policies/post_policy'), }) return view.render('posts/show', { post, ...bouncer.edgeHelpers, // injects { bouncer: { can, cannot } } }) }) ``` -------------------------------- ### Define a Standalone Ability with Bouncer.ability() Source: https://context7.com/adonisjs/bouncer/llms.txt Define a standalone authorization rule using Bouncer.ability. This function wraps an authorizer callback. Use `{ allowGuest: true }` to permit unauthenticated users. ```typescript import { Bouncer } from '@adonisjs/bouncer' class User { constructor(public id: number, public role: string) {} } class Post { constructor(public authorId: number, public isPublished: boolean) {} } // Basic ability — guest users are automatically denied export const editPost = Bouncer.ability((user: User, post: Post) => { return user.id === post.authorId }) // Ability allowing guest access — user may be null export const viewPost = Bouncer.ability({ allowGuest: true }, (user: User | null, post: Post) => { return post.isPublished || (user !== null && user.id === post.authorId) }) // Async ability with custom denial response export const deletePost = Bouncer.ability(async (user: User, post: Post) => { if (user.role === 'admin') return true if (user.id !== post.authorId) { return AuthorizationResponse.deny('Only the author can delete this post', 403) } return true }) ``` -------------------------------- ### Generated Policy Class Structure Source: https://context7.com/adonisjs/bouncer/llms.txt A newly generated policy class extends BasePolicy and includes stub methods for the specified actions. These methods should be implemented to define the authorization logic for the associated model. ```typescript // Generated: app/policies/post_policy.ts import User from '#models/user' import Post from '#models/post' import { BasePolicy } from '@adonisjs/bouncer' import type { AuthorizerResponse } from '@adonisjs/bouncer/types' export default class PostPolicy extends BasePolicy { view(user: User): AuthorizerResponse { return false } create(user: User): AuthorizerResponse { return false } edit(user: User): AuthorizerResponse { return false } delete(user: User): AuthorizerResponse { return false } } ``` -------------------------------- ### AdonisJS Bouncer Configuration in adonisrc.ts Source: https://context7.com/adonisjs/bouncer/llms.txt Update your adonisrc.ts file to include Bouncer's commands, provider, and initializer middleware. The `configure` command typically handles these updates automatically. ```typescript import { indexPolicies } from '@adonisjs/bouncer' export default defineConfig({ commands: ['@adonisjs/bouncer/commands'], providers: ['@adonisjs/bouncer/bouncer_provider'], assembler: { hooks: { init: [indexPolicies()], }, }, router: { middleware: ['#middleware/initialize_bouncer_middleware'], }, }) ``` -------------------------------- ### Edge.js Template Tags for Authorization Source: https://context7.com/adonisjs/bouncer/llms.txt Use @can and @cannot tags in Edge.js templates to conditionally render content based on user abilities or policy methods. These tags require the bouncer helper to be shared with the view state. ```edge {{-- Ability check --}} @can('editPost', post) Edit @end {{-- Policy check (dot notation: PolicyName.methodName) --}} @can('PostPolicy.delete', post) @end {{-- Inverse check --}} @cannot('PostPolicy.edit', post)

You do not have permission to edit this post.

@end ``` -------------------------------- ### IoC Container Integration for Policy Construction Source: https://context7.com/adonisjs/bouncer/llms.txt When bouncer.setContainerResolver() is used with an AdonisJS container resolver, policy classes are constructed via the container, enabling constructor injection of services. ```typescript import { inject } from '@adonisjs/core' import { Container } from '@adonisjs/core/container' import { Bouncer, BasePolicy } from '@adonisjs/bouncer' import type { AuthorizerResponse } from '@adonisjs/bouncer/types' class PermissionService { async getPermissions(userId: number): Promise { // Fetch from DB... return ['posts:edit', 'posts:view'] } } @inject() class PostPolicy extends BasePolicy { constructor(private permissionService: PermissionService) { super() } async edit(user: User, _post: Post): Promise { const perms = await this.permissionService.getPermissions(user.id) return perms.includes('posts:edit') } } const container = new Container() const bouncer = new Bouncer(new User(1)) bouncer.setContainerResolver(container.createResolver()) const canEdit = await bouncer.with(PostPolicy).allows('edit', new Post(1)) // PostPolicy is constructed via container, PermissionService is injected automatically ``` -------------------------------- ### AuthorizationResponse - Rich Response Objects Source: https://context7.com/adonisjs/bouncer/llms.txt `AuthorizationResponse` carries authorization state, an optional message, optional HTTP status, and optional i18n translation data. It can be returned from ability or policy methods for fine-grained control. ```APIDOC ## `AuthorizationResponse` — Rich Response Objects `AuthorizationResponse` carries authorization state, an optional message, optional HTTP status, and optional i18n translation data. Return it from any ability or policy method for fine-grained control. ### Factory Helpers: - **`AuthorizationResponse.allow()`**: Creates an allowed response. - **`AuthorizationResponse.deny(message?, status?)`**: Creates a denied response with an optional message and HTTP status. ### Example Usage: ```typescript import { AuthorizationResponse } from '@adonisjs/bouncer' // Factory helpers const allowed = AuthorizationResponse.allow() // allowed.authorized → true const denied = AuthorizationResponse.deny('Insufficient permissions', 403) // denied.authorized → false // denied.message → 'Insufficient permissions' // denied.status → 403 // With i18n translation key (requires @adonisjs/i18n) const i18nDenied = AuthorizationResponse.deny() i18nDenied.t('errors.unauthorized', { resource: 'posts' }) // i18nDenied.translation → { identifier: 'errors.unauthorized', data: { resource: 'posts' } } // In a policy method class PostPolicy extends BasePolicy { edit(user: User, post: Post): AuthorizationResponse { if (user.id === post.authorId) return AuthorizationResponse.allow() return AuthorizationResponse.deny('Only the author can edit this post', 403) .t('errors.post_edit_denied', { postId: post.id }) as AuthorizationResponse } } ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.