Skip to content

Fire ShieldType-safe RBAC Library

Fast, flexible, and framework-agnostic Role-Based Access Control for JavaScript/TypeScript

Fire Shield

Quick Start โ€‹

Install Fire Shield in your project:

bash
npm install @fire-shield/core
bash
yarn add @fire-shield/core
bash
pnpm add @fire-shield/core

Basic Usage โ€‹

typescript
import { RBAC } from '@fire-shield/core'

// Create RBAC instance
const rbac = new RBAC()

// Define roles and permissions
rbac.createRole('admin', ['posts:*', 'users:*'])
rbac.createRole('editor', ['posts:read', 'posts:write'])
rbac.createRole('viewer', ['posts:read'])

// Check permissions
const user = { id: '1', roles: ['editor'] }
rbac.hasPermission(user, 'posts:write') // โœ… true
rbac.hasPermission(user, 'users:delete') // โŒ false

Framework Integrations โ€‹

Fire Shield provides first-class support for popular frameworks:

vue
<template>
  <!-- Show button only if user can write posts -->
  <button v-can="'posts:write'">Create Post</button>

  <!-- Hide button if user can't delete -->
  <button v-cannot="'posts:delete'">Delete Post</button>
</template>

<script setup>
import { useRBAC } from '@fire-shield/vue'

const { can, cannot } = useRBAC()
</script>
tsx
import { Can, useRBAC } from '@fire-shield/react'

function PostEditor() {
  const { can } = useRBAC()

  return (
    <>
      {/* Conditional rendering */}
      <Can permission="posts:write">
        <button>Create Post</button>
      </Can>

      {/* Programmatic check */}
      {can('posts:delete') && (
        <button>Delete Post</button>
      )}
    </>
  )
}
tsx
import { RBACProvider } from '@fire-shield/react'
import { rbac } from '@/lib/rbac'

// In layout or _app
export default function RootLayout({ children }) {
  return (
    <RBACProvider rbac={rbac}>
      {children}
    </RBACProvider>
  )
}

// Middleware for route protection
export function middleware(request) {
  const user = getUser(request)
  if (!rbac.hasPermission(user, 'admin:access')) {
    return NextResponse.redirect('/unauthorized')
  }
}
vue
<template>
  <div>
    <!-- Use composables -->
    <button v-if="canWrite">Create Post</button>
    <button v-if="isAdmin">Admin Panel</button>
  </div>
</template>

<script setup>
const { can } = useFireShield()
const canWrite = can('posts:write')
const isAdmin = can('admin:access')
</script>
typescript
import { Component } from '@angular/core'
import { RBACService } from '@fire-shield/angular'

@Component({
  selector: 'app-posts',
  template: `
    <!-- Structural directive -->
    <button *fsCanPermission="'posts:write'">
      Create Post
    </button>

    <!-- Observable -->
    <button *ngIf="canDelete$ | async">
      Delete Post
    </button>
  `
})
export class PostsComponent {
  canDelete$ = this.rbac.can$('posts:delete')

  constructor(private rbac: RBACService) {}
}
svelte
<script>
  import { can, hasRole } from '@fire-shield/svelte'

  const canWrite = can('posts:write')
  const isAdmin = hasRole('admin')
</script>

