Nuxt Integration
Fire Shield provides a Nuxt module for seamless RBAC integration with composables, server utilities, and middleware.
Installation
bash
npm install @fire-shield/nuxt @fire-shield/coreSetup
1. Add Module
Add Fire Shield to your Nuxt configuration:
typescript
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@fire-shield/nuxt'],
fireShield: {
// Define roles
roles: {
admin: ['user:*', 'post:*'],
editor: ['post:read', 'post:write'],
viewer: ['post:read']
},
// Enable features
auditLogging: true,
enableWildcards: true
}
});2. Initialize User State
typescript
// plugins/auth.ts
export default defineNuxtPlugin(async (nuxtApp) => {
const user = await $fetch('/api/auth/me');
if (user) {
const { setUser } = useFireShield();
setUser(user);
}
});Composables
useFireShield()
Main composable for accessing RBAC functionality:
vue
<template>
<div>
<button v-if="can('post:write')" @click="createPost">
Create Post
</button>
<button v-if="hasRole('admin')" @click="openAdmin">
Admin Panel
</button>
</div>
</template>
<script setup>
const { can, hasRole, authorize, rbac } = useFireShield();
function createPost() {
// Create post logic
}
function openAdmin() {
navigateTo('/admin');
}
</script>API:
typescript
interface UseFireShieldReturn {
can: (permission: string) => boolean;
hasRole: (role: string) => boolean;
authorize: (permission: string) => AuthorizationResult;
rbac: RBAC;
setUser: (user: RBACUser | null) => void;
}useRBAC()
Access RBAC instance directly:
typescript
const rbac = useRBAC();
// Create dynamic roles
rbac.createRole('moderator', ['comment:moderate', 'post:flag']);
// Check permissions
rbac.hasPermission(user, 'comment:moderate');
// Deny permissions
rbac.denyPermission(user.id, 'system:delete');Server Routes
Basic Protection
typescript
// server/api/users.get.ts
import { defineEventHandler } from 'h3';
import { useRBAC } from '#fire-shield';
export default defineEventHandler(async (event) => {
const rbac = useRBAC();
const user = event.context.user;
if (!rbac.hasPermission(user, 'user:read')) {
throw createError({
statusCode: 403,
message: 'Forbidden'
});
}
const users = await getUsers();
return { users };
});Using defineRBACEventHandler
typescript
// server/api/admin/users.get.ts
export default defineRBACEventHandler('user:read', async (event) => {
const users = await getUsers();
return { users };
});Using defineRBACRoleHandler
typescript
// server/api/admin/stats.get.ts
export default defineRBACRoleHandler('admin', async (event) => {
const stats = await getAdminStats();
return { stats };
});Dynamic Permission Checks
typescript
// server/api/posts/[id].delete.ts
import { defineEventHandler, createError } from 'h3';
import { useRBAC } from '#fire-shield';
export default defineEventHandler(async (event) => {
const rbac = useRBAC();
const user = event.context.user;
const postId = event.context.params.id;
const post = await getPost(postId);
// Check ownership
const isOwner = post.authorId === user.id;
const canDelete =
rbac.hasPermission(user, 'post:delete:any') ||
(isOwner && rbac.hasPermission(user, 'post:delete:own'));
if (!canDelete) {
throw createError({
statusCode: 403,
message: 'You do not have permission to delete this post'
});
}
await deletePost(postId);
return { success: true };
});Middleware
Route Protection
typescript
// middleware/auth.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
const { can } = useFireShield();
// Protect admin routes
if (to.path.startsWith('/admin')) {
if (!can('admin:access')) {
return navigateTo('/unauthorized');
}
}
// Protect specific routes
const protectedRoutes = {
'/posts/create': 'post:write',
'/users/manage': 'user:manage'
};
const permission = protectedRoutes[to.path];
if (permission && !can(permission)) {
return navigateTo('/unauthorized');
}
});Role-Based Middleware
typescript
// middleware/admin.ts
export default defineNuxtRouteMiddleware((to, from) => {
const { hasRole } = useFireShield();
if (!hasRole('admin')) {
return navigateTo('/unauthorized');
}
});Usage:
vue
<script setup>
definePageMeta({
middleware: 'admin'
});
</script>
<template>
<div>
<h1>Admin Panel</h1>
</div>
</template>Components
Conditional Rendering
vue
<template>
<div>
<!-- Show/hide based on permissions -->
<section v-if="can('user:manage')">
<h2>User Management</h2>
<UserList />
</section>
<section v-if="can('post:manage')">
<h2>Post Management</h2>
<PostList />
</section>
<!-- Show/hide based on roles -->
<div v-if="hasRole('admin')">
<AdminControls />
</div>
</div>
</template>
<script setup>
const { can, hasRole } = useFireShield();
</script>Dynamic Actions
vue
<template>
<div>
<button
v-for="action in availableActions"
:key="action.permission"
v-if="can(action.permission)"
@click="action.handler"
>
{{ action.label }}
</button>
</div>
</template>
<script setup>
const { can } = useFireShield();
const availableActions = [
{ label: 'Edit', permission: 'post:write', handler: editPost },
{ label: 'Delete', permission: 'post:delete', handler: deletePost },
{ label: 'Publish', permission: 'post:publish', handler: publishPost },
];
function editPost() {
// Edit logic
}
function deletePost() {
// Delete logic
}
function publishPost() {
// Publish logic
}
</script>Form Protection
vue
<template>
<form @submit.prevent="handleSubmit">
<input v-model="form.name" placeholder="Name" />
<input
v-if="can('user:edit:email')"
v-model="form.email"
type="email"
placeholder="Email"
/>
<select
v-if="can('user:edit:role')"
v-model="form.role"
>
<option value="user">User</option>
<option value="editor">Editor</option>
<option v-if="hasRole('admin')" value="admin">Admin</option>
</select>
<button type="submit">Save</button>
</form>
</template>
<script setup>
const { can, hasRole } = useFireShield();
const form = reactive({
name: '',
email: '',
role: 'user'
});
async function handleSubmit() {
await $fetch('/api/users', {
method: 'POST',
body: form
});
}
</script>Layouts
Protected Layout
vue
<!-- layouts/admin.vue -->
<template>
<div v-if="can('admin:access')">
<AdminSidebar />
<main>
<slot />
</main>
</div>
<div v-else>
<h1>Access Denied</h1>
<p>You do not have permission to access the admin panel</p>
</div>
</template>
<script setup>
const { can } = useFireShield();
</script>Usage:
vue
<script setup>
definePageMeta({
layout: 'admin'
});
</script>
<template>
<div>
<h1>Admin Dashboard</h1>
</div>
</template>Server Middleware
Authentication Middleware
typescript
// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
const token = getCookie(event, 'auth-token');
if (token) {
const user = await validateToken(token);
event.context.user = user;
}
});RBAC Middleware
typescript
// server/middleware/rbac.ts
export default defineEventHandler(async (event) => {
const rbac = useRBAC();
const user = event.context.user;
// Add RBAC helper to context
event.context.can = (permission: string) =>
user ? rbac.hasPermission(user, permission) : false;
event.context.hasRole = (role: string) =>
user ? user.roles.includes(role) : false;
});Usage:
typescript
// server/api/posts.get.ts
export default defineEventHandler(async (event) => {
if (!event.context.can('post:read')) {
throw createError({
statusCode: 403,
message: 'Forbidden'
});
}
const posts = await getPosts();
return { posts };
});Audit Logging
Enable in Configuration
typescript
// nuxt.config.ts
export default defineNuxtConfig({
fireShield: {
auditLogging: true
}
});Custom Audit Logger
typescript
// server/plugins/audit.ts
import { defineNitroPlugin } from '#imports';
import { BufferedAuditLogger } from '@fire-shield/core';
export default defineNitroPlugin((nitroApp) => {
const logger = new BufferedAuditLogger(
async (events) => {
await $fetch('/api/audit-logs', {
method: 'POST',
body: { events }
});
},
{ maxBufferSize: 50, flushIntervalMs: 3000 }
);
const rbac = useRBAC();
rbac.setAuditLogger(logger);
});Audit Log API
typescript
// server/api/audit-logs.post.ts
export default defineRBACEventHandler('audit:write', async (event) => {
const { events } = await readBody(event);
await db.auditLogs.insertMany(events);
return { success: true };
});Authentication Integration
With Nuxt Auth
typescript
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@sidebase/nuxt-auth', '@fire-shield/nuxt'],
auth: {
// Auth configuration
}
});typescript
// middleware/auth.global.ts
import { useAuth } from '#auth';
export default defineNuxtRouteMiddleware(async (to, from) => {
const { status, data } = useAuth();
if (status.value === 'authenticated' && data.value) {
const { setUser } = useFireShield();
setUser({
id: data.value.user.id,
roles: data.value.user.roles
});
}
});Multi-Tenant Support
typescript
// server/api/tenant/[tenantId]/data.get.ts
export default defineEventHandler(async (event) => {
const rbac = useRBAC();
const user = event.context.user;
const tenantId = event.context.params.tenantId;
// Check tenant-specific permission
const permission = `tenant:${tenantId}:read`;
if (!rbac.hasPermission(user, permission)) {
throw createError({
statusCode: 403,
message: 'You do not have access to this tenant'
});
}
const data = await getTenantData(tenantId);
return { data };
});Best Practices
1. Centralize Permission Checks
typescript
// composables/usePermissions.ts
export const usePermissions = () => {
const { can, hasRole } = useFireShield();
return {
canManageUsers: () => can('user:manage'),
canEditPost: (post: Post, user: User) =>
can('post:edit:any') ||
(post.authorId === user.id && can('post:edit:own')),
canDeletePost: (post: Post, user: User) =>
can('post:delete:any') ||
(post.authorId === user.id && can('post:delete:own')),
isAdmin: () => hasRole('admin'),
};
};2. Use Module Configuration
typescript
// nuxt.config.ts
export default defineNuxtConfig({
fireShield: {
roles: {
admin: ['*'],
editor: ['post:*', 'comment:moderate'],
user: ['post:read', 'comment:create']
},
auditLogging: true,
enableWildcards: true
}
});3. Error Handling
typescript
// server/api/protected.get.ts
export default defineEventHandler(async (event) => {
try {
const rbac = useRBAC();
const user = event.context.user;
const result = rbac.authorize(user, 'protected:access');
if (!result.allowed) {
throw createError({
statusCode: 403,
message: result.reason || 'Forbidden'
});
}
return { data: 'protected data' };
} catch (error) {
console.error('RBAC error:', error);
throw error;
}
});TypeScript Support
typescript
import type { RBACUser } from '@fire-shield/nuxt';
interface User extends RBACUser {
email: string;
name: string;
}
const user: User = {
id: 'user-123',
roles: ['editor'],
email: 'user@example.com',
name: 'John Doe',
};
const { setUser } = useFireShield();
setUser(user);Next Steps
- Explore API Reference
- Learn about Permissions
- Check out Examples
