
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
A strictly typed, framework-agnostic authorization library for TypeScript.
Guardap utilizes a generic-first architecture where Roles, Features, Actions, and Conditions are defined once in the configuration and flow seamlessly to logic builders and React props. The entire authorization chain is statically verified, ensuring that invalid roles or actions are caught at compile time.
Complex authorization logic is transformed into readable, sequential sentences. The builder pattern supports standard AND logic by default, with branching OR logic available via .or(). This approach eliminates nested conditionals and improves code maintainability.
The system supports checking static Roles and Groups alongside dynamic boolean Conditions (State-based) within the same chain. It features granular control with Feature-Level Wildcards (*), allowing for flexible permission modeling that adapts to complex business rules.
The core logic is pure TypeScript, making it Isomorphic and SSR-ready. First-class React bindings are provided, including native support for React Suspense to handle asynchronous authorization states without boilerplate loading logic.
Standard Implementation
// Manual checks often lead to nested, hard-to-read logic
if (
user.roles.includes('admin') ||
(user.roles.includes('staff') && user.conditions.isActive)
) {
return <AdminPanel />;
}
Guardap Implementation
// Fluent, readable, and type-safe
if (
AccessGuard.requireRole('admin')
.or()
.requireRole('staff')
.mustBe('isActive')
.allowed()
) {
return <AdminPanel />;
}
npm install guardap
# or
pnpm add guardap
The createGuard factory is the entry point. It accepts 5 generic types to enforce strict type safety across your application.
import { createGuard } from 'guardap';
// 1. Define your Domain Types
type Roles = 'admin' | 'editor' | 'viewer';
type Features = 'posts' | 'settings';
type Actions = 'create' | 'read' | 'update' | 'delete';
type Conditions = 'isVerified' | 'hasSubscription';
type Groups = 'staff';
// 2. Create the Guard Instance
const AccessGuard = createGuard<Roles, Features, Actions, Conditions, Groups>({
// Map roles to permissions
getPermissions: (roles) => {
if (roles.includes('admin')) return { '*': '*' }; // Global Wildcard
if (roles.includes('editor')) return { posts: '*', settings: 'r' }; // Feature Wildcard
return { posts: 'r' };
},
// Define Groups (Optional)
groups: {
staff: ['admin', 'editor'],
},
// Resolve current user state (Sync or Async)
// This can return a direct object OR a Promise
getUserState: async () => {
// Example: Fetch from session or context
const session = await fetchSession();
return {
roles: session.roles,
conditions: {
isVerified: session.emailVerified,
hasSubscription: !!session.subId
},
// Explicit auth flag (optional, defaults to roles.length > 0)
isAuthenticated: !!session.user,
};
},
// Optional: Custom Action Resolver (Default: first char, e.g. 'create' -> 'c')
resolveAction: (action) => action[0],
// Optional: Enable Debug Mode to log permission rejections to console
debug: true,
});
The AccessGuard instance provides a fluent builder for checking permissions.
Synchronous Checks (Client)
// Uses default/global state
const isAllowed = AccessGuard.requireRole('admin')
.require('create').on('posts')
.allowed();
Asynchronous Checks (Server)
// Injects request context
const isAllowed = await AccessGuard.with(context)
.requireRole('admin')
.allowedAsync();
Complex Logic (.or)
AccessGuard.requireRole('admin') // Check A
.or() // OR
.requireRole('editor') // (Check B
.mustBe('isVerified') // AND Check C)
.allowed();
Guardap supports two initialization patterns depending on your environment.
1. Client-Side (Implicit Context) In a client-side app (SPA), your user state is often global or retrieved from a store/hook. You don't need to pass context every time.
// Config: getUserState uses global store or default logic
const isAllowed = AccessGuard.requireRole('admin').allowed();
2. Server-Side (Explicit Context)
In SSR or Middleware (Node/Next.js), state is request-scoped. Use .with(context) to inject the specific request context.
// Config: getUserState(ctx) uses the passed context
const isAllowed = await AccessGuard.with(req).requireRole('admin').allowedAsync();
The IGuardChain interface provides a readable, sentence-like API.
| Method | Description |
|---|---|
requireRole(role) | Checks if user has a specific role (or one of an array of roles). |
requireGroup(group) | Checks if user belongs to a configured group. |
requireLogin() | Enforces that the user is authenticated. |
guestOnly() | Enforces that the user is NOT authenticated. |
mustBe(condition) | Checks a custom boolean condition defined in getUserState. |
require(action).on(feature) | Checks specific permission. Supports wildcards (*). |
.or() | Logic Switcher. Snapshots the current chain result and resets for a new branch. (A |
.allowed() | Terminal. Returns boolean. Throws error if the chain is async. |
.allowedAsync() | Terminal. Returns Promise<boolean>. Works for both sync and async chains. |
.redirect(to?) | Terminal. Triggers the configured router driver if access is denied. |
Example: Branching Logic
AccessGuard.requireRole('admin') // Branch 1
.or() // OR
.requireRole('editor') // Branch 2 (Start)
.mustBe('isVerified') // Branch 2 (Continue - AND)
.allowed();
Guardap provides a powerful React adapter with full TypeScript support.
1. Create the Instance
// src/guard.ts
import { createGuard } from 'guardap/react';
// Create your guard and export the bound components
export const { GuardProvider, AccessGuard, useGuard, withAuth } = createGuard(config);
2. Wrap your App
// src/App.tsx
import { GuardProvider } from './guard';
<GuardProvider>
<AppContent />
</GuardProvider>
3. Protect Components (AccessGuard)
The AccessGuard component accepts props that mirror the fluent API. All props are evaluated with AND logic.
<AccessGuard
role={['admin', 'editor']} // OR logic within role array
condition="isVerified" // AND condition
fallback={<ForbiddenPage />}
loadingComponent={<Spinner />} // Shown during async checks
>
<ProtectedContent />
</AccessGuard>
4. Protect Components (HOC)
Wrap components directly using withAuth.
const AdminPanel = withAuth(Dashboard, { role: 'admin' });
5. Suspense Support (Experimental)
Enable suspense={true} to let a parent <Suspense> boundary handle the loading state.
<Suspense fallback={<GlobalSkeleton />}>
<AccessGuard role="admin" suspense={true}>
<AsyncProtectedContent />
</AccessGuard>
</Suspense>
Guardap comes with built-in drivers for popular routers.
React Router (v6+)
import { useNavigate } from 'react-router-dom';
import { createReactRouterDriver } from 'guardap/drivers/react-router';
// Inside your component/hook
const navigate = useNavigate();
const AccessGuard = createGuard({
// ... config
router: {
driver: createReactRouterDriver(navigate),
},
});
TanStack Router
import { TanStackDriver } from 'guardap/drivers/tanstack';
const AccessGuard = createGuard({
// ... config
router: {
driver: TanStackDriver,
},
});
Other Routers (Next.js / Custom) You can easily create a custom driver for any router.
const AccessGuard = createGuard({
// ... config
router: {
driver: (url) => {
// Your custom redirect logic
window.location.href = url;
},
},
});
We welcome contributions! Please follow these steps:
git clone https://github.com/your-username/guardap.gitpnpm installgit checkout -b feature/my-new-featurenpm testgit commit -m 'Add some feature'git push origin feature/my-new-featurePlease ensure your code follows the existing style and includes tests for new features.
FAQs
Fluent & isomorphic access control library
We found that guardap demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.