Plugins
The plugin system lets you hook into every access check, feature evaluation, policy evaluation, and experiment assignment. Use plugins for audit logging, analytics, custom condition operators, and more.
Plugin Interface
interface AccessPlugin {
name: string;
onAccessCheck?: (event: AccessCheckEvent) => void;
onFeatureEvaluate?: (event: FeatureEvaluateEvent) => void;
onPolicyEvaluate?: (event: PolicyEvaluateEvent) => void;
onExperimentAssign?: (event: ExperimentAssignEvent) => void;
onConfigLoad?: (event: ConfigLoadEvent) => void;
onRenderDenied?: (event: RenderDeniedEvent) => void;
operators?: readonly CustomOperator[];
}All hooks are optional. Plugin errors are silently caught — they never break your application.
Built-In Plugins
Audit Logger
Logs all access events for audit trails:
import { createAuditLoggerPlugin } from "react-access-engine";
const auditPlugin = createAuditLoggerPlugin({
log: (event) => {
// Send to your logging service
console.log(`[AUDIT] ${event.type}:`, event);
},
deniedOnly: false, // Set true to only log denied events
});Analytics
Track access events in your analytics provider:
import { createAnalyticsPlugin } from "react-access-engine";
const analyticsPlugin = createAnalyticsPlugin({
adapter: {
track: (eventName, properties) => {
mixpanel.track(eventName, properties);
},
},
prefix: "rac", // Event prefix (default: 'rac')
trackFeatures: true, // Track feature evaluations
trackExperiments: true, // Track experiment assignments
trackDenied: true, // Track denied access attempts
});Custom Operators
Register custom condition operators for the ABAC engine:
import { createOperatorPlugin } from "react-access-engine";
const geoPlugin = createOperatorPlugin([
{
name: "withinRegion",
evaluate: (fieldValue, targetValue) => {
return Array.isArray(targetValue)
? targetValue.includes(fieldValue)
: fieldValue === targetValue;
},
},
{
name: "matchesPattern",
evaluate: (fieldValue, pattern) => {
return new RegExp(String(pattern)).test(String(fieldValue));
},
},
]);Then use them in conditions:
policies: [
{
id: 'geo-restricted',
effect: 'deny',
permissions: ['data:export'],
condition: (ctx) => {
// Custom operators are available through the condition engine
return ctx.user.attributes?.region !== 'EU';
},
},
],Writing a Custom Plugin
const myPlugin: AccessPlugin = {
name: "my-custom-plugin",
onAccessCheck: ({ permission, user, allowed, resource }) => {
// Called after every permission check
console.log(`${user.id} ${allowed ? "can" : "cannot"} ${permission}`);
},
onFeatureEvaluate: ({ feature, enabled, reason, user }) => {
// Called after every feature evaluation
if (!enabled) {
console.log(`Feature ${feature} disabled for ${user.id}: ${reason}`);
}
},
onPolicyEvaluate: ({ permission, effect, matchedRule, user }) => {
// Called after every policy evaluation
console.log(`Policy: ${permission} → ${effect} (rule: ${matchedRule})`);
},
onExperimentAssign: ({ experimentId, variant, user }) => {
// Called when a user is assigned to an experiment variant
console.log(`Experiment ${experimentId}: ${user.id} → ${variant}`);
},
onRenderDenied: ({ component, permission, user }) => {
// Called when a component denies rendering
console.log(`Render denied: ${component} for ${user.id}`);
},
};Registering Plugins
Add plugins to your config:
const config = defineAccess({
roles: ["admin", "user"] as const,
permissions: {
/* ... */
},
plugins: [auditPlugin, analyticsPlugin, geoPlugin, myPlugin],
});Plugin Engine (Internal)
The PluginEngine class is used internally by the library to manage plugin lifecycle. It is not exported as part of the public API — plugins are automatically initialized when passed via the plugins array in defineAccess().