### Client-Side User Resolver Setup Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/denies-client.md Configure a client-side user resolver plugin for authorization. This example demonstrates how to provide the user session to the authorization system. ```typescript export default defineNuxtPlugin({ name: 'authorization-resolver', parallel: true, setup() { return { provide: { authorization: { resolveClientUser: async () => { return useUserSession().user.value }, }, }, } }, }) ``` -------------------------------- ### Install nuxt-authorization Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/README.md Install the nuxt-authorization package using npm. ```bash npm install nuxt-authorization ``` -------------------------------- ### Nuxt Module Configuration Example Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/types.md Example of how to configure the nuxt-authorization module in nuxt.config.ts. No specific options are needed at this time. ```typescript // nuxt.config.ts export default defineNuxtConfig({ modules: ['nuxt-authorization'], authorization: { // No configuration options currently required } }) ``` -------------------------------- ### Local Development Commands Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/README.md Commands for setting up local development, running tests, and building the project. Includes dependency installation, linting, and testing. ```bash # Install dependencies npm install # Generate type stubs npm run dev:prepare # Develop with the playground npm run dev # Build the playground npm run dev:build # Run ESLint npm run lint # Run Vitest npm run test npm run test:watch # Release new version npm run release ``` -------------------------------- ### Client-Side Resolver Example with Custom Auth State Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/configuration.md Implement `resolveClientUser` using a custom store (e.g., Pinia) to manage authentication state. This example shows fetching the user if not already loaded. ```typescript import { useAuthStore } from '~/stores/auth' export default defineNuxtPlugin({ name: 'authorization-resolver', parallel: true, setup() { return { provide: { authorization: { resolveClientUser: async () => { const auth = useAuthStore() // Optionally fetch user if not already loaded if (!auth.user && auth.token) { await auth.fetchUser() } return auth.user ?? null }, }, }, } }, }) ``` -------------------------------- ### Install Nuxt Authorization Module Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/README.md Install the module to your Nuxt application using nuxi. ```bash npx nuxi module add nuxt-authorization ``` -------------------------------- ### BouncerAbility Example Usage Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/types.md Demonstrates creating a BouncerAbility object that allows guest access and how its execute function handles guest logic automatically. ```typescript import { defineAbility } from 'nuxt-authorization/utils' export const viewPublicPost = defineAbility({ allowGuest: true }, () => true) // This ability object: // { // allowGuest: true, // original: () => true, // execute: (user, ...args) => { // // Handles guest logic automatically // return true // } // } // Can be used with bouncer functions: const allowed = await allows(viewPublicPost) ``` -------------------------------- ### Example of AuthorizerToAbility Transformation Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/types.md This example illustrates the output of the AuthorizerToAbility type utility. It shows how a simple authorizer function type is transformed into a detailed ability type, including the `execute` method signature. ```typescript // Internal to defineAbility implementation interface User { id: number } interface Post { id: number; authorId: number } type MyAuthorizer = (user: User, post: Post) => boolean type MyAbility = AuthorizerToAbility // Results in: // { // allowGuest: boolean // original: MyAuthorizer // execute(user: User | null, post: Post): boolean // } ``` -------------------------------- ### Server-Side Resolver with nuxt-auth-utils Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/configuration.md Example of implementing `resolveServerUser` using `nuxt-auth-utils` to retrieve the user session. Ensure `getUserSession` is correctly imported and configured. ```typescript import { getUserSession } from '#auth' export default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook('request', async (event) => { event.context.$authorization = { resolveServerUser: async () => { const session = await getUserSession(event) return session.user ?? null }, } }) }) ``` -------------------------------- ### Define and Use Type-Safe Abilities Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/README.md Demonstrates defining a type-safe ability using `defineAbility` and how TypeScript infers types for users and arguments. Shows correct and incorrect usage examples. ```typescript interface User { id: number } interface Post { id: number; authorId: number } // Type-safe ability const editPost = defineAbility((user: User, post: Post) => { return user.id === post.authorId }) // Type-safe usage const post: Post = { id: 1, authorId: 5 } const allowed = await allows(editPost, post) // ✓ Correct // Type error — missing argument const allowed = await allows(editPost) // ✗ Type error ``` -------------------------------- ### Client-Side Resolver Example Synchronous Promise Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/configuration.md Demonstrates a synchronous resolver that returns a Promise resolving to the current user state. Useful for simple state management. ```typescript import { useState } from '#app' export default defineNuxtPlugin({ name: 'authorization-resolver', parallel: true, setup() { const currentUser = useState('currentUser', () => null) return { provide: { authorization: { resolveClientUser: () => Promise.resolve(currentUser.value), }, }, } }, }) ``` -------------------------------- ### Client-Side Resolver Example with nuxt-auth-utils Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/configuration.md Implement the `resolveClientUser` function using `nuxt-auth-utils` to retrieve the user session. This is suitable when using `nuxt-auth-utils` for authentication. ```typescript import { useUserSession } from '#auth' export default defineNuxtPlugin({ name: 'authorization-resolver', parallel: true, setup() { return { provide: { authorization: { resolveClientUser: () => useUserSession().user.value, }, }, } }, }) ``` -------------------------------- ### Example Usage of AuthorizationResponse Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/types.md Demonstrates how to return a detailed AuthorizationResponse from a defineAbility function, including a custom message and status code when a resource is not found. Ensure the 'nuxt-authorization/utils' is imported. ```typescript import { deny } from 'nuxt-authorization/utils' export const viewResource = defineAbility((user, resource) => { if (!resource) { return { authorized: false, message: 'Not Found', statusCode: 404 } as AuthorizationResponse } return { authorized: true } }) ``` -------------------------------- ### Examples of Using createAuthorizationError Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/errors.md Demonstrates creating default, custom message, and custom message with status code AuthorizationErrors. Also shows how to use it within custom authorization logic. ```typescript import { createAuthorizationError } from 'nuxt-authorization/utils' // Default error const error1 = createAuthorizationError() console.log(error1.message) // 'Unauthorized' console.log(error1.statusCode) // 403 // Custom message only const error2 = createAuthorizationError('Access Denied') console.log(error2.message) // 'Access Denied' console.log(error2.statusCode) // 403 // Custom message and status code const error3 = createAuthorizationError('Resource Not Found', 404) console.log(error3.message) // 'Resource Not Found' console.log(error3.statusCode) // 404 // Use in custom authorization logic export const customAuthorize = (ability, user, args) => { if (!ability.execute(user, ...args).authorized) { throw createAuthorizationError('Custom denial message', 403) } } ``` -------------------------------- ### Resolver Performance: Bad vs. Good Caching Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/configuration.md Compares inefficient user fetching with an optimized approach using caching. The 'Good' example demonstrates caching the user result in context to avoid repeated database calls. ```typescript // ✗ Bad — fetches every time resolveServerUser: async () => { return await db.users.findById(getCookie(event, 'user_id')) } ``` ```typescript // ✓ Good — cache result in context let cachedUser = null resolveServerUser: async () => { if (cachedUser !== null) { return cachedUser } cachedUser = await db.users.findById(getCookie(event, 'user_id')) return cachedUser } ``` -------------------------------- ### BouncerAuthorizer Examples Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/types.md Illustrates how to define and use BouncerAuthorizer functions for different authorization scenarios, including simple role checks and resource-specific permissions. ```typescript interface User { id: number role: string } interface Post { id: number authorId: number } // Simple authorizer without extra args const canListPosts: BouncerAuthorizer = (user) => { return user.role !== 'banned' } // Authorizer with resource argument const canEditPost: BouncerAuthorizer = (user, post: Post) => { return user.id === post.authorId } // Use in ability export const listPosts = defineAbility(canListPosts) export const editPost = defineAbility(canEditPost) ``` -------------------------------- ### allows() (client) Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/MANIFEST.md `allows()` checks if a user can perform an action on the client-side. It returns a Promise resolving to a boolean and supports simple, argument-based, and component-integrated examples. ```APIDOC ## allows() (client) ### Description Checks if the user is authorized to perform a specific action on the client-side. ### Function Signature `allows(ability: Ability, args?: any): Promise ` ### Parameters - **ability** (Ability) - Required - The authorization ability object. - **args** (any) - Optional - Arguments to pass to the ability check. ### Return Type - **Promise** - A promise that resolves to `true` if the action is allowed, `false` otherwise. ### Examples ```javascript // Simple check const canEdit = await allows(ability, 'edit', 'Post'); // With arguments const canComment = await allows(ability, { action: 'comment', postId: 123 }); // In components (e.g., Vue) ``` ### Setup Requirements Ensure the `ability` object is properly initialized and available in the client-side context. ``` -------------------------------- ### Server User Resolver Setup Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/denies-server.md This code configures the server-side user resolver, which is required for the `denies()` function to resolve the user from the event context. It should be placed in `server/plugins/authorization-resolver.ts`. ```typescript export default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook('request', async (event) => { event.context.$authorization = { resolveServerUser: async () => { const session = await getUserSession(event) return session.user ?? null }, } }) }) ``` -------------------------------- ### Examples of Valid Authorizer Functions Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/types.md Illustrates various ways to define authorizer functions using defineAbility, showcasing synchronous boolean returns, explicit AuthorizationResponse objects, and asynchronous operations returning Promises of either type. ```typescript // Returns boolean export const ability1 = defineAbility(() => true) // Returns AuthorizationResponse export const ability2 = defineAbility(() => ({ authorized: true })) // Returns Promise export const ability3 = defineAbility(async () => Promise.resolve(true)) // Returns Promise export const ability4 = defineAbility(async () => Promise.resolve({ authorized: false, message: 'Denied' }) ) ``` -------------------------------- ### allows() (server) Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/MANIFEST.md `allows()` checks server-side if a user can perform an action, considering the `H3Event`. It returns a Promise resolving to a boolean and includes examples for various server-side scenarios. ```APIDOC ## allows() (server) ### Description Checks if the user is authorized to perform a specific action on the server-side. ### Function Signature `allows(event: H3Event, ability: Ability, args?: any): Promise ` ### Parameters - **event** (H3Event) - Required - The H3 event object. - **ability** (Ability) - Required - The authorization ability object. - **args** (any) - Optional - Arguments to pass to the ability check. ### Return Type - **Promise** - A promise that resolves to `true` if the action is allowed, `false` otherwise. ### Examples ```javascript // Basic check in a server route export default defineEventHandler(async (event) => { const canReadPosts = await allows(event, ability, 'read', 'Post'); if (canReadPosts) { return { posts: await getPosts() }; } return createError({ statusCode: 403, message: 'Forbidden' }); }); // With resources and conditional logic const canEdit = await allows(event, ability, 'edit', resource('Post', postId)); ``` ### Notes on Performance Server-side checks are crucial for security and should be implemented efficiently. ``` -------------------------------- ### Nuxt Plugin with nuxt-auth-utils Client Resolver Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/README.md Example of a Nuxt plugin using `nuxt-auth-utils` to provide the `resolveClientUser` function, returning the user from the session. ```typescript export default defineNuxtPlugin({ name: 'authorization-resolver', parallel: true, setup() { return { provide: { authorization: { resolveClientUser: () => useUserSession().user.value, }, }, } }, }) ``` -------------------------------- ### Using Bouncer Functions for Permissions Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/API-OVERVIEW.md Demonstrates how to use `allows`, `denies`, and `authorize` functions for conditional logic, negative checks, and guarding operations respectively. Ensure the necessary ability definitions and bouncer setup are in place. ```typescript // Using allows() for conditional logic if (await allows(editPost, post)) { await savePost(post) } // Using denies() for negative checks if (await denies(deletePost, post)) { showError('Cannot delete') return } // Using authorize() as a guard try { await authorize(editPost, post) await savePost(post) } catch (error) { showError(error.message) } ``` -------------------------------- ### Nitro Plugin with nuxt-auth-utils Server Resolver Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/README.md Example of a Nitro plugin using `nuxt-auth-utils` to provide the `resolveServerUser` function, retrieving the user from the session asynchronously. ```typescript export default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook('request', async (event) => { event.context.$authorization = { resolveServerUser: async () => { const session = await getUserSession(event) return session.user ?? null }, } }) }) ``` -------------------------------- ### deny() with multiple denial reasons and codes Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/deny.md Handle different denial scenarios within a single ability by providing specific messages and status codes for each. This example shows handling a missing user and insufficient permissions. ```typescript export const updateUser = defineAbility((user: User, targetUser: User) => { if (!targetUser) { return deny({ message: 'User not found', statusCode: 404 }) } if (user.id !== targetUser.id && user.role !== 'admin') { return deny({ message: 'Forbidden', statusCode: 403 }) } return true }) ``` -------------------------------- ### defineAbility() Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/MANIFEST.md `defineAbility()` is used to create an authorization ability. It accepts an authorizer function and options, returning detailed type information and supporting multiple usage examples. ```APIDOC ## defineAbility() ### Description Creates an authorization ability for your application. ### Function Signature `defineAbility(authorizer: Function, options?: Object): Ability ` ### Parameters - **authorizer** (Function) - Required - A function that defines the authorization rules. - **options** (Object) - Optional - Configuration options for the ability. ### Return Type - **Ability** - An object representing the authorization ability. ### Examples ```markdown Basic usage: defineAbility((can, cannot) => { can('read', 'all'); }); With resources: defineAbility((can, cannot, resource) => { can('read', resource('Post')); }); Guest access: defineAbility((can, cannot) => { can('read', 'all'); }); Async ability: defineAbility(async (can, cannot) => { const user = await fetchUser(); if (user.isAdmin) { can('manage', 'all'); } else { can('read', 'posts'); } }); ``` ### Related Types and Functions - `allow()` - `deny()` ``` -------------------------------- ### Showing Different Messages Based on Permission Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/cannot-component.md This example demonstrates how to use both `Can` and `Cannot` components to display context-specific messages or actions based on user permissions. It allows for showing an edit button when allowed and a denial message otherwise. ```vue ``` -------------------------------- ### Client-Side Resolver Plugin Setup Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/configuration.md Defines the client-side resolver plugin for nuxt-authorization. This plugin provides the `resolveClientUser` function. ```typescript export default defineNuxtPlugin({ name: 'authorization-resolver', parallel: true, setup() { return { provide: { authorization: { resolveClientUser: async () => { // Return the current user object or null if not authenticated // This function is called by allows(), denies(), and authorize() // to determine the user before checking authorization }, }, }, } }, }) ``` -------------------------------- ### Client-Side User Resolver Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/API-OVERVIEW.md Example of a client-side user resolver configuration for Nuxt Authorization. This function should return the current user's data. ```typescript // Client: plugins/authorization-resolver.ts resolveClientUser: () => useUserSession().user.value ``` -------------------------------- ### authorize() (client) Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/MANIFEST.md `authorize()` on the client-side either authorizes an action or throws an error. It returns a Promise resolving to void and provides examples for guards, middleware, and error handling. ```APIDOC ## authorize() (client) ### Description Authorizes an action on the client-side or throws an error if unauthorized. ### Function Signature `authorize(ability: Ability, args?: any): Promise ` ### Parameters - **ability** (Ability) - Required - The authorization ability object. - **args** (any) - Optional - Arguments to pass to the ability check. ### Return Type - **Promise** - A promise that resolves when authorized, or rejects with an error if unauthorized. ### Examples ```javascript // As a guard in a route router.beforeEach(async (to, from) => { if (to.meta.requiresAuth) { try { await authorize(ability, 'access', to.name); } catch (error) { return '/login'; } } }); // In a component method async submitForm() { await authorize(ability, 'submit', this.formData); // Proceed with form submission } ``` ### Error Structure Errors thrown by `authorize` typically contain details about the authorization failure. ``` -------------------------------- ### Server User Resolver Setup Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/allows-server.md Configure the server user resolver in `server/plugins/authorization-resolver.ts`. This function is responsible for fetching the user from the session, JWT, or request context. ```typescript export default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook('request', async (event) => { event.context.$authorization = { resolveServerUser: async () => { // Fetch user from session, JWT, or request context const session = await getUserSession(event) return session.user ?? null }, } }) }) ``` -------------------------------- ### Client User Resolver Setup Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/allows-client.md Configure a client-side user resolver plugin for Nuxt. This function provides the `resolveClientUser` method to the Nuxt app context, which `allows()` uses internally. ```typescript export default defineNuxtPlugin({ name: 'authorization-resolver', parallel: true, setup() { return { provide: { authorization: { resolveClientUser: async () => { // Return the current user or null if not authenticated return useUserSession().user.value }, }, }, } }, }) ``` -------------------------------- ### Server-Side Resolver with JWT Token Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/configuration.md Example of implementing `resolveServerUser` by verifying a JWT token from the `Authorization` header. Requires the `jsonwebtoken` library and `JWT_SECRET` environment variable. ```typescript import jwt from 'jsonwebtoken' export default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook('request', async (event) => { event.context.$authorization = { resolveServerUser: async () => { const authHeader = getHeader(event, 'authorization') if (!authHeader?.startsWith('Bearer ')) { return null } const token = authHeader.slice(7) try { const decoded = jwt.verify(token, process.env.JWT_SECRET) return decoded } catch { return null } }, } }) }) ``` -------------------------------- ### Form Validation Feedback with Denial Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/cannot-component.md This example provides feedback within a form context when a user is denied permission to edit a product. It displays a denial message and can conditionally show additional details, like who has locked the product. ```vue ``` -------------------------------- ### authorize() (server) Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/MANIFEST.md `authorize()` on the server-side either authorizes an action or throws an error, utilizing the `H3Event`. It returns a Promise resolving to void and offers extensive examples for various server-side authorization scenarios. ```APIDOC ## authorize() (server) ### Description Authorizes an action on the server-side or throws an error if unauthorized. ### Function Signature `authorize(event: H3Event, ability: Ability, args?: any): Promise ` ### Parameters - **event** (H3Event) - Required - The H3 event object. - **ability** (Ability) - Required - The authorization ability object. - **args** (any) - Optional - Arguments to pass to the ability check. ### Return Type - **Promise** - A promise that resolves when authorized, or rejects with an `H3Error` if unauthorized. ### Examples ```javascript // Basic guard in middleware export const authMiddleware = (event, ability) => { authorize(event, ability, 'access', 'protected-route'); }; // Multiple checks authorize(event, ability, 'edit', resource('Post', postId)); authorize(event, ability, 'publish', resource('Post', postId)); // Conditional authorization if (user.isEditor) { authorize(event, ability, 'edit', post); } else { authorize(event, ability, 'view', post); } ``` ### Error Handling Details Throws `H3Error` instances with appropriate status codes and messages upon authorization failure. ``` -------------------------------- ### denies() (client) Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/MANIFEST.md `denies()` checks if a user is denied from performing an action on the client-side. It returns a Promise resolving to a boolean and includes examples for basic checks and managing disabled states. ```APIDOC ## denies() (client) ### Description Checks if the user is denied from performing a specific action on the client-side. ### Function Signature `denies(ability: Ability, args?: any): Promise ` ### Parameters - **ability** (Ability) - Required - The authorization ability object. - **args** (any) - Optional - Arguments to pass to the ability check. ### Return Type - **Promise** - A promise that resolves to `true` if the action is denied, `false` otherwise. ### Examples ```javascript // Basic check const isDenied = await denies(ability, 'delete', 'User'); // For disabled states // Displaying messages
You do not have access to view reports.
``` ### Setup Requirements Ensure the `ability` object is properly initialized and available in the client-side context. ``` -------------------------------- ### Display Access Denied Message Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/denies-client.md Conditionally display an 'access denied' message based on the user's permissions. This example fetches post data and then checks if the user is denied from deleting it. ```vue ``` -------------------------------- ### Catching AuthorizationError on the Server Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/errors.md Illustrates how authorization errors are handled on the server. Nitro automatically converts them to H3Error. The example also shows how to manually catch and inspect an AuthorizationError if needed. ```typescript import { authorize } from '#server/utils' export default defineEventHandler(async (event) => { // No need for try-catch — Nitro handles it await authorize(event, someAbility, args) // If you do catch it: try { await authorize(event, someAbility, args) } catch (error) { if (error instanceof AuthorizationError) { console.log(error.statusCode) console.log(error.message) } } }) ``` -------------------------------- ### Custom 404 for Hidden Resources Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/errors.md Example of using deny to return a 'Not Found' error (404) for resources that are either missing or inaccessible to the user. This prevents revealing the existence of protected resources. ```typescript import { deny } from 'nuxt-authorization/utils' export const viewResource = defineAbility((user, resource) => { // Don't reveal if resource exists if (!resource) { return deny({ message: 'Not Found', statusCode: 404 }) } // Don't reveal if user lacks permission if (user.id !== resource.ownerId) { return deny({ message: 'Not Found', statusCode: 404 }) } return true }) // Both missing and forbidden resources return 404 ``` -------------------------------- ### Conditional Response Based on Permissions Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/denies-server.md This example demonstrates how to conditionally include sensitive data in a response based on user permissions. If the user is not denied access to view product details, pricing information is included. ```typescript import { denies } from '#server/utils' import { viewProductDetails } from '~/shared/abilities' export default defineEventHandler(async (event) => { const product = await fetchProduct(getRouterParam(event, 'id')) const responseData = { id: product.id, name: product.name, } // Include detailed pricing only if user is not denied if (!(await denies(event, viewProductDetails, product))) { responseData.price = product.price responseData.cost = product.cost } return responseData }) ``` -------------------------------- ### Server-Side Resolver with Database Lookup (Cached) Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/configuration.md Example of implementing `resolveServerUser` with a cached database lookup based on a user ID from cookies. The user is fetched from the database only if not already resolved and cached. ```typescript export default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook('request', async (event) => { let user = null let userResolved = false event.context.$authorization = { resolveServerUser: async () => { // Return cached user if already resolved if (userResolved) { return user } userResolved = true const userId = getCookie(event, 'user_id') if (!userId) { return null } try { user = await db.users.findById(userId) return user } catch { return null } }, } }) }) ``` -------------------------------- ### deny() with custom status code and message (404) Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/deny.md Use deny() with both a custom message and status code (e.g., 404) to obscure resource existence from unauthorized users. This example demonstrates returning 'Not Found' for both non-existent resources and unauthorized access to existing ones. ```typescript export const viewResource = defineAbility((user: User, resource: Resource | null) => { if (!resource) { return deny({ message: 'Not Found', statusCode: 404 }) } if (user.id !== resource.ownerId) { return deny({ message: 'Not Found', statusCode: 404 }) } return true }) // Usage in an API endpoint export default defineEventHandler(async (event) => { const resourceId = getRouterParam(event, 'id') const resource = await findResourceById(resourceId) // This will throw H3Error with statusCode 404 if not found or not owned await authorize(event, viewResource, resource) return resource }) ``` -------------------------------- ### Server-Side Resolver with Session Storage Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/configuration.md Example of implementing `resolveServerUser` by retrieving a session ID from cookies and fetching session data. This approach caches the user on the event context to prevent repeated lookups. ```typescript import { getSession } from '~/server/utils/session' export default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook('request', async (event) => { // Cache the user on event context to avoid repeated lookups let cachedUser = null event.context.$authorization = { resolveServerUser: async () => { if (cachedUser !== null) { return cachedUser } const sessionId = getCookie(event, 'session_id') if (!sessionId) { return null } const session = await getSession(sessionId) cachedUser = session?.user ?? null return cachedUser }, } }) }) ``` -------------------------------- ### Custom Error Handling with Status Codes Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/authorize-client.md Handle specific error status codes returned by `authorize()`. This example shows how to display different user messages based on whether the resource was not found (404) or access was denied (403). ```typescript import { authorize } from '#app' import { viewResource } from '~/shared/abilities' const resource = await fetchResource(resourceId) try { await authorize(viewResource, resource) } catch (error) { if (error.statusCode === 404) { showNotification('Resource not found') } else if (error.statusCode === 403) { showNotification('You do not have access to this resource') } else { showNotification('Unexpected error: ' + error.message) } } ``` -------------------------------- ### Server-Side User Resolver Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/API-OVERVIEW.md Example of a server-side user resolver configuration for Nuxt Authorization. This function fetches and returns the user session data. ```typescript // Server: server/plugins/authorization-resolver.ts resolveServerUser: async () => { const session = await getUserSession(event) return session.user ?? null } ``` -------------------------------- ### Basic Route Guard with authorize() Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/authorize-server.md Use this snippet to authorize a user before performing a destructive action like deleting a product. It ensures the user has the necessary permissions before proceeding. ```typescript import { authorize } from '#server/utils' import { deleteProduct } from '~/shared/abilities' export default defineEventHandler(async (event) => { const product = await fetchProduct(getRouterParam(event, 'id')) // Authorize or throw automatically await authorize(event, deleteProduct, product) // User is authorized, proceed with deletion await product.destroy() return { success: true } }) ``` -------------------------------- ### Using allow() and deny() in Conditional Logic Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/allow.md Illustrates how to use `allow()` and `deny()` within conditional logic inside an ability definition to control access based on user and resource properties. ```typescript import { allow, deny } from 'nuxt-authorization/utils' export const editPost = defineAbility((user: User, post: Post) => { if (user.id === post.authorId) { return allow() } return deny({ message: 'You cannot edit this post' }) }) ``` -------------------------------- ### Project File Organization Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/MANIFEST.md Illustrates the directory structure and the purpose of each markdown file within the project. Useful for understanding where to find specific documentation. ```markdown /output/ ├── README.md # Master index ├── API-OVERVIEW.md # High-level overview ├── MANIFEST.md # This file ├── types.md # Type definitions ├── errors.md # Error handling ├── configuration.md # Setup and config └── api-reference/ # Detailed API docs ├── defineability.md ├── allow.md ├── deny.md ├── allows-client.md ├── allows-server.md ├── denies-client.md ├── denies-server.md ├── authorize-client.md ├── authorize-server.md ├── can-component.md ├── cannot-component.md └── bouncer-component.md ``` -------------------------------- ### Basic Usage of allow() in an Ability Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/allow.md Demonstrates the basic usage of the `allow()` function within a `defineAbility` block to grant access. It also shows the equivalent direct return of `true`. ```typescript import { allow } from 'nuxt-authorization/utils' export const viewPublicPage = defineAbility(() => { return allow() }) // Equivalent to: export const viewPublicPageSimple = defineAbility(() => true) ``` -------------------------------- ### Different Messages for Different Scenarios Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/errors.md Demonstrates using the deny utility to provide specific error messages and status codes based on different authorization failure conditions. This enhances user feedback by explaining the exact reason for denial. ```typescript import { deny } from 'nuxt-authorization/utils' export const editPost = defineAbility((user, post) => { if (post.status === 'archived') { return deny({ message: 'Cannot edit archived posts', statusCode: 403 }) } if (user.id !== post.authorId) { return deny({ message: 'You are not the author', statusCode: 403 }) } return true }) ``` -------------------------------- ### Handling Authorization Errors on Server (Manual) Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/API-OVERVIEW.md Demonstrates manual error handling for authorization errors on the server, allowing for custom logic. ```typescript try { await authorize(event, ability, args) } catch (error) { if (error instanceof AuthorizationError) { // Custom handling } } ``` -------------------------------- ### Check Authorization with Bouncer Functions Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/API-OVERVIEW.md Demonstrates how to use bouncer functions to check authorization on both client and server. Ensure the appropriate user resolver is configured. ```typescript // Client if (await allows(editPost, post)) { // User can edit } ``` ```typescript // Server if (await allows(event, editPost, post)) { // User can edit } ``` -------------------------------- ### Basic Server Authorization Check Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/allows-server.md Use this snippet to check if a user can perform a basic action, like listing products. It throws a 403 error if the user is not authorized. ```typescript import { allows } from '#server/utils' import { listProducts } from '~/shared/abilities' export default defineEventHandler(async (event) => { if (!(await allows(event, listProducts))) { throw createError({ statusCode: 403, message: 'You cannot list products', }) } return getProductList() }) ``` -------------------------------- ### denies() (server) Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/MANIFEST.md `denies()` checks server-side if a user is denied from performing an action, using the `H3Event`. It returns a Promise resolving to a boolean and provides examples for conditional responses and multiple checks. ```APIDOC ## denies() (server) ### Description Checks if the user is denied from performing a specific action on the server-side. ### Function Signature `denies(event: H3Event, ability: Ability, args?: any): Promise ` ### Parameters - **event** (H3Event) - Required - The H3 event object. - **ability** (Ability) - Required - The authorization ability object. - **args** (any) - Optional - Arguments to pass to the ability check. ### Return Type - **Promise** - A promise that resolves to `true` if the action is denied, `false` otherwise. ### Examples ```javascript // Basic check export default defineEventHandler(async (event) => { if (await denies(event, ability, 'delete', 'User')) { return createError({ statusCode: 403, message: 'Cannot delete user.' }); } // Proceed with deletion }); // Conditional responses const isDenied = await denies(event, ability, 'access', '/admin'); if (isDenied) { await sendRedirect(event, '/dashboard'); } ``` ### Setup Requirements Ensure the `ability` object is correctly configured and accessible on the server. ``` -------------------------------- ### Custom Status Codes for Deny Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/README.md Demonstrates how to return custom status codes and messages using `deny()` within a `defineAbility` function. This is useful for scenarios like hiding resource existence or unauthorized access by returning a 'Not Found' status. ```typescript export const viewResource = defineAbility((user, resource) => { if (!resource) { // Return 404 to hide resource existence return deny({ message: 'Not Found', statusCode: 404 }) } if (user.id !== resource.ownerId) { // Also return 404 for unauthorized access return deny({ message: 'Not Found', statusCode: 404 }) } return true }) ``` -------------------------------- ### Authorization in Middleware Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/authorize-server.md Implement authorization checks within middleware to enforce permissions globally or for specific request methods before route handlers are executed. This example authorizes a user for PATCH requests. ```typescript import { authorize } from '#server/utils' import { editProduct } from '~/shared/abilities' export default defineEventHandler(async (event) => { if (event.node.req.method === 'PATCH') { const product = await fetchProduct(getRouterParam(event, 'id')) // Authorize before the handler runs await authorize(event, editProduct, product) } }) ``` -------------------------------- ### Client-Side Authorization Checks in Nuxt Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/API-OVERVIEW.md Demonstrates how to perform authorization checks on the client-side using `allows`, `denies`, and `authorize` functions, along with importing abilities. ```typescript import { allows, denies, authorize } from '#app' import { editPost } from '~/shared/abilities' const post = { id: 1, authorId: 5 } // Check if allowed if (await allows(editPost, post)) { console.log('Can edit') } // Check if denied if (await denies(editPost, post)) { console.log('Cannot edit') } // Authorize or throw try { await authorize(editPost, post) } catch (error) { console.error('Not allowed:', error.message) } ``` -------------------------------- ### Resolving Client User with Custom Auth Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/API-OVERVIEW.md Shows how to integrate a custom client-side authentication store to resolve the user. ```typescript const authStore = useAuthStore() resolveClientUser: () => Promise.resolve(authStore.user) ``` -------------------------------- ### authorize() Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/api-reference/authorize-client.md Authorizes a user to perform an action by checking their abilities. Throws a NuxtError if the user is denied, providing details about the denial or defaulting to a 403 Forbidden error. ```APIDOC ## authorize() ### Description Client-side utility that authorizes a user to perform an action, throwing a NuxtError if the user is denied. This is useful as a guard before executing an action, as it prevents further execution when authorization fails. The thrown error contains the `statusCode` and `message` specified by the ability through [[deny]], or defaults to 403 Forbidden with message "Unauthorized". ### Function Signature ```typescript export async function authorize>( ability: Ability, ...args: BouncerArgs ): Promise ``` ### Parameters #### Parameters - **ability** (BouncerAbility) - Required - The ability to check, created with [[defineAbility]] - **...args** (any[]) - Optional - Additional arguments to pass to the ability's authorizer function ### Return Type ```typescript Promise ``` Resolves if the user is authorized. Rejects with a NuxtError if denied. ### Examples #### Basic authorization guard ```typescript import { authorize } from '#app' import { deletePost } from '~/shared/abilities' async function handleDeletePost(post) { try { await authorize(deletePost, post) // User is authorized, proceed with deletion await api.delete(`/posts/${post.id}`) } catch (error) { // User is not authorized console.error('Cannot delete:', error.message) } } ``` #### In a page guard ```typescript import { authorize } from '#app' import { editPost } from '~/shared/abilities' export default definePageMeta({ async middleware(to) { const post = await fetchPost(to.params.id) try { await authorize(editPost, post) } catch (error) { return navigateTo('/forbidden') } }, }) ``` #### In an event handler ```typescript import { authorize } from '#app' import { publishPost } from '~/shared/abilities' const post = ref(null) const publishing = ref(false) const publishPost = async () => { publishing.value = true try { await authorize(publishPost, post.value) // Authorization passed, now perform the action await api.patch(`/posts/${post.value.id}`, { status: 'published' }) } catch (error) { showNotification({ type: 'error', message: error.message || 'Permission denied', }) } finally { publishing.value = false } } ``` #### Multiple authorization checks ```typescript import { authorize } from '#app' import { editPost, publishPost } from '~/shared/abilities' const post = await fetchPost(postId) try { // Check both abilities, throw immediately if either is denied await Promise.all([ authorize(editPost, post), authorize(publishPost, post), ]) // Both checks passed await performBothActions(post) } catch (error) { console.error('Permission denied:', error.message) } ``` #### Custom error handling with statusCode ```typescript import { authorize } from '#app' import { viewResource } from '~/shared/abilities' const resource = await fetchResource(resourceId) try { await authorize(viewResource, resource) } catch (error) { if (error.statusCode === 404) { showNotification('Resource not found') } else if (error.statusCode === 403) { showNotification('You do not have access to this resource') } else { showNotification('Unexpected error: ' + error.message) } } ``` ``` -------------------------------- ### Defining and Using BouncerArgs for Abilities Source: https://github.com/barbapapazes/nuxt-authorization/blob/main/_autodocs/types.md This example demonstrates how BouncerArgs enforces correct argument passing. It shows a correctly typed call to `allows` and a type error for a call missing a required argument. ```typescript interface User { id: number } interface Post { id: number; authorId: number } export const editPost = defineAbility((user: User, post: Post) => { return user.id === post.authorId }) // BouncerArgs = [Post] // So calling allows requires: allows(editPost, post) // Not: allows(editPost) — would be a type error const post = { id: 1, authorId: 5 } const canEdit = await allows(editPost, post) // ✓ Correct // await allows(editPost) // ✗ Type error — missing post argument ```