Skip to content

Permission Flags

Add boolean flags to API responses so the frontend knows what actions the current user can perform, without duplicating permission logic in every component.

Why use permission flags?

Instead of replicating permission logic in every Vue component:

vue
<!-- ❌ Repeated logic in every component -->
<button v-if="user.role === 'admin' || pet.ownerId === user.id">Edit</button>
<button v-if="user.role === 'admin'">Delete</button>

The BFF computes it once and the component just reads the flag:

vue
<!-- ✅ Clean, no permission logic in the frontend -->
<button v-if="pet.canEdit">Edit</button>
<button v-if="pet.canDelete">Delete</button>

Adding flags in the transformer

Use auth?.permissions.includes() or the helpers from ~/server/auth/types:

typescript
// server/bff/transformers/pet.ts
import type { H3Event } from 'h3'
import type { AuthContext } from '~/server/auth/types'
import { hasPermission, hasRole } from '~/server/auth/types'

export async function transformPet<T = any>(
  data: T,
  event: H3Event,
  auth: AuthContext | null
): Promise<T> {
  if (typeof data !== 'object' || data === null) return data

  return {
    ...(data as any),
    canEdit: hasPermission(auth!, 'pet:write'),
    canDelete: hasPermission(auth!, 'pet:delete'),
  } as T
}

TIP

hasPermission returns false when auth is null or isAuthenticated is false — no extra null guard needed.

Role-based flags

typescript
import { hasRole, hasAnyRole } from '~/server/auth/types'

export async function transformPet<T = any>(
  data: T,
  event: H3Event,
  auth: AuthContext | null
): Promise<T> {
  return {
    ...(data as any),
    canEdit: hasAnyRole(auth!, ['admin', 'editor']),
    canDelete: hasRole(auth!, 'admin'),
  } as T
}

Conditional fields based on permissions

Only expose a field if the user has the right permission:

typescript
export async function transformPet<T = any>(
  data: T,
  event: H3Event,
  auth: AuthContext | null
): Promise<T> {
  const pet = data as any

  const result: any = {
    id: pet.id,
    name: pet.name,
    status: pet.status,
    canEdit: hasPermission(auth!, 'pet:write'),
  }

  // Only admins see cost price
  if (hasRole(auth!, 'admin')) {
    result.costPrice = pet.costPrice
  }

  return result as T
}

Available helpers

All helpers are exported from ~/server/auth/types:

typescript
import { hasPermission, hasRole, hasAnyRole, hasAllRoles } from '~/server/auth/types'

hasPermission(auth, 'pet:write')            // single permission
hasRole(auth, 'admin')                       // single role
hasAnyRole(auth, ['admin', 'editor'])        // any of the roles
hasAllRoles(auth, ['admin', 'editor'])       // all of the roles

All return false when auth is null or auth.isAuthenticated is false.

Released under the Apache-2.0 License.