<!-- Reactive permission checks -->
{#if $canWrite}
  <button>Create Post</button>
{/if}

{#if $isAdmin}
  <button>Admin Panel</button>
{/if}

<!-- Use actions -->
<button use:can={'posts:delete'}>Delete</button>
typescript
import { createExpressRBAC } from '@fire-shield/express'

const rbacMiddleware = createExpressRBAC(rbac, {
  getUser: (req) => req.user
})

// Protect routes
app.post('/posts',
  rbacMiddleware.requirePermission('posts:write'),
  createPost
)

app.delete('/posts/:id',
  rbacMiddleware.requirePermission('posts:delete'),
  deletePost
)
typescript
import { createFastifyRBAC } from '@fire-shield/fastify'

const { rbacPlugin, requirePermission } = createFastifyRBAC(rbac, {
  getUser: (request) => request.user
})

// Register plugin
fastify.register(rbacPlugin)

// Protect routes
fastify.post('/posts', {
  preHandler: requirePermission('posts:write')
}, createPost)
typescript
import { Hono } from 'hono'
import { HonoRBACAdapter } from '@fire-shield/hono'

const app = new Hono()
const rbacMiddleware = new HonoRBACAdapter(rbac)

// Protect routes
app.get('/admin',
  rbacMiddleware.permission('admin:access'),
  (c) => c.json({ admin: true })
)

// Works on edge: Cloudflare, Deno, Vercel
export default app
svelte
<script lang="ts">
  import { can } from '@fire-shield/sveltekit'

  const canWrite = can('posts:write')
  const canDelete = can('posts:delete')
</script>

<!-- Reactive permission checks -->
{#if $canWrite}
  <button>Create Post</button>
{/if}

{#if $canDelete}
  <button>Delete Post</button>
{/if}
typescript
import { GraphQLRBACAdapter } from '@fire-shield/graphql'

const rbacAdapter = new GraphQLRBACAdapter(rbac)

const resolvers = {
  Mutation: {
    createPost: rbacAdapter.protect('posts:write', (parent, args, context) => {
      // Create post logic
      return createPost(args.input)
    }),

    deletePost: rbacAdapter.protect('posts:delete', (parent, args, context) => {
      // Delete post logic
      return deletePost(args.id)
    })
  }
}
typescript
import { createTRPCRBAC } from '@fire-shield/trpc'

const { protectedProcedure } = createTRPCRBAC(rbac, {
  getUser: (ctx) => ctx.user
})

const appRouter = router({
  createPost: protectedProcedure('posts:write')
    .input(z.object({ title: z.string() }))
    .mutation(({ input }) => {
      return createPost(input)
    }),

  deletePost: protectedProcedure('posts:delete')
    .input(z.object({ id: z.string() }))
    .mutation(({ input }) => {
      return deletePost(input.id)
    })
})
tsx
import { Can, useRBAC } from '@fire-shield/react-native'
import { View, Button, Text } from 'react-native'

function PostScreen() {
  const { can } = useRBAC()

  return (
    <View>
      <Can permission="posts:write">
        <Button title="Create Post" onPress={createPost} />
      </Can>

      {can('posts:delete') && (
        <Button title="Delete Post" onPress={deletePost} />
      )}
    </View>
  )
}
tsx
import { Can, useRBAC } from '@fire-shield/expo'
import { View, Button } from 'react-native'

export default function App() {
  const { can, hasRole } = useRBAC()

  return (
    <View>
      <Can permission="posts:write">
        <Button title="Create Post" onPress={createPost} />
      </Can>

      {hasRole('admin') && (
        <Button title="Admin Panel" onPress={openAdmin} />
      )}
    </View>
  )
}
bash
# Validate RBAC configuration
fire-shield validate rbac.config.json

# Check if user has permission
fire-shield check user-123 posts:write --config rbac.config.json

# Get system info
fire-shield info
typescript
// AI agents can use RBAC via Model Context Protocol
import { createMCPServer } from '@fire-shield/mcp'

// Start MCP server
const server = createMCPServer(rbac)

// AI can now:
// - Check permissions: check_permission(user_id, permission)
// - List roles: list_roles()
// - Deny permissions: deny_permission(user_id, permission)
// - Get denied permissions: get_denied_permissions(user_id)

server.start()

Why Fire Shield? โ€‹

Compared to other RBAC libraries

FeatureFire ShieldCasbinCASLAccessControlacl
TypeScriptโœ… Nativeโœ… Fullโœ… Full๐ŸŸก Partial๐ŸŸก Partial
Bundle Size๐ŸŽฏ ~25KB~600KB+~350KB~180KB~35KB
Dependenciesโœ… 0~510Few
Wildcard Permissionsโœ… Yesโœ… Yes (regex)๐ŸŸก Partialโœ… YesโŒ No
Role Hierarchyโœ… Yesโœ… YesโŒ NoโŒ NoโŒ No
Audit Loggingโœ… Built-in๐ŸŸก PluginโŒ NoโŒ NoโŒ No
Deny Permissionsโœ… Yesโœ… YesโŒ NoโŒ NoโŒ No
Lazy Role Evaluationโœ… YesโŒ NoโŒ NoโŒ NoโŒ No
Permission Cachingโœ… Built-inโŒ NoโŒ NoโŒ NoโŒ No
Memory Optimizationโœ… YesโŒ NoโŒ NoโŒ NoโŒ No
Framework Adaptersโœ… 16๐ŸŸก Limited๐ŸŸก LimitedโŒ NoโŒ No
Maintainedโœ… Activeโœ… Activeโœ… Active๐ŸŸก Low Activity๐ŸŸก Old/Little Maintenance

Support the Project โ€‹

If you find Fire Shield helpful, consider supporting its development:

๐Ÿฅ–Buy me a banh mi

Your support helps maintain and improve Fire Shield! ๐Ÿ™

Ready to secure your application?

Get Started โ†